From 8984a2421c5348ac7a533a2abd3ba4f7acf56b5c Mon Sep 17 00:00:00 2001 From: Fu Zi Xiang Date: Tue, 28 Nov 2023 00:39:36 +0800 Subject: [PATCH] feat: sso saml admin frontend --- admin_frontend/src/models.rs | 7 +++ admin_frontend/src/templates.rs | 15 +++++- admin_frontend/src/web_api.rs | 43 ++++++++++++++-- admin_frontend/src/web_app.rs | 49 ++++++++++++++++--- .../components/admin_sso_create.html | 31 ++++++++++++ .../components/admin_sso_detail.html | 42 ++++++++++++++++ .../templates/components/admin_sso_list.html | 36 +++++++++++++- libs/gotrue-entity/src/sso.rs | 8 +-- libs/gotrue/src/params.rs | 1 + tem | 0 10 files changed, 214 insertions(+), 18 deletions(-) create mode 100644 admin_frontend/templates/components/admin_sso_create.html create mode 100644 admin_frontend/templates/components/admin_sso_detail.html create mode 100644 tem diff --git a/admin_frontend/src/models.rs b/admin_frontend/src/models.rs index 38e07d30..f8a79515 100644 --- a/admin_frontend/src/models.rs +++ b/admin_frontend/src/models.rs @@ -28,3 +28,10 @@ pub struct WebApiAdminCreateUserRequest { pub struct WebApiInviteUserRequest { pub email: String, } + +#[derive(Deserialize)] +pub struct WebApiCreateSSOProviderRequest { + #[serde(rename = "type")] + pub type_: String, + pub metadata_url: String, +} diff --git a/admin_frontend/src/templates.rs b/admin_frontend/src/templates.rs index 548557af..b2414b01 100644 --- a/admin_frontend/src/templates.rs +++ b/admin_frontend/src/templates.rs @@ -1,10 +1,21 @@ use askama::Template; -use gotrue_entity::dto::User; +use gotrue_entity::{dto::User, sso::SSOProvider}; + +#[derive(Template)] +#[template(path = "components/admin_sso_detail.html")] +pub struct SsoDetail { + pub sso_provider: SSOProvider, + pub mapping_json: String, +} + +#[derive(Template)] +#[template(path = "components/admin_sso_create.html")] +pub struct SsoCreate; #[derive(Template)] #[template(path = "components/admin_sso_list.html")] pub struct SsoList { - // TODO + pub sso_providers: Vec, } #[derive(Template)] diff --git a/admin_frontend/src/web_api.rs b/admin_frontend/src/web_api.rs index 51ccb02d..a547ea0c 100644 --- a/admin_frontend/src/web_api.rs +++ b/admin_frontend/src/web_api.rs @@ -1,7 +1,7 @@ use crate::error::WebApiError; use crate::models::{ - WebApiAdminCreateUserRequest, WebApiChangePasswordRequest, WebApiInviteUserRequest, - WebApiPutUserRequest, + WebApiAdminCreateUserRequest, WebApiChangePasswordRequest, WebApiCreateSSOProviderRequest, + WebApiInviteUserRequest, WebApiPutUserRequest, }; use crate::response::WebApiResponse; use crate::session::{self, UserSession}; @@ -14,7 +14,10 @@ use axum::Form; use axum::{extract::State, routing::post, Router}; use axum_extra::extract::cookie::Cookie; use axum_extra::extract::CookieJar; -use gotrue::params::{AdminDeleteUserParams, AdminUserParams, GenerateLinkParams, MagicLinkParams}; +use gotrue::params::{ + AdminDeleteUserParams, AdminUserParams, CreateSSOProviderParams, GenerateLinkParams, + MagicLinkParams, +}; use gotrue_entity::dto::{GotrueTokenResponse, SignUpResponse, UpdateGotrueUserParams, User}; use gotrue_entity::error::GoTrueError; @@ -40,6 +43,40 @@ pub fn router() -> Router { "/admin/user/:email/generate-link", post(post_user_generate_link_handler), ) + .route("/admin/sso", post(admin_create_sso_handler)) + .route("/admin/sso/:provider_id", delete(admin_delete_sso_handler)) +} + +pub async fn admin_delete_sso_handler( + State(state): State, + session: UserSession, + Path(provider_id): Path, +) -> Result, WebApiError<'static>> { + let _ = state + .gotrue_client + .admin_delete_sso_provider(&session.token.access_token, &provider_id) + .await?; + + Ok(WebApiResponse::<()>::from_str("SSO Deleted".into())) +} + +pub async fn admin_create_sso_handler( + State(state): State, + session: UserSession, + Form(param): Form, +) -> Result, WebApiError<'static>> { + let provider_params = CreateSSOProviderParams { + type_: param.type_, + metadata_url: param.metadata_url, + ..Default::default() + }; + + let _ = state + .gotrue_client + .admin_create_sso_providers(&session.token.access_token, &provider_params) + .await?; + + Ok(WebApiResponse::<()>::from_str("SSO Added".into())) } // provide a link which when open in browser, opens the appflowy app diff --git a/admin_frontend/src/web_app.rs b/admin_frontend/src/web_app.rs index b65fc1c7..4e3796d0 100644 --- a/admin_frontend/src/web_app.rs +++ b/admin_frontend/src/web_app.rs @@ -37,26 +37,61 @@ pub fn component_router() -> Router { .route("/admin/users/create", get(admin_users_create_handler)) // SSO .route("/admin/sso", get(admin_sso_handler)) + .route("/admin/sso/create", get(admin_sso_create_handler)) + .route("/admin/sso/:sso_provider_id", get(admin_sso_detail_handler)) } -pub async fn admin_sso_handler() -> Result, WebAppError> { - render_template(templates::SsoList {}) +pub async fn admin_sso_detail_handler( + State(state): State, + session: UserSession, + Path(sso_provider_id): Path, +) -> Result, WebAppError> { + let sso_provider = state + .gotrue_client + .admin_get_sso_provider(&session.token.access_token, &sso_provider_id) + .await?; + + let mapping_json = + serde_json::to_string_pretty(&sso_provider.saml.attribute_mapping).unwrap_or("".to_owned()); + + render_template(templates::SsoDetail { + sso_provider, + mapping_json, + }) +} + +pub async fn admin_sso_create_handler() -> Result, WebAppError> { + render_template(templates::SsoCreate) +} + +pub async fn admin_sso_handler( + State(state): State, + session: UserSession, +) -> Result, WebAppError> { + let sso_providers = state + .gotrue_client + .admin_list_sso_providers(&session.token.access_token) + .await? + .items + .unwrap_or_default(); + + render_template(templates::SsoList { sso_providers }) } pub async fn user_navigate_handler() -> Result, WebAppError> { - render_template(templates::Navigate {}) + render_template(templates::Navigate) } pub async fn admin_navigate_handler() -> Result, WebAppError> { - render_template(templates::AdminNavigate {}) + render_template(templates::AdminNavigate) } pub async fn user_invite_handler() -> Result, WebAppError> { - render_template(templates::Invite {}) + render_template(templates::Invite) } pub async fn admin_users_create_handler() -> Result, WebAppError> { - render_template(templates::CreateUser {}) + render_template(templates::CreateUser) } pub async fn user_user_handler( @@ -77,7 +112,7 @@ pub async fn login_handler(State(state): State) -> Result } pub async fn user_change_password_handler() -> Result, WebAppError> { - render_template(templates::ChangePassword {}) + render_template(templates::ChangePassword) } pub async fn home_handler( diff --git a/admin_frontend/templates/components/admin_sso_create.html b/admin_frontend/templates/components/admin_sso_create.html new file mode 100644 index 00000000..dfa789d7 --- /dev/null +++ b/admin_frontend/templates/components/admin_sso_create.html @@ -0,0 +1,31 @@ +
+

Please enter the following information to create new SSO

+
+ + + + + + + + + + + + + +
Email + +
Metadata Url + +
+ +
+
+
diff --git a/admin_frontend/templates/components/admin_sso_detail.html b/admin_frontend/templates/components/admin_sso_detail.html new file mode 100644 index 00000000..c1204c13 --- /dev/null +++ b/admin_frontend/templates/components/admin_sso_detail.html @@ -0,0 +1,42 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{ sso_provider.id|escape }}
Entity ID{{ sso_provider.saml.entity_id|escape }}
Domains +
    + {% for domain in sso_provider.domains %} +
  • {{ domain|escape }}
  • + {% endfor %} +
