use std::error::Error;
use std::fmt;
use async_trait::async_trait;
use rusoto_core::credential::ProvideAwsCredentials;
use rusoto_core::region;
use rusoto_core::request::{BufferedHttpResponse, DispatchSignedRequest};
use rusoto_core::{Client, RusotoError};
use rusoto_core::proto;
use rusoto_core::signature::SignedRequest;
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "deserialize_structs", derive(Deserialize))]
pub struct CreateTokenRequest {
#[serde(rename = "clientId")]
pub client_id: String,
#[serde(rename = "clientSecret")]
pub client_secret: String,
#[serde(rename = "code")]
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(rename = "deviceCode")]
pub device_code: String,
#[serde(rename = "grantType")]
pub grant_type: String,
#[serde(rename = "redirectUri")]
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_uri: Option<String>,
#[serde(rename = "refreshToken")]
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_token: Option<String>,
#[serde(rename = "scope")]
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[cfg_attr(any(test, feature = "serialize_structs"), derive(Serialize))]
pub struct CreateTokenResponse {
#[serde(rename = "accessToken")]
#[serde(skip_serializing_if = "Option::is_none")]
pub access_token: Option<String>,
#[serde(rename = "expiresIn")]
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_in: Option<i64>,
#[serde(rename = "idToken")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id_token: Option<String>,
#[serde(rename = "refreshToken")]
#[serde(skip_serializing_if = "Option::is_none")]
pub refresh_token: Option<String>,
#[serde(rename = "tokenType")]
#[serde(skip_serializing_if = "Option::is_none")]
pub token_type: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "deserialize_structs", derive(Deserialize))]
pub struct RegisterClientRequest {
#[serde(rename = "clientName")]
pub client_name: String,
#[serde(rename = "clientType")]
pub client_type: String,
#[serde(rename = "scopes")]
#[serde(skip_serializing_if = "Option::is_none")]
pub scopes: Option<Vec<String>>,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[cfg_attr(any(test, feature = "serialize_structs"), derive(Serialize))]
pub struct RegisterClientResponse {
#[serde(rename = "authorizationEndpoint")]
#[serde(skip_serializing_if = "Option::is_none")]
pub authorization_endpoint: Option<String>,
#[serde(rename = "clientId")]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id: Option<String>,
#[serde(rename = "clientIdIssuedAt")]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id_issued_at: Option<i64>,
#[serde(rename = "clientSecret")]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_secret: Option<String>,
#[serde(rename = "clientSecretExpiresAt")]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_secret_expires_at: Option<i64>,
#[serde(rename = "tokenEndpoint")]
#[serde(skip_serializing_if = "Option::is_none")]
pub token_endpoint: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize)]
#[cfg_attr(feature = "deserialize_structs", derive(Deserialize))]
pub struct StartDeviceAuthorizationRequest {
#[serde(rename = "clientId")]
pub client_id: String,
#[serde(rename = "clientSecret")]
pub client_secret: String,
#[serde(rename = "startUrl")]
pub start_url: String,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[cfg_attr(any(test, feature = "serialize_structs"), derive(Serialize))]
pub struct StartDeviceAuthorizationResponse {
#[serde(rename = "deviceCode")]
#[serde(skip_serializing_if = "Option::is_none")]
pub device_code: Option<String>,
#[serde(rename = "expiresIn")]
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_in: Option<i64>,
#[serde(rename = "interval")]
#[serde(skip_serializing_if = "Option::is_none")]
pub interval: Option<i64>,
#[serde(rename = "userCode")]
#[serde(skip_serializing_if = "Option::is_none")]
pub user_code: Option<String>,
#[serde(rename = "verificationUri")]
#[serde(skip_serializing_if = "Option::is_none")]
pub verification_uri: Option<String>,
#[serde(rename = "verificationUriComplete")]
#[serde(skip_serializing_if = "Option::is_none")]
pub verification_uri_complete: Option<String>,
}
#[derive(Debug, PartialEq)]
pub enum CreateTokenError {
AccessDenied(String),
AuthorizationPending(String),
ExpiredToken(String),
InternalServer(String),
InvalidClient(String),
InvalidGrant(String),
InvalidRequest(String),
InvalidScope(String),
SlowDown(String),
UnauthorizedClient(String),
UnsupportedGrantType(String),
}
impl CreateTokenError {
pub fn from_response(res: BufferedHttpResponse) -> RusotoError<CreateTokenError> {
if let Some(err) = proto::json::Error::parse_rest(&res) {
match err.typ.as_str() {
"AccessDeniedException" => {
return RusotoError::Service(CreateTokenError::AccessDenied(err.msg))
}
"AuthorizationPendingException" => {
return RusotoError::Service(CreateTokenError::AuthorizationPending(err.msg))
}
"ExpiredTokenException" => {
return RusotoError::Service(CreateTokenError::ExpiredToken(err.msg))
}
"InternalServerException" => {
return RusotoError::Service(CreateTokenError::InternalServer(err.msg))
}
"InvalidClientException" => {
return RusotoError::Service(CreateTokenError::InvalidClient(err.msg))
}
"InvalidGrantException" => {
return RusotoError::Service(CreateTokenError::InvalidGrant(err.msg))
}
"InvalidRequestException" => {
return RusotoError::Service(CreateTokenError::InvalidRequest(err.msg))
}
"InvalidScopeException" => {
return RusotoError::Service(CreateTokenError::InvalidScope(err.msg))
}
"SlowDownException" => {
return RusotoError::Service(CreateTokenError::SlowDown(err.msg))
}
"UnauthorizedClientException" => {
return RusotoError::Service(CreateTokenError::UnauthorizedClient(err.msg))
}
"UnsupportedGrantTypeException" => {
return RusotoError::Service(CreateTokenError::UnsupportedGrantType(err.msg))
}
"ValidationException" => return RusotoError::Validation(err.msg),
_ => {}
}
}
RusotoError::Unknown(res)
}
}
impl fmt::Display for CreateTokenError {
#[allow(unused_variables)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CreateTokenError::AccessDenied(ref cause) => write!(f, "{}", cause),
CreateTokenError::AuthorizationPending(ref cause) => write!(f, "{}", cause),
CreateTokenError::ExpiredToken(ref cause) => write!(f, "{}", cause),
CreateTokenError::InternalServer(ref cause) => write!(f, "{}", cause),
CreateTokenError::InvalidClient(ref cause) => write!(f, "{}", cause),
CreateTokenError::InvalidGrant(ref cause) => write!(f, "{}", cause),
CreateTokenError::InvalidRequest(ref cause) => write!(f, "{}", cause),
CreateTokenError::InvalidScope(ref cause) => write!(f, "{}", cause),
CreateTokenError::SlowDown(ref cause) => write!(f, "{}", cause),
CreateTokenError::UnauthorizedClient(ref cause) => write!(f, "{}", cause),
CreateTokenError::UnsupportedGrantType(ref cause) => write!(f, "{}", cause),
}
}
}
impl Error for CreateTokenError {}
#[derive(Debug, PartialEq)]
pub enum RegisterClientError {
InternalServer(String),
InvalidClientMetadata(String),
InvalidRequest(String),
InvalidScope(String),
}
impl RegisterClientError {
pub fn from_response(res: BufferedHttpResponse) -> RusotoError<RegisterClientError> {
if let Some(err) = proto::json::Error::parse_rest(&res) {
match err.typ.as_str() {
"InternalServerException" => {
return RusotoError::Service(RegisterClientError::InternalServer(err.msg))
}
"InvalidClientMetadataException" => {
return RusotoError::Service(RegisterClientError::InvalidClientMetadata(
err.msg,
))
}
"InvalidRequestException" => {
return RusotoError::Service(RegisterClientError::InvalidRequest(err.msg))
}
"InvalidScopeException" => {
return RusotoError::Service(RegisterClientError::InvalidScope(err.msg))
}
"ValidationException" => return RusotoError::Validation(err.msg),
_ => {}
}
}
RusotoError::Unknown(res)
}
}
impl fmt::Display for RegisterClientError {
#[allow(unused_variables)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RegisterClientError::InternalServer(ref cause) => write!(f, "{}", cause),
RegisterClientError::InvalidClientMetadata(ref cause) => write!(f, "{}", cause),
RegisterClientError::InvalidRequest(ref cause) => write!(f, "{}", cause),
RegisterClientError::InvalidScope(ref cause) => write!(f, "{}", cause),
}
}
}
impl Error for RegisterClientError {}
#[derive(Debug, PartialEq)]
pub enum StartDeviceAuthorizationError {
InternalServer(String),
InvalidClient(String),
InvalidRequest(String),
SlowDown(String),
UnauthorizedClient(String),
}
impl StartDeviceAuthorizationError {
pub fn from_response(res: BufferedHttpResponse) -> RusotoError<StartDeviceAuthorizationError> {
if let Some(err) = proto::json::Error::parse_rest(&res) {
match err.typ.as_str() {
"InternalServerException" => {
return RusotoError::Service(StartDeviceAuthorizationError::InternalServer(
err.msg,
))
}
"InvalidClientException" => {
return RusotoError::Service(StartDeviceAuthorizationError::InvalidClient(
err.msg,
))
}
"InvalidRequestException" => {
return RusotoError::Service(StartDeviceAuthorizationError::InvalidRequest(
err.msg,
))
}
"SlowDownException" => {
return RusotoError::Service(StartDeviceAuthorizationError::SlowDown(err.msg))
}
"UnauthorizedClientException" => {
return RusotoError::Service(StartDeviceAuthorizationError::UnauthorizedClient(
err.msg,
))
}
"ValidationException" => return RusotoError::Validation(err.msg),
_ => {}
}
}
RusotoError::Unknown(res)
}
}
impl fmt::Display for StartDeviceAuthorizationError {
#[allow(unused_variables)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
StartDeviceAuthorizationError::InternalServer(ref cause) => write!(f, "{}", cause),
StartDeviceAuthorizationError::InvalidClient(ref cause) => write!(f, "{}", cause),
StartDeviceAuthorizationError::InvalidRequest(ref cause) => write!(f, "{}", cause),
StartDeviceAuthorizationError::SlowDown(ref cause) => write!(f, "{}", cause),
StartDeviceAuthorizationError::UnauthorizedClient(ref cause) => write!(f, "{}", cause),
}
}
}
impl Error for StartDeviceAuthorizationError {}
#[async_trait]
pub trait SsoOidc {
async fn create_token(
&self,
input: CreateTokenRequest,
) -> Result<CreateTokenResponse, RusotoError<CreateTokenError>>;
async fn register_client(
&self,
input: RegisterClientRequest,
) -> Result<RegisterClientResponse, RusotoError<RegisterClientError>>;
async fn start_device_authorization(
&self,
input: StartDeviceAuthorizationRequest,
) -> Result<StartDeviceAuthorizationResponse, RusotoError<StartDeviceAuthorizationError>>;
}
#[derive(Clone)]
pub struct SsoOidcClient {
client: Client,
region: region::Region,
}
impl SsoOidcClient {
pub fn new(region: region::Region) -> SsoOidcClient {
SsoOidcClient {
client: Client::shared(),
region,
}
}
pub fn new_with<P, D>(
request_dispatcher: D,
credentials_provider: P,
region: region::Region,
) -> SsoOidcClient
where
P: ProvideAwsCredentials + Send + Sync + 'static,
D: DispatchSignedRequest + Send + Sync + 'static,
{
SsoOidcClient {
client: Client::new_with(credentials_provider, request_dispatcher),
region,
}
}
pub fn new_with_client(client: Client, region: region::Region) -> SsoOidcClient {
SsoOidcClient { client, region }
}
}
#[async_trait]
impl SsoOidc for SsoOidcClient {
#[allow(unused_mut)]
async fn create_token(
&self,
input: CreateTokenRequest,
) -> Result<CreateTokenResponse, RusotoError<CreateTokenError>> {
let request_uri = "/token";
let mut request = SignedRequest::new("POST", "awsssooidc", &self.region, &request_uri);
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.set_endpoint_prefix("oidc".to_string());
let encoded = Some(serde_json::to_vec(&input).unwrap());
request.set_payload(encoded);
let mut response = self
.client
.sign_and_dispatch(request)
.await
.map_err(RusotoError::from)?;
if response.status.is_success() {
let mut response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
let result = proto::json::ResponsePayload::new(&response)
.deserialize::<CreateTokenResponse, _>()?;
Ok(result)
} else {
let response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
Err(CreateTokenError::from_response(response))
}
}
#[allow(unused_mut)]
async fn register_client(
&self,
input: RegisterClientRequest,
) -> Result<RegisterClientResponse, RusotoError<RegisterClientError>> {
let request_uri = "/client/register";
let mut request = SignedRequest::new("POST", "awsssooidc", &self.region, &request_uri);
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.set_endpoint_prefix("oidc".to_string());
let encoded = Some(serde_json::to_vec(&input).unwrap());
request.set_payload(encoded);
let mut response = self
.client
.sign_and_dispatch(request)
.await
.map_err(RusotoError::from)?;
if response.status.is_success() {
let mut response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
let result = proto::json::ResponsePayload::new(&response)
.deserialize::<RegisterClientResponse, _>()?;
Ok(result)
} else {
let response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
Err(RegisterClientError::from_response(response))
}
}
#[allow(unused_mut)]
async fn start_device_authorization(
&self,
input: StartDeviceAuthorizationRequest,
) -> Result<StartDeviceAuthorizationResponse, RusotoError<StartDeviceAuthorizationError>> {
let request_uri = "/device_authorization";
let mut request = SignedRequest::new("POST", "awsssooidc", &self.region, &request_uri);
request.set_content_type("application/x-amz-json-1.1".to_owned());
request.set_endpoint_prefix("oidc".to_string());
let encoded = Some(serde_json::to_vec(&input).unwrap());
request.set_payload(encoded);
let mut response = self
.client
.sign_and_dispatch(request)
.await
.map_err(RusotoError::from)?;
if response.status.is_success() {
let mut response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
let result = proto::json::ResponsePayload::new(&response)
.deserialize::<StartDeviceAuthorizationResponse, _>()?;
Ok(result)
} else {
let response = response.buffer().await.map_err(RusotoError::HttpDispatch)?;
Err(StartDeviceAuthorizationError::from_response(response))
}
}
}