diff --git a/Cargo.toml b/Cargo.toml index 49d16b1..b168fcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "propelauth" -version = "0.23.3" +version = "0.23.4" authors = ["support@propelauth.com"] description = "A Rust crate for managing authentication and authorization with support for multi-tenant / B2B products, powered by PropelAuth" keywords = ["authentication", "auth", "authorization", "b2b", "tenant"] diff --git a/src/apis/api_key_service_api.rs b/src/apis/api_key_service_api.rs index 138075c..d14df3d 100644 --- a/src/apis/api_key_service_api.rs +++ b/src/apis/api_key_service_api.rs @@ -30,6 +30,16 @@ pub struct CreateApiKeyParams { pub org_id: Option, } +/// struct for passing parameters to the method [`import_api_key`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct ImportApiKeyParams { + pub imported_api_key: String, + pub expires_at_seconds: Option, + pub metadata: Option, + pub user_id: Option, + pub org_id: Option, +} + /// struct for passing parameters to the method [`update_api_key`] #[derive(Clone, Debug, Default, Serialize)] pub struct UpdateApiKeyParams { @@ -38,12 +48,21 @@ pub struct UpdateApiKeyParams { pub set_to_never_expire: Option, } -/// struct for passing parameters to the method [`validate_api_key`] +/// struct for passing parameters to the method [`validate_api_key`] and [`validate_imported_api_key`] #[derive(Clone, Debug, Default, Serialize)] pub struct ValidateApiKeyParams { pub api_key_token: String, } +/// struct for passing parameters to the method [`fetch_api_key_usage`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct FetchApiKeyUsageParams { + pub date: String, + pub user_id: Option, + pub org_id: Option, + pub api_key_id: Option, +} + // struct for typed errors on the api keys service #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] @@ -79,6 +98,21 @@ pub enum ApiKeyValidationErrorResponse { }, } +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum FetchApiKeyUsageError { + InvalidIntegrationAPIKey, + RateLimited { + wait_seconds: f64, + user_facing_error: String, + }, + PropelAuthRateLimit, + NotFound, + UnknownValue(serde_json::Value), + UnknownError, + UnexpectedExceptionWithSDK, +} + pub async fn fetch_current_api_keys( configuration: &configuration::Configuration, params: ApiKeyQueryParams, @@ -404,3 +438,137 @@ pub async fn validate_api_key( Err(Error::ResponseError(error)) } } + +pub async fn fetch_api_key_usage( + configuration: &configuration::Configuration, + params: FetchApiKeyUsageParams, +) -> Result> { + let client = &configuration.client; + + let date = params.date; + let uri = format!( + "{}/api/backend/v1/end_user_api_keys/usage", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::GET, uri.as_str()); + + // assemble the query parameters + req_builder = req_builder.query(&[("date", &date.to_string())]); + if let Some(ref user_id) = params.user_id { + req_builder = req_builder.query(&[("user_id", user_id)]); + } + if let Some(ref org_id) = params.org_id { + req_builder = req_builder.query(&[("org_id", org_id)]); + } + if let Some(ref api_key_id) = params.api_key_id { + req_builder = req_builder.query(&[("api_key_id", api_key_id)]); + } + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error: ResponseContent = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} + +pub async fn import_api_key( + configuration: &configuration::Configuration, + params: ImportApiKeyParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/end_user_api_keys/import", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} + +pub async fn validate_imported_api_key( + configuration: &configuration::Configuration, + params: ValidateApiKeyParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/end_user_api_keys/validate_imported", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} \ No newline at end of file diff --git a/src/apis/employee_service_api.rs b/src/apis/employee_service_api.rs new file mode 100644 index 0000000..03ed035 --- /dev/null +++ b/src/apis/employee_service_api.rs @@ -0,0 +1,89 @@ +/* + * propelauth + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * Generated by: https://openapi-generator.tech + */ + +use reqwest; + +use super::{configuration, Error}; +use crate::propelauth::auth::AUTH_HOSTNAME_HEADER; +use crate::apis::ResponseContent; + +/// struct for passing parameters to the method [`fetch_employee_by_id`] +#[derive(Clone, Debug, Default)] +pub struct FetchEmployeeByIdParams { + pub employee_id: String, +} + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct Employee { + #[serde(rename = "email")] + pub email: String, +} + +/// struct for typed errors of method [`fetch_employee_by_id`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum FetchEmployeeByIdError { + Status401(serde_json::Value), + Status404(serde_json::Value), + UnknownValue(serde_json::Value), +} + + +pub async fn fetch_employee_by_id( + configuration: &configuration::Configuration, + params: FetchEmployeeByIdParams, +) -> Result> { + let local_var_configuration = configuration; + + // unbox the parameters + let employee_id = params.employee_id; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/api/backend/v1/employee/{employee_id}", + local_var_configuration.base_path, + employee_id = crate::apis::urlencode(employee_id) + ); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.header( + AUTH_HOSTNAME_HEADER, + local_var_configuration.auth_hostname.to_owned(), + ); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error: crate::apis::ResponseContent = ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} + diff --git a/src/apis/mfa_service_api.rs b/src/apis/mfa_service_api.rs new file mode 100644 index 0000000..24b82c0 --- /dev/null +++ b/src/apis/mfa_service_api.rs @@ -0,0 +1,229 @@ +/* + * propelauth + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * Generated by: https://openapi-generator.tech + */ + +use reqwest; + +use super::{configuration, Error}; +use crate::propelauth::auth::AUTH_HOSTNAME_HEADER; +use crate::apis::ResponseContent; + +/// struct for passing parameters to the method [`verify_step_up_mfa_totp_challenge`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct VerifyTotpChallengeParams { + pub action_type: String, + pub user_id: String, + pub code: String, + pub grant_type: String, + pub valid_for_seconds: i64, +} + +/// struct for passing parameters to the method [`verify_step_up_grant`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct VerifyStepUpGrantParams { + pub action_type: String, + pub user_id: String, + pub grant: String, +} + +/// struct for passing parameters to the method [`send_sms_mfa_code`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct SendSmsMfaCodeParams { + pub action_type: String, + pub user_id: String, + pub mfa_phone_id: String, + pub grant_type: String, + pub valid_for_seconds: i64, +} + +/// struct for passing parameters to the method [`verify_sms_challenge`] +#[derive(Clone, Debug, Default, Serialize)] +pub struct VerifySmsChallengeParams { + pub challenge_id: String, + pub user_id: String, + pub code: String, +} + + + +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum StepUpMfaError { + InvalidIntegrationAPIKey, + PropelAuthRateLimit, + NotFound, + UnknownValue(serde_json::Value), + UnknownError, + UnexpectedExceptionWithSDK, +} + + +pub async fn verify_step_up_totp_challenge( + configuration: &configuration::Configuration, + params: VerifyTotpChallengeParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/mfa/step-up/verify-totp", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} + +pub async fn verify_step_up_grant( + configuration: &configuration::Configuration, + params: VerifyStepUpGrantParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/mfa/step-up/verify-grant", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + Ok(crate::models::successful_response::SuccessfulResponse { message: None }) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} + +pub async fn send_sms_mfa_code( + configuration: &configuration::Configuration, + params: SendSmsMfaCodeParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/mfa/step-up/phone/send", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} + +pub async fn verify_sms_challenge( + configuration: &configuration::Configuration, + params: VerifySmsChallengeParams, +) -> Result> { + let client = &configuration.client; + + let uri = format!( + "{}/api/backend/v1/mfa/step-up/phone/verify", + configuration.base_path + ); + let mut req_builder = client.request(reqwest::Method::POST, uri.as_str()); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref bearer_token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(bearer_token.to_owned()); + } + req_builder = req_builder.header(AUTH_HOSTNAME_HEADER, configuration.auth_hostname.to_owned()); + + req_builder = req_builder.json(¶ms); + + let req = req_builder.build()?; + let resp = client.execute(req).await?; + + let status = resp.status(); + let content = resp.text().await?; + + if !status.is_client_error() && !status.is_server_error() { + serde_json::from_str(&content).map_err(Error::from) + } else { + let entity: Option = serde_json::from_str(&content).ok(); + let error = ResponseContent { + status, + content, + entity, + }; + Err(Error::ResponseError(error)) + } +} diff --git a/src/apis/mod.rs b/src/apis/mod.rs index 673e87b..a159d61 100644 --- a/src/apis/mod.rs +++ b/src/apis/mod.rs @@ -74,5 +74,7 @@ pub mod api_key_service_api; pub mod auth_service_api; pub mod org_service_api; pub mod user_service_api; +pub mod employee_service_api; +pub mod mfa_service_api; pub mod configuration; diff --git a/src/apis/user_service_api.rs b/src/apis/user_service_api.rs index af637a4..230d409 100644 --- a/src/apis/user_service_api.rs +++ b/src/apis/user_service_api.rs @@ -67,6 +67,18 @@ pub struct FetchUserByIdParams { pub include_orgs: Option, } +/// struct for passing parameters to the method [`fetch_user_signup_query_parameters`] +#[derive(Clone, Debug, Default)] +pub struct FetchUserSignupQueryParamsParams { + pub user_id: String, +} + +/// struct for passing parameters to the method [`fetch_user_signup_query_parameters`] +#[derive(Clone, Debug, Default)] +pub struct FetchUserMfaMethodsParams { + pub user_id: String, +} + /// struct for passing parameters to the method [`fetch_user_by_username`] #[derive(Clone, Debug, Default)] pub struct FetchUserByUsernameParams { @@ -1631,3 +1643,105 @@ pub async fn invite_user_to_org( Err(Error::ResponseError(local_var_error)) } } + +pub async fn fetch_user_signup_query_parameters( + configuration: &configuration::Configuration, + params: FetchUserSignupQueryParamsParams, +) -> Result> { + let local_var_configuration = configuration; + + // unbox the parameters + let user_id = params.user_id; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/api/backend/v1/user/{user_id}/signup_query_parameters", + local_var_configuration.base_path, + user_id = crate::apis::urlencode(user_id) + ); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.header( + AUTH_HOSTNAME_HEADER, + local_var_configuration.auth_hostname.to_owned(), + ); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} + +pub async fn fetch_user_mfa_methods( + configuration: &configuration::Configuration, + params: FetchUserMfaMethodsParams, +) -> Result> { + let local_var_configuration = configuration; + + // unbox the parameters + let user_id = params.user_id; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/api/backend/v1/user/{user_id}/mfa", + local_var_configuration.base_path, + user_id = crate::apis::urlencode(user_id) + ); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.header( + AUTH_HOSTNAME_HEADER, + local_var_configuration.auth_hostname.to_owned(), + ); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} \ No newline at end of file diff --git a/src/models/create_access_token_request.rs b/src/models/create_access_token_request.rs index 5bd4359..c191619 100644 --- a/src/models/create_access_token_request.rs +++ b/src/models/create_access_token_request.rs @@ -4,13 +4,16 @@ pub struct CreateAccessTokenRequest { pub user_id: String, #[serde(rename = "duration_in_minutes")] pub duration_in_minutes: u64, + #[serde(rename = "active_org_id")] + pub active_org_id: Option, } impl CreateAccessTokenRequest { - pub fn new(user_id: String, duration_in_minutes: u64) -> CreateAccessTokenRequest { + pub fn new(user_id: String, duration_in_minutes: u64, active_org_id: Option) -> CreateAccessTokenRequest { CreateAccessTokenRequest { user_id, duration_in_minutes, + active_org_id } } } diff --git a/src/models/create_magic_link_request.rs b/src/models/create_magic_link_request.rs index 7a7678f..23d2ffd 100644 --- a/src/models/create_magic_link_request.rs +++ b/src/models/create_magic_link_request.rs @@ -21,6 +21,8 @@ pub struct CreateMagicLinkRequest { pub expires_in_hours: Option, #[serde(rename = "create_new_user_if_one_doesnt_exist", skip_serializing_if = "Option::is_none")] pub create_new_user_if_one_doesnt_exist: Option, + #[serde(rename = "expire_after_first_use", skip_serializing_if = "Option::is_none")] + pub expire_after_first_use: Option, } impl CreateMagicLinkRequest { @@ -30,6 +32,7 @@ impl CreateMagicLinkRequest { redirect_to_url: None, expires_in_hours: None, create_new_user_if_one_doesnt_exist: None, + expire_after_first_use: None, } } } diff --git a/src/models/fetch_api_key_usage_response.rs b/src/models/fetch_api_key_usage_response.rs new file mode 100644 index 0000000..c273d46 --- /dev/null +++ b/src/models/fetch_api_key_usage_response.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct FetchApiKeyUsageResponse { + #[serde(rename = "count")] + pub count: i64, +} + +impl FetchApiKeyUsageResponse { + pub fn new(count: i64) -> Self { + Self { count } + } +} diff --git a/src/models/fetch_user_mfa_methods_response.rs b/src/models/fetch_user_mfa_methods_response.rs new file mode 100644 index 0000000..5402f60 --- /dev/null +++ b/src/models/fetch_user_mfa_methods_response.rs @@ -0,0 +1,30 @@ + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct MfaPhones { + #[serde(rename = "mfa_phone_number_suffix")] + pub mfa_phone_number_suffix: String, + #[serde(rename = "mfa_phone_id")] + pub mfa_phone_id: String +} + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct MfaSetupType { + #[serde(rename = "type")] + pub type_: String, + #[serde(rename = "phone_numbers")] + pub phone_numbers: Option>, +} + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct FetchUserMfaMethodsResponse { + #[serde(rename = "mfa_setup")] + pub mfa_setup: Option, +} + +impl FetchUserMfaMethodsResponse { + pub fn new(mfa_setup: Option) -> Self { + FetchUserMfaMethodsResponse { + mfa_setup, + } + } +} diff --git a/src/models/fetch_user_signup_query_params_response.rs b/src/models/fetch_user_signup_query_params_response.rs new file mode 100644 index 0000000..cb09f8c --- /dev/null +++ b/src/models/fetch_user_signup_query_params_response.rs @@ -0,0 +1,15 @@ +use std::collections::HashMap; + +use serde_json::Value; + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct FetchSignupQueryParamsResponse { + #[serde(rename = "user_signup_query_parameters")] + pub user_signup_query_parameters: HashMap, +} + +impl FetchSignupQueryParamsResponse { + pub fn new(user_signup_query_parameters: HashMap) -> Self { + Self { user_signup_query_parameters } + } +} diff --git a/src/models/import_api_key_response.rs b/src/models/import_api_key_response.rs new file mode 100644 index 0000000..dc06cc6 --- /dev/null +++ b/src/models/import_api_key_response.rs @@ -0,0 +1,12 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct ImportApiKeyResponse { + pub api_key_id: String, +} + +impl ImportApiKeyResponse { + pub fn new(api_key_id: String) -> Self { + ImportApiKeyResponse { + api_key_id, + } + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index c8740de..ac152ac 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -117,3 +117,19 @@ pub mod fetch_saml_sp_metadata_response; pub use self::fetch_saml_sp_metadata_response::FetchSamlSpMetadataResponse; pub mod set_saml_idp_metadata_request; pub use self::set_saml_idp_metadata_request::SetSamlIdpMetadataRequest; +pub mod fetch_api_key_usage_response; +pub use self::fetch_api_key_usage_response::FetchApiKeyUsageResponse; +pub mod import_api_key_response; +pub use self::import_api_key_response::ImportApiKeyResponse; +pub mod fetch_user_signup_query_params_response; +pub use self::fetch_user_signup_query_params_response::FetchSignupQueryParamsResponse; +pub mod fetch_user_mfa_methods_response; +pub use self::fetch_user_mfa_methods_response::FetchUserMfaMethodsResponse; +pub mod verify_totp_challenge_response; +pub use self::verify_totp_challenge_response::VerifyTotpChallengeResponse; +pub mod verify_grant_response; +pub use self::verify_grant_response::VerifyStepUpGrantResponse; +pub mod send_sms_mfa_code_response; +pub use self::send_sms_mfa_code_response::SendSmsCodeResponse; +pub mod verify_sms_challenge_response; +pub use self::verify_sms_challenge_response::VerifySmsChallengeResponse; diff --git a/src/models/send_sms_mfa_code_response.rs b/src/models/send_sms_mfa_code_response.rs new file mode 100644 index 0000000..54c59bf --- /dev/null +++ b/src/models/send_sms_mfa_code_response.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct SendSmsCodeResponse { + #[serde(rename = "challenge_id")] + pub challenge_id: String, +} + +impl SendSmsCodeResponse { + pub fn new(challenge_id: String) -> Self { + Self { challenge_id } + } +} diff --git a/src/models/verify_grant_response.rs b/src/models/verify_grant_response.rs new file mode 100644 index 0000000..6d8dc84 --- /dev/null +++ b/src/models/verify_grant_response.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct VerifyStepUpGrantResponse { + #[serde(rename = "success")] + pub success: bool, +} + +impl VerifyStepUpGrantResponse { + pub fn new(success: bool) -> Self { + Self { success } + } +} diff --git a/src/models/verify_sms_challenge_response.rs b/src/models/verify_sms_challenge_response.rs new file mode 100644 index 0000000..5afe0d5 --- /dev/null +++ b/src/models/verify_sms_challenge_response.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct VerifySmsChallengeResponse { + #[serde(rename = "step_up_grant")] + pub step_up_grant: String, +} + +impl VerifySmsChallengeResponse { + pub fn new(step_up_grant: String) -> Self { + Self { step_up_grant } + } +} diff --git a/src/models/verify_totp_challenge_response.rs b/src/models/verify_totp_challenge_response.rs new file mode 100644 index 0000000..c152997 --- /dev/null +++ b/src/models/verify_totp_challenge_response.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct VerifyTotpChallengeResponse { + #[serde(rename = "step_up_grant")] + pub step_up_grant: String, +} + +impl VerifyTotpChallengeResponse { + pub fn new(step_up_grant: String) -> Self { + Self { step_up_grant } + } +} diff --git a/src/propelauth/api_key.rs b/src/propelauth/api_key.rs index 244e8ff..7f7ced6 100644 --- a/src/propelauth/api_key.rs +++ b/src/propelauth/api_key.rs @@ -1,13 +1,12 @@ use crate::apis::api_key_service_api::{ - ApiKeyError, ApiKeyQueryParams, ApiKeyValidationErrorResponse, CreateApiKeyParams, - UpdateApiKeyParams, ValidateApiKeyParams, + ApiKeyError, ApiKeyQueryParams, ApiKeyValidationErrorResponse, CreateApiKeyParams, FetchApiKeyUsageError, FetchApiKeyUsageParams, ImportApiKeyParams, UpdateApiKeyParams, ValidateApiKeyParams }; use crate::apis::configuration::Configuration; use crate::models::validate_api_key_response::{ ValidateOrgApiKeyResponse, ValidatePersonalApiKeyResponse, }; use crate::models::{ - CreateApiKeyResponse, FetchApiKeyResponse, FetchApiKeysPagedResponse, ValidateApiKeyResponse, + CreateApiKeyResponse, FetchApiKeyResponse, FetchApiKeyUsageResponse, FetchApiKeysPagedResponse, ImportApiKeyResponse, ValidateApiKeyResponse }; use crate::propelauth::helpers::map_autogenerated_error; @@ -222,4 +221,81 @@ impl ApiKeyService<'_> { user_in_org: resp.user_in_org, }) } + + pub async fn fetch_api_key_usage( + &self, + params: FetchApiKeyUsageParams, + ) -> Result { + crate::apis::api_key_service_api::fetch_api_key_usage(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + FetchApiKeyUsageError::UnexpectedExceptionWithSDK, + |status_code, _| match status_code.as_u16() { + 401 => FetchApiKeyUsageError::InvalidIntegrationAPIKey, + 429 => FetchApiKeyUsageError::PropelAuthRateLimit, + 404 => FetchApiKeyUsageError::NotFound, + _ => FetchApiKeyUsageError::UnknownError, + }, + ) + }) + } + + pub async fn import_api_key( + &self, + params: ImportApiKeyParams, + ) -> Result { + crate::apis::api_key_service_api::import_api_key(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + ApiKeyError::UnexpectedExceptionWithSDK, + |status_code, _| match status_code.as_u16() { + 401 => ApiKeyError::InvalidIntegrationAPIKey, + 429 => ApiKeyError::PropelAuthRateLimit, + 404 => ApiKeyError::NotFound, + _ => ApiKeyError::UnknownError, + }, + ) + }) + } + + pub async fn validate_imported_api_key( + &self, + params: ValidateApiKeyParams, + ) -> Result { + + crate::apis::api_key_service_api::validate_imported_api_key(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + ApiKeyError::UnexpectedExceptionWithSDK, + |status_code, error_response_body| match error_response_body { + Some(ApiKeyValidationErrorResponse::InvalidEndUserApiKey { + api_key_token, + }) => ApiKeyError::InvalidAPIKey { + message: api_key_token, + }, + Some(ApiKeyValidationErrorResponse::EndUserApiKeyRateLimited { + wait_seconds, + user_facing_error, + .. + }) => ApiKeyError::RateLimited { + wait_seconds, + user_facing_error, + }, + None => match status_code.as_u16() { + 401 => ApiKeyError::InvalidIntegrationAPIKey, + 429 => ApiKeyError::PropelAuthRateLimit, + 404 => ApiKeyError::NotFound, + _ => ApiKeyError::UnknownError, + }, + }, + ) + }) + } + } diff --git a/src/propelauth/auth.rs b/src/propelauth/auth.rs index 238eeb2..71b8b87 100644 --- a/src/propelauth/auth.rs +++ b/src/propelauth/auth.rs @@ -7,10 +7,12 @@ use crate::propelauth::access_token::AccessTokenService; use crate::propelauth::api_key::ApiKeyService; use crate::propelauth::errors::InitializationError; use crate::propelauth::helpers::map_autogenerated_error; +use crate::propelauth::mfa::MfaService; use crate::propelauth::options::{AuthOptions, AuthOptionsWithTokenVerification}; use crate::propelauth::org::OrgService; use crate::propelauth::token::TokenService; use crate::propelauth::user::UserService; +use crate::propelauth::employee::EmployeeService; static BACKEND_API_BASE_URL: &str = "https://propelauth-api.com"; pub(crate) static AUTH_HOSTNAME_HEADER: &str = "X-Propelauth-url"; @@ -114,6 +116,20 @@ impl PropelAuth { config: &self.config, } } + + /// API requests related to employees. + pub fn employee(&self) -> EmployeeService { + EmployeeService { + config: &self.config, + } + } + + /// API requests related to employees. + pub fn mfa(&self) -> MfaService { + MfaService { + config: &self.config, + } + } } fn validate_auth_url_extract_hostname(auth_url: &str) -> Result { diff --git a/src/propelauth/employee.rs b/src/propelauth/employee.rs new file mode 100644 index 0000000..42de878 --- /dev/null +++ b/src/propelauth/employee.rs @@ -0,0 +1,39 @@ +use crate::apis::configuration::Configuration; +use crate::apis::employee_service_api::{ + FetchEmployeeByIdParams, Employee, +}; +use crate::propelauth::helpers::{is_valid_id, map_autogenerated_error}; +use crate::propelauth::errors::ErrorsWithNotFound; + +pub struct EmployeeService<'a> { + pub(crate) config: &'a Configuration, +} + +impl EmployeeService<'_> { + + pub async fn fetch_employee_by_id( + &self, + params: FetchEmployeeByIdParams, + ) -> Result { + if !is_valid_id(¶ms.employee_id) { + return Err(ErrorsWithNotFound::NotFound); + } + + crate::apis::employee_service_api::fetch_employee_by_id(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + ErrorsWithNotFound::UnexpectedException, + |status_code, _| match status_code.as_u16() { + 401 => ErrorsWithNotFound::InvalidApiKey, + 429 => ErrorsWithNotFound::PropelAuthRateLimit, + 404 => ErrorsWithNotFound::NotFound, + _ => ErrorsWithNotFound::UnexpectedException, + }, + ) + }) + } + + +} diff --git a/src/propelauth/errors.rs b/src/propelauth/errors.rs index 99e20e6..7f79515 100644 --- a/src/propelauth/errors.rs +++ b/src/propelauth/errors.rs @@ -414,3 +414,100 @@ impl From for UnauthorizedOrForbiddenError { } } } + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum VerifyStepUpTotpChallengeError { + #[error("Invalid API Key")] + InvalidApiKey, + + #[error("Rate limited by PropelAuth")] + PropelAuthRateLimit, + + #[error("User not found")] + UserNotFound, + + #[error("MFA not enabled for this user")] + MfaNotEnabled, + + #[error("Incorrect MFA code")] + IncorrectMfaCode, + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("This feature isn't available on your current pricing plan")] + FeatureGated, + + #[error("Unexpected exception, please try again")] + UnexpectedException, +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum VerifyStepUpGrantError { + #[error("Invalid API Key")] + InvalidApiKey, + + #[error("Rate limited by PropelAuth")] + PropelAuthRateLimit, + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("This feature isn't available on your current pricing plan")] + FeatureGated, + + #[error("Unexpected exception, please try again")] + UnexpectedException, +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum SendSmsCodeError { + #[error("Invalid API Key")] + InvalidApiKey, + + #[error("Rate limited by PropelAuth")] + PropelAuthRateLimit, + + #[error("User not found")] + UserNotFound, + + #[error("MFA not enabled for this user")] + MfaNotEnabled, + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("This feature isn't available on your current pricing plan")] + FeatureGated, + + #[error("Unexpected exception, please try again")] + UnexpectedException, +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum VerifySmsChallengeError { + #[error("Invalid API Key")] + InvalidApiKey, + + #[error("Rate limited by PropelAuth")] + PropelAuthRateLimit, + + #[error("User not found")] + UserNotFound, + + #[error("MFA not enabled for this user")] + MfaNotEnabled, + + #[error("Incorrect MFA code")] + IncorrectMfaCode, + + #[error("Bad request: {0}")] + BadRequest(String), + + #[error("This feature isn't available on your current pricing plan")] + FeatureGated, + + #[error("Unexpected exception, please try again")] + UnexpectedException, +} + diff --git a/src/propelauth/mfa.rs b/src/propelauth/mfa.rs new file mode 100644 index 0000000..c83c014 --- /dev/null +++ b/src/propelauth/mfa.rs @@ -0,0 +1,207 @@ +use crate::apis::configuration::Configuration; +use crate::apis::mfa_service_api::{StepUpMfaError, VerifySmsChallengeParams, VerifyStepUpGrantParams}; +use crate::apis::Error; +use crate::models::{SendSmsCodeResponse, VerifySmsChallengeResponse, VerifyStepUpGrantResponse}; +use crate::propelauth::errors::{VerifyStepUpGrantError, VerifyStepUpTotpChallengeError, SendSmsCodeError, VerifySmsChallengeError}; +use crate::propelauth::helpers::map_autogenerated_error; +use crate::models::VerifyTotpChallengeResponse; +use crate::apis::mfa_service_api::{SendSmsMfaCodeParams, VerifyTotpChallengeParams}; + +pub struct MfaService<'a> { + pub(crate) config: &'a Configuration, +} + +impl MfaService<'_> { + pub async fn verify_step_up_totp_challenge( + &self, + params: VerifyTotpChallengeParams, + ) -> Result { + let result = + crate::apis::mfa_service_api::verify_step_up_totp_challenge(&self.config, params).await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(VerifyStepUpTotpChallengeError::InvalidApiKey); + } else if response.status == 429 { + return Err(VerifyStepUpTotpChallengeError::PropelAuthRateLimit); + } + + let error_response: Result = + serde_json::from_str(&response.content); + if let Ok(error_json) = error_response { + if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) + { + match error_code { + "user_not_found" => { + return Err(VerifyStepUpTotpChallengeError::UserNotFound) + } + "mfa_not_enabled" => { + return Err(VerifyStepUpTotpChallengeError::MfaNotEnabled) + } + "incorrect_mfa_code" => { + return Err(VerifyStepUpTotpChallengeError::IncorrectMfaCode) + } + "invalid_request_fields" => { + return Err(VerifyStepUpTotpChallengeError::BadRequest( + response.content, + )) + } + "feature_gated" => { + return Err(VerifyStepUpTotpChallengeError::FeatureGated) + } + _ => {} + } + } + } + + Err(VerifyStepUpTotpChallengeError::UnexpectedException) + } + Err(_) => Err(VerifyStepUpTotpChallengeError::UnexpectedException), + } + } + + pub async fn verify_step_up_grant( + &self, + params: VerifyStepUpGrantParams, + ) -> Result { + let result = + crate::apis::mfa_service_api::verify_step_up_grant(&self.config, params).await; + + match result { + Ok(_) => Ok(VerifyStepUpGrantResponse { success: true }), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(VerifyStepUpGrantError::InvalidApiKey); + } else if response.status == 429 { + return Err(VerifyStepUpGrantError::PropelAuthRateLimit); + } + + let error_response: Result = + serde_json::from_str(&response.content); + if let Ok(error_json) = error_response { + if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) + { + match error_code { + "invalid_request_fields" => { + if let Some(field_to_errors) = + error_json.get("field_to_errors").and_then(|v| v.as_object()) + { + if let Some(grant_error) = + field_to_errors.get("grant").and_then(|v| v.as_str()) + { + if grant_error == "grant_not_found" { + return Ok(VerifyStepUpGrantResponse { success: false }); + } + } + } + + return Err(VerifyStepUpGrantError::BadRequest(response.content)); + } + "feature_gated" => { + return Err(VerifyStepUpGrantError::FeatureGated) + } + _ => {} + } + } + } + + Err(VerifyStepUpGrantError::UnexpectedException) + } + Err(_) => Err(VerifyStepUpGrantError::UnexpectedException), + } + } + + pub async fn send_sms_mfa_code( + &self, + params: SendSmsMfaCodeParams, + ) -> Result { + let result = + crate::apis::mfa_service_api::send_sms_mfa_code(&self.config, params).await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(SendSmsCodeError::InvalidApiKey); + } else if response.status == 429 { + return Err(SendSmsCodeError::PropelAuthRateLimit); + } + + let error_response: Result = + serde_json::from_str(&response.content); + if let Ok(error_json) = error_response { + if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) + { + match error_code { + "user_not_found" => { + return Err(SendSmsCodeError::UserNotFound) + } + "mfa_not_enabled" => { + return Err(SendSmsCodeError::MfaNotEnabled) + } + "invalid_request_fields" => { + return Err(SendSmsCodeError::BadRequest( + response.content, + )) + } + "feature_gated" => { + return Err(SendSmsCodeError::FeatureGated) + } + _ => {} + } + } + } + Err(SendSmsCodeError::UnexpectedException) + } + Err(_) => Err(SendSmsCodeError::UnexpectedException), + } + } + + pub async fn verify_sms_challenge( + &self, + params: VerifySmsChallengeParams, + ) -> Result { + let result = + crate::apis::mfa_service_api::verify_sms_challenge(&self.config, params).await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(VerifySmsChallengeError::InvalidApiKey); + } else if response.status == 429 { + return Err(VerifySmsChallengeError::PropelAuthRateLimit); + } + + let error_response: Result = + serde_json::from_str(&response.content); + if let Ok(error_json) = error_response { + if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) + { + match error_code { + "user_not_found" => { + return Err(VerifySmsChallengeError::UserNotFound) + } + "mfa_not_enabled" => { + return Err(VerifySmsChallengeError::MfaNotEnabled) + } + "invalid_request_fields" => { + return Err(VerifySmsChallengeError::BadRequest( + response.content, + )) + } + "feature_gated" => { + return Err(VerifySmsChallengeError::FeatureGated) + } + _ => {} + } + } + } + Err(VerifySmsChallengeError::UnexpectedException) + } + Err(_) => Err(VerifySmsChallengeError::UnexpectedException), + } + } +} diff --git a/src/propelauth/mod.rs b/src/propelauth/mod.rs index 4da2b79..2d69864 100644 --- a/src/propelauth/mod.rs +++ b/src/propelauth/mod.rs @@ -8,3 +8,5 @@ pub mod token; pub mod token_models; pub mod user; pub mod access_token; +pub mod employee; +pub mod mfa; \ No newline at end of file diff --git a/src/propelauth/user.rs b/src/propelauth/user.rs index 90ca8aa..1e4e0f6 100644 --- a/src/propelauth/user.rs +++ b/src/propelauth/user.rs @@ -5,11 +5,11 @@ use crate::apis::user_service_api::{ FetchUserByUsernameParams, FetchUsersByEmailsError, FetchUsersByEmailsParams, FetchUsersByIdsError, FetchUsersByIdsParams, FetchUsersByQueryError, FetchUsersByQueryParams, FetchUsersByUsernamesError, FetchUsersByUsernamesParams, MigrateUserParams, MigrateUserPasswordParams, - UpdateUserEmailParams, UpdateUserMetadataParams, UpdateUserPasswordParams, + UpdateUserEmailParams, UpdateUserMetadataParams, UpdateUserPasswordParams, FetchUserSignupQueryParamsParams, FetchUserMfaMethodsParams }; use crate::models::{ CreateMagicLinkRequest, CreateUserRequest, CreatedUserResponse, MagicLink, MigrateUserRequest, MigrateUserPasswordRequest, - UserMetadata, UserPagedResponse, + UserMetadata, UserPagedResponse, FetchSignupQueryParamsResponse, FetchUserMfaMethodsResponse }; use crate::propelauth::errors::{ BatchFetchError, ClearPasswordError, CreateMagicLinkError, CreateUserError, ErrorsWithNotFound, @@ -649,4 +649,52 @@ impl UserService<'_> { })?; Ok(()) } + + pub async fn fetch_user_signup_query_parameters( + &self, + params: FetchUserSignupQueryParamsParams, + ) -> Result { + if !is_valid_id(¶ms.user_id) { + return Err(ErrorsWithNotFound::NotFound); + } + + crate::apis::user_service_api::fetch_user_signup_query_parameters(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + ErrorsWithNotFound::UnexpectedException, + |status_code, _| match status_code.as_u16() { + 401 => ErrorsWithNotFound::InvalidApiKey, + 429 => ErrorsWithNotFound::PropelAuthRateLimit, + 404 => ErrorsWithNotFound::NotFound, + _ => ErrorsWithNotFound::UnexpectedException, + }, + ) + }) + } + + pub async fn fetch_user_mfa_methods( + &self, + params: FetchUserMfaMethodsParams, + ) -> Result { + if !is_valid_id(¶ms.user_id) { + return Err(ErrorsWithNotFound::NotFound); + } + + crate::apis::user_service_api::fetch_user_mfa_methods(&self.config, params) + .await + .map_err(|err| { + map_autogenerated_error( + err, + ErrorsWithNotFound::UnexpectedException, + |status_code, _| match status_code.as_u16() { + 401 => ErrorsWithNotFound::InvalidApiKey, + 429 => ErrorsWithNotFound::PropelAuthRateLimit, + 404 => ErrorsWithNotFound::NotFound, + _ => ErrorsWithNotFound::UnexpectedException, + }, + ) + }) + } }