+
Created At{{ sso_provider.created_at|escape }}
Updated At{{ sso_provider.updated_at|escape }}
Metadata XML + {{ sso_provider.saml.metadata_xml|default("")|escape }} +
Attribute Mapping + {{ mapping_json|escape }} +
+
diff --git a/admin_frontend/templates/components/admin_sso_list.html b/admin_frontend/templates/components/admin_sso_list.html index e992205a..40a70602 100644 --- a/admin_frontend/templates/components/admin_sso_list.html +++ b/admin_frontend/templates/components/admin_sso_list.html @@ -1,3 +1,35 @@ -
- TODO <--> +
+ + + + + + + + {% for sso_provider in sso_providers %} + + + + + + + {% endfor %} +
Entity IDCreated AtActions
{{ sso_provider.saml.entity_id|escape }}{{ sso_provider.created_at|escape }} + + +
diff --git a/libs/gotrue-entity/src/sso.rs b/libs/gotrue-entity/src/sso.rs index 066d8564..ff16d15f 100644 --- a/libs/gotrue-entity/src/sso.rs +++ b/libs/gotrue-entity/src/sso.rs @@ -1,10 +1,10 @@ use std::collections::BTreeMap; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize)] pub struct SSOProviders { - pub items: Vec, + pub items: Option>, } #[derive(Debug, Deserialize)] @@ -24,12 +24,12 @@ pub struct SAMLProvider { pub attribute_mapping: SAMLAttributeMapping, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct SAMLAttributeMapping { pub keys: Option>, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct SAMLAttribute { pub name: Option, pub names: Option>, diff --git a/libs/gotrue/src/params.rs b/libs/gotrue/src/params.rs index 2f23feef..3a1dd390 100644 --- a/libs/gotrue/src/params.rs +++ b/libs/gotrue/src/params.rs @@ -108,6 +108,7 @@ pub struct GenerateLinkResponse { #[derive(Debug, Serialize, Default)] pub struct CreateSSOProviderParams { + #[serde(rename = "type")] pub type_: String, pub metadata_url: String, pub metadata_xml: String, diff --git a/tem b/tem new file mode 100644 index 00000000..e69de29b