From c35e121ccf6e2910d8df2149d70fda455440ea1b Mon Sep 17 00:00:00 2001 From: Fu Zi Xiang Date: Mon, 27 Nov 2023 15:45:10 +0800 Subject: [PATCH] feat: sso saml admin placeholders --- admin_frontend/src/templates.rs | 6 ++ admin_frontend/src/web_app.rs | 6 ++ .../templates/components/admin_sidebar.html | 14 ++++ .../templates/components/admin_sso_list.html | 3 + doc/OKTA_SAML.md | 5 ++ libs/gotrue-entity/src/lib.rs | 1 + libs/gotrue-entity/src/sso.rs | 41 ++++++++++ libs/gotrue/src/api.rs | 76 ++++++++++++++++++- libs/gotrue/src/params.rs | 9 +++ 9 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 admin_frontend/templates/components/admin_sso_list.html create mode 100644 doc/OKTA_SAML.md create mode 100644 libs/gotrue-entity/src/sso.rs diff --git a/admin_frontend/src/templates.rs b/admin_frontend/src/templates.rs index 745a9126..548557af 100644 --- a/admin_frontend/src/templates.rs +++ b/admin_frontend/src/templates.rs @@ -1,6 +1,12 @@ use askama::Template; use gotrue_entity::dto::User; +#[derive(Template)] +#[template(path = "components/admin_sso_list.html")] +pub struct SsoList { + // TODO +} + #[derive(Template)] #[template(path = "components/change_password.html")] pub struct ChangePassword; diff --git a/admin_frontend/src/web_app.rs b/admin_frontend/src/web_app.rs index c41b787a..b65fc1c7 100644 --- a/admin_frontend/src/web_app.rs +++ b/admin_frontend/src/web_app.rs @@ -35,6 +35,12 @@ pub fn component_router() -> Router { .route("/admin/users", get(admin_users_handler)) .route("/admin/users/:user_id", get(admin_user_details_handler)) .route("/admin/users/create", get(admin_users_create_handler)) + // SSO + .route("/admin/sso", get(admin_sso_handler)) +} + +pub async fn admin_sso_handler() -> Result, WebAppError> { + render_template(templates::SsoList {}) } pub async fn user_navigate_handler() -> Result, WebAppError> { diff --git a/admin_frontend/templates/components/admin_sidebar.html b/admin_frontend/templates/components/admin_sidebar.html index 88034203..690551a9 100644 --- a/admin_frontend/templates/components/admin_sidebar.html +++ b/admin_frontend/templates/components/admin_sidebar.html @@ -22,4 +22,18 @@ > Create User + + diff --git a/admin_frontend/templates/components/admin_sso_list.html b/admin_frontend/templates/components/admin_sso_list.html new file mode 100644 index 00000000..e992205a --- /dev/null +++ b/admin_frontend/templates/components/admin_sso_list.html @@ -0,0 +1,3 @@ +
+ TODO <--> +
diff --git a/doc/OKTA_SAML.md b/doc/OKTA_SAML.md new file mode 100644 index 00000000..07834be9 --- /dev/null +++ b/doc/OKTA_SAML.md @@ -0,0 +1,5 @@ +# Okta Authentication via SAML +- Guide for adding AppFlowy to [Okta](https://www.okta.com) +- This guide assumes the following + - You are an Admin of Okta Identity Provider + - You have AppFlowy-Cloud deployed [Deployment](./DEPLOYMENT.md) diff --git a/libs/gotrue-entity/src/lib.rs b/libs/gotrue-entity/src/lib.rs index 2d3812ed..a4fd6e2b 100644 --- a/libs/gotrue-entity/src/lib.rs +++ b/libs/gotrue-entity/src/lib.rs @@ -3,3 +3,4 @@ pub mod error { pub use app_error::gotrue::*; } pub mod gotrue_jwt; +pub mod sso; diff --git a/libs/gotrue-entity/src/sso.rs b/libs/gotrue-entity/src/sso.rs new file mode 100644 index 00000000..066d8564 --- /dev/null +++ b/libs/gotrue-entity/src/sso.rs @@ -0,0 +1,41 @@ +use std::collections::BTreeMap; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct SSOProviders { + pub items: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct SSOProvider { + pub id: String, + pub saml: SAMLProvider, + pub domains: Vec, + pub created_at: String, + pub updated_at: String, +} + +#[derive(Debug, Deserialize)] +pub struct SAMLProvider { + pub entity_id: String, + pub metadata_xml: Option, + pub metadata_url: Option, + pub attribute_mapping: SAMLAttributeMapping, +} + +#[derive(Debug, Deserialize)] +pub struct SAMLAttributeMapping { + pub keys: Option>, +} + +#[derive(Debug, Deserialize)] +pub struct SAMLAttribute { + pub name: Option, + pub names: Option>, + pub default: serde_json::Value, +} + +pub struct SSODomain { + pub domain: String, +} diff --git a/libs/gotrue/src/api.rs b/libs/gotrue/src/api.rs index 7aae8a39..0f8075e2 100644 --- a/libs/gotrue/src/api.rs +++ b/libs/gotrue/src/api.rs @@ -1,6 +1,7 @@ use super::grant::Grant; use crate::params::{ - AdminDeleteUserParams, AdminUserParams, GenerateLinkParams, GenerateLinkResponse, MagicLinkParams, + AdminDeleteUserParams, AdminUserParams, CreateSSOProviderParams, GenerateLinkParams, + GenerateLinkResponse, MagicLinkParams, }; use anyhow::Context; use gotrue_entity::dto::{ @@ -8,6 +9,7 @@ use gotrue_entity::dto::{ UpdateGotrueUserParams, User, }; use gotrue_entity::error::{GoTrueError, GoTrueErrorSerde, GotrueClientError}; +use gotrue_entity::sso::{SSOProvider, SSOProviders}; use infra::reqwest::{check_response, from_body, from_response}; #[derive(Clone)] @@ -235,6 +237,78 @@ impl Client { .await?; check_gotrue_result(resp).await } + + pub async fn admin_list_sso_providers( + &self, + access_token: &str, + ) -> Result { + let resp = self + .client + .get(format!("{}/admin/sso/providers", self.base_url)) + .header("Authorization", format!("Bearer {}", access_token)) + .send() + .await?; + to_gotrue_result(resp).await + } + + pub async fn admin_create_sso_providers( + &self, + access_token: &str, + create_sso_provider_params: &CreateSSOProviderParams, + ) -> Result { + let resp = self + .client + .post(format!("{}/admin/sso/providers", self.base_url)) + .header("Authorization", format!("Bearer {}", access_token)) + .json(create_sso_provider_params) + .send() + .await?; + to_gotrue_result(resp).await + } + + pub async fn admin_get_sso_provider( + &self, + access_token: &str, + idp_id: &str, + ) -> Result { + let resp = self + .client + .get(format!("{}/admin/sso/providers/{}", self.base_url, idp_id)) + .header("Authorization", format!("Bearer {}", access_token)) + .send() + .await?; + to_gotrue_result(resp).await + } + + pub async fn admin_update_sso_provider( + &self, + access_token: &str, + idp_id: &str, + create_sso_provider_params: &CreateSSOProviderParams, + ) -> Result { + let resp = self + .client + .put(format!("{}/admin/sso/providers/{}", self.base_url, idp_id)) + .header("Authorization", format!("Bearer {}", access_token)) + .json(create_sso_provider_params) + .send() + .await?; + to_gotrue_result(resp).await + } + + pub async fn admin_delete_sso_provider( + &self, + access_token: &str, + idp_id: &str, + ) -> Result { + let resp = self + .client + .delete(format!("{}/admin/sso/providers/{}", self.base_url, idp_id)) + .header("Authorization", format!("Bearer {}", access_token)) + .send() + .await?; + to_gotrue_result(resp).await + } } async fn to_gotrue_result(resp: reqwest::Response) -> Result diff --git a/libs/gotrue/src/params.rs b/libs/gotrue/src/params.rs index d4f34746..2f23feef 100644 --- a/libs/gotrue/src/params.rs +++ b/libs/gotrue/src/params.rs @@ -105,3 +105,12 @@ pub struct GenerateLinkResponse { pub verification_type: String, pub redirect_to: String, } + +#[derive(Debug, Serialize, Default)] +pub struct CreateSSOProviderParams { + pub type_: String, + pub metadata_url: String, + pub metadata_xml: String, + pub domains: Vec, + pub attribute_mapping: serde_json::Value, +}