feat: sso saml admin frontend
This commit is contained in:
parent
c35e121ccf
commit
8984a2421c
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SSOProvider>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
|
|
|
|||
|
|
@ -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<AppState> {
|
|||
"/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<AppState>,
|
||||
session: UserSession,
|
||||
Path(provider_id): Path<String>,
|
||||
) -> Result<WebApiResponse<()>, 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<AppState>,
|
||||
session: UserSession,
|
||||
Form(param): Form<WebApiCreateSSOProviderRequest>,
|
||||
) -> Result<WebApiResponse<()>, 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
|
||||
|
|
|
|||
|
|
@ -37,26 +37,61 @@ pub fn component_router() -> Router<AppState> {
|
|||
.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<Html<String>, WebAppError> {
|
||||
render_template(templates::SsoList {})
|
||||
pub async fn admin_sso_detail_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
Path(sso_provider_id): Path<String>,
|
||||
) -> Result<Html<String>, 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<Html<String>, WebAppError> {
|
||||
render_template(templates::SsoCreate)
|
||||
}
|
||||
|
||||
pub async fn admin_sso_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
) -> Result<Html<String>, 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<Html<String>, WebAppError> {
|
||||
render_template(templates::Navigate {})
|
||||
render_template(templates::Navigate)
|
||||
}
|
||||
|
||||
pub async fn admin_navigate_handler() -> Result<Html<String>, WebAppError> {
|
||||
render_template(templates::AdminNavigate {})
|
||||
render_template(templates::AdminNavigate)
|
||||
}
|
||||
|
||||
pub async fn user_invite_handler() -> Result<Html<String>, WebAppError> {
|
||||
render_template(templates::Invite {})
|
||||
render_template(templates::Invite)
|
||||
}
|
||||
|
||||
pub async fn admin_users_create_handler() -> Result<Html<String>, 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<AppState>) -> Result<Html<String>
|
|||
}
|
||||
|
||||
pub async fn user_change_password_handler() -> Result<Html<String>, WebAppError> {
|
||||
render_template(templates::ChangePassword {})
|
||||
render_template(templates::ChangePassword)
|
||||
}
|
||||
|
||||
pub async fn home_handler(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<div>
|
||||
<h4>Please enter the following information to create new SSO</h4>
|
||||
<form hx-post="/web-api/admin/sso" hx-target="#none">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Email</td>
|
||||
<td>
|
||||
<select name="type" class="input">
|
||||
<option value="saml">saml</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Metadata Url</td>
|
||||
<td>
|
||||
<input
|
||||
class="input"
|
||||
name="metadata_url"
|
||||
placeholder="https://example.com/metadata"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style="text-align: right">
|
||||
<button class="button cyan" type="submit">Create</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">ID</td>
|
||||
<td>{{ sso_provider.id|escape }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">Entity ID</td>
|
||||
<td>{{ sso_provider.saml.entity_id|escape }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">Domains</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for domain in sso_provider.domains %}
|
||||
<li>{{ domain|escape }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">Created At</td>
|
||||
<td>{{ sso_provider.created_at|escape }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">Updated At</td>
|
||||
<td>{{ sso_provider.updated_at|escape }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap; align">Metadata XML</td>
|
||||
<td>
|
||||
<code> {{ sso_provider.saml.metadata_xml|default("")|escape }} </code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="white-space: nowrap">Attribute Mapping</td>
|
||||
<td>
|
||||
<code> {{ mapping_json|escape }} </code>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -1,3 +1,35 @@
|
|||
<div>
|
||||
<!--> TODO <-->
|
||||
<div id="sso-list">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Entity ID</th>
|
||||
<th>Created At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
||||
{% for sso_provider in sso_providers %}
|
||||
<tr>
|
||||
<td>{{ sso_provider.saml.entity_id|escape }}</td>
|
||||
<td>{{ sso_provider.created_at|escape }}</td>
|
||||
|
||||
<td>
|
||||
<button
|
||||
class="button cyan"
|
||||
hx-target="#sso-list"
|
||||
hx-get="/web/components/admin/sso/{{ sso_provider.id }}"
|
||||
>
|
||||
More Info
|
||||
</button>
|
||||
<button
|
||||
class="deletUserBtn button red"
|
||||
hx-delete="/web-api/admin/sso/{{ sso_provider.id }}"
|
||||
hx-confirm="Are you sure?"
|
||||
hx-target="closest tr"
|
||||
hx-swap="delete"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SSOProviders {
|
||||
pub items: Vec<SSOProvider>,
|
||||
pub items: Option<Vec<SSOProvider>>,
|
||||
}
|
||||
|
||||
#[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<BTreeMap<String, SAMLAttribute>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SAMLAttribute {
|
||||
pub name: Option<String>,
|
||||
pub names: Option<Vec<String>>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue