Allow setting an explicit upstream account name (#3600)
This commit is contained in:
@@ -955,9 +955,10 @@ impl UserCreationRequest<'_> {
|
||||
}
|
||||
|
||||
for (provider, subject) in upstream_provider_mappings {
|
||||
// Note that we don't pass a human_account_name here, as we don't ask for it
|
||||
let link = repo
|
||||
.upstream_oauth_link()
|
||||
.add(rng, clock, provider, subject)
|
||||
.add(rng, clock, provider, subject, None)
|
||||
.await?;
|
||||
|
||||
repo.upstream_oauth_link()
|
||||
|
||||
@@ -67,6 +67,9 @@ fn map_claims_imports(
|
||||
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Import
|
||||
}
|
||||
},
|
||||
account_name: mas_data_model::UpstreamOAuthProviderSubjectPreference {
|
||||
template: config.account_name.template.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +285,23 @@ impl EmailImportPreference {
|
||||
}
|
||||
}
|
||||
|
||||
/// What should be done for the account name attribute
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct AccountNameImportPreference {
|
||||
/// The Jinja2 template to use for the account name. This name is only used
|
||||
/// for display purposes.
|
||||
///
|
||||
/// If not provided, it will be ignored.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
impl AccountNameImportPreference {
|
||||
const fn is_default(&self) -> bool {
|
||||
self.template.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// How claims should be imported
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
|
||||
pub struct ClaimsImports {
|
||||
@@ -307,6 +324,13 @@ pub struct ClaimsImports {
|
||||
/// `email_verified` claims
|
||||
#[serde(default, skip_serializing_if = "EmailImportPreference::is_default")]
|
||||
pub email: EmailImportPreference,
|
||||
|
||||
/// Set a human-readable name for the upstream account for display purposes
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "AccountNameImportPreference::is_default"
|
||||
)]
|
||||
pub account_name: AccountNameImportPreference,
|
||||
}
|
||||
|
||||
impl ClaimsImports {
|
||||
|
||||
@@ -14,5 +14,6 @@ pub struct UpstreamOAuthLink {
|
||||
pub provider_id: Ulid,
|
||||
pub user_id: Option<Ulid>,
|
||||
pub subject: String,
|
||||
pub human_account_name: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -301,10 +301,14 @@ pub struct ClaimsImports {
|
||||
#[serde(default)]
|
||||
pub email: ImportPreference,
|
||||
|
||||
#[serde(default)]
|
||||
pub account_name: SubjectPreference,
|
||||
|
||||
#[serde(default)]
|
||||
pub verify_email: SetEmailVerification,
|
||||
}
|
||||
|
||||
// XXX: this should have another name
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct SubjectPreference {
|
||||
#[serde(default)]
|
||||
|
||||
@@ -359,7 +359,7 @@ pub(crate) async fn handler(
|
||||
.as_deref()
|
||||
.unwrap_or("{{ user.sub }}");
|
||||
let subject = env
|
||||
.render_str(template, context)
|
||||
.render_str(template, context.clone())
|
||||
.map_err(RouteError::ExtractSubject)?;
|
||||
|
||||
if subject.is_empty() {
|
||||
@@ -375,8 +375,26 @@ pub(crate) async fn handler(
|
||||
let link = if let Some(link) = maybe_link {
|
||||
link
|
||||
} else {
|
||||
// Try to render the human account name if we have one,
|
||||
// but just log if it fails
|
||||
let human_account_name = provider
|
||||
.claims_imports
|
||||
.account_name
|
||||
.template
|
||||
.as_deref()
|
||||
.and_then(|template| match env.render_str(template, context) {
|
||||
Ok(name) => Some(name),
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
error = &e as &dyn std::error::Error,
|
||||
"Failed to render account name"
|
||||
);
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
repo.upstream_oauth_link()
|
||||
.add(&mut rng, &clock, &provider, subject)
|
||||
.add(&mut rng, &clock, &provider, subject, human_account_name)
|
||||
.await?
|
||||
};
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ pub(crate) async fn get(
|
||||
.await?
|
||||
.ok_or(RouteError::ProviderNotFound)?;
|
||||
|
||||
let ctx = UpstreamRegister::default();
|
||||
let ctx = UpstreamRegister::new(link.clone(), provider.clone());
|
||||
|
||||
let env = environment();
|
||||
|
||||
@@ -596,7 +596,7 @@ pub(crate) async fn post(
|
||||
.map_or(false, |v| v == "true");
|
||||
|
||||
// Create a template context in case we need to re-render because of an error
|
||||
let ctx = UpstreamRegister::default();
|
||||
let ctx = UpstreamRegister::new(link.clone(), provider.clone());
|
||||
|
||||
let display_name = if provider
|
||||
.claims_imports
|
||||
@@ -954,7 +954,13 @@ mod tests {
|
||||
|
||||
let link = repo
|
||||
.upstream_oauth_link()
|
||||
.add(&mut rng, &state.clock, &provider, "subject".to_owned())
|
||||
.add(
|
||||
&mut rng,
|
||||
&state.clock,
|
||||
&provider,
|
||||
"subject".to_owned(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n created_at\n FROM upstream_oauth_links\n WHERE upstream_oauth_provider_id = $1\n AND subject = $2\n ",
|
||||
"query": "\n SELECT\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n human_account_name,\n created_at\n FROM upstream_oauth_links\n WHERE upstream_oauth_provider_id = $1\n AND subject = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -25,6 +25,11 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "human_account_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
@@ -40,8 +45,9 @@
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e6dc63984aced9e19c20e90e9cd75d6f6d7ade64f782697715ac4da077b2e1fc"
|
||||
"hash": "5402b8ddb674d05319830477eb3e72ecb536092b46c92a7dda01598962842323"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n created_at\n FROM upstream_oauth_links\n WHERE upstream_oauth_link_id = $1\n ",
|
||||
"query": "\n SELECT\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n human_account_name,\n created_at\n FROM upstream_oauth_links\n WHERE upstream_oauth_link_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -25,6 +25,11 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "human_account_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
@@ -39,8 +44,9 @@
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4187907bfc770b2c76f741671d5e672f5c35eed7c9a9e57ff52888b1768a5ed6"
|
||||
"hash": "785e6bceed803cb1caccc373cde0c999d601f3a9730e6bbb40cfc43c04195c61"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO upstream_oauth_links (\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n created_at\n ) VALUES ($1, $2, NULL, $3, $4)\n ",
|
||||
"query": "\n INSERT INTO upstream_oauth_links (\n upstream_oauth_link_id,\n upstream_oauth_provider_id,\n user_id,\n subject,\n human_account_name,\n created_at\n ) VALUES ($1, $2, NULL, $3, $4, $5)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -8,10 +8,11 @@
|
||||
"Uuid",
|
||||
"Uuid",
|
||||
"Text",
|
||||
"Text",
|
||||
"Timestamptz"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5f6b7e38ef9bc3b39deabba277d0255fb8cfb2adaa65f47b78a8fac11d8c91c3"
|
||||
"hash": "9eaf35f045aaca8473efc4a1f529afe24f01d9ec34609f373db5c535ccb58516"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Copyright 2024 New Vector Ltd.
|
||||
--
|
||||
-- SPDX-License-Identifier: AGPL-3.0-only
|
||||
-- Please see LICENSE in the repository root for full details.
|
||||
|
||||
-- Add the human_account_name column to the upstream_oauth_links table to store
|
||||
-- a human-readable name for the upstream account
|
||||
ALTER TABLE "upstream_oauth_links"
|
||||
ADD COLUMN "human_account_name" TEXT;
|
||||
@@ -122,5 +122,6 @@ pub enum UpstreamOAuthLinks {
|
||||
UpstreamOAuthProviderId,
|
||||
UserId,
|
||||
Subject,
|
||||
HumanAccountName,
|
||||
CreatedAt,
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ struct LinkLookup {
|
||||
upstream_oauth_provider_id: Uuid,
|
||||
user_id: Option<Uuid>,
|
||||
subject: String,
|
||||
human_account_name: Option<String>,
|
||||
created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -57,6 +58,7 @@ impl From<LinkLookup> for UpstreamOAuthLink {
|
||||
provider_id: Ulid::from(value.upstream_oauth_provider_id),
|
||||
user_id: value.user_id.map(Ulid::from),
|
||||
subject: value.subject,
|
||||
human_account_name: value.human_account_name,
|
||||
created_at: value.created_at,
|
||||
}
|
||||
}
|
||||
@@ -124,6 +126,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
upstream_oauth_provider_id,
|
||||
user_id,
|
||||
subject,
|
||||
human_account_name,
|
||||
created_at
|
||||
FROM upstream_oauth_links
|
||||
WHERE upstream_oauth_link_id = $1
|
||||
@@ -163,6 +166,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
upstream_oauth_provider_id,
|
||||
user_id,
|
||||
subject,
|
||||
human_account_name,
|
||||
created_at
|
||||
FROM upstream_oauth_links
|
||||
WHERE upstream_oauth_provider_id = $1
|
||||
@@ -186,6 +190,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
db.query.text,
|
||||
upstream_oauth_link.id,
|
||||
upstream_oauth_link.subject = subject,
|
||||
upstream_oauth_link.human_account_name = human_account_name,
|
||||
%upstream_oauth_provider.id,
|
||||
%upstream_oauth_provider.issuer,
|
||||
%upstream_oauth_provider.client_id,
|
||||
@@ -198,6 +203,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
clock: &dyn Clock,
|
||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||
subject: String,
|
||||
human_account_name: Option<String>,
|
||||
) -> Result<UpstreamOAuthLink, Self::Error> {
|
||||
let created_at = clock.now();
|
||||
let id = Ulid::from_datetime_with_source(created_at.into(), rng);
|
||||
@@ -210,12 +216,14 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
upstream_oauth_provider_id,
|
||||
user_id,
|
||||
subject,
|
||||
human_account_name,
|
||||
created_at
|
||||
) VALUES ($1, $2, NULL, $3, $4)
|
||||
) VALUES ($1, $2, NULL, $3, $4, $5)
|
||||
"#,
|
||||
Uuid::from(id),
|
||||
Uuid::from(upstream_oauth_provider.id),
|
||||
&subject,
|
||||
human_account_name.as_deref(),
|
||||
created_at,
|
||||
)
|
||||
.traced()
|
||||
@@ -227,6 +235,7 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
provider_id: upstream_oauth_provider.id,
|
||||
user_id: None,
|
||||
subject,
|
||||
human_account_name,
|
||||
created_at,
|
||||
})
|
||||
}
|
||||
@@ -300,6 +309,13 @@ impl<'c> UpstreamOAuthLinkRepository for PgUpstreamOAuthLinkRepository<'c> {
|
||||
Expr::col((UpstreamOAuthLinks::Table, UpstreamOAuthLinks::Subject)),
|
||||
LinkLookupIden::Subject,
|
||||
)
|
||||
.expr_as(
|
||||
Expr::col((
|
||||
UpstreamOAuthLinks::Table,
|
||||
UpstreamOAuthLinks::HumanAccountName,
|
||||
)),
|
||||
LinkLookupIden::HumanAccountName,
|
||||
)
|
||||
.expr_as(
|
||||
Expr::col((UpstreamOAuthLinks::Table, UpstreamOAuthLinks::CreatedAt)),
|
||||
LinkLookupIden::CreatedAt,
|
||||
|
||||
@@ -124,7 +124,7 @@ mod tests {
|
||||
// Create a link
|
||||
let link = repo
|
||||
.upstream_oauth_link()
|
||||
.add(&mut rng, &clock, &provider, "a-subject".to_owned())
|
||||
.add(&mut rng, &clock, &provider, "a-subject".to_owned(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ pub trait UpstreamOAuthLinkRepository: Send + Sync {
|
||||
/// * `upsream_oauth_provider`: The upstream OAuth provider for which to
|
||||
/// create the link
|
||||
/// * `subject`: The subject of the upstream OAuth link to create
|
||||
/// * `human_account_name`: A human-readable name for the upstream account
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -138,6 +139,7 @@ pub trait UpstreamOAuthLinkRepository: Send + Sync {
|
||||
clock: &dyn Clock,
|
||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||
subject: String,
|
||||
human_account_name: Option<String>,
|
||||
) -> Result<UpstreamOAuthLink, Self::Error>;
|
||||
|
||||
/// Associate an upstream OAuth link to a user
|
||||
@@ -201,6 +203,7 @@ repository_impl!(UpstreamOAuthLinkRepository:
|
||||
clock: &dyn Clock,
|
||||
upstream_oauth_provider: &UpstreamOAuthProvider,
|
||||
subject: String,
|
||||
human_account_name: Option<String>,
|
||||
) -> Result<UpstreamOAuthLink, Self::Error>;
|
||||
|
||||
async fn associate_to_user(
|
||||
|
||||
@@ -20,12 +20,14 @@ use chrono::{DateTime, Duration, Utc};
|
||||
use http::{Method, Uri, Version};
|
||||
use mas_data_model::{
|
||||
AuthorizationGrant, BrowserSession, Client, CompatSsoLogin, CompatSsoLoginState,
|
||||
DeviceCodeGrant, UpstreamOAuthLink, UpstreamOAuthProvider, User, UserAgent, UserEmail,
|
||||
UserEmailVerification, UserRecoverySession,
|
||||
DeviceCodeGrant, UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
|
||||
UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderPkceMode,
|
||||
UpstreamOAuthProviderResponseMode, UpstreamOAuthProviderTokenAuthMethod, User, UserAgent,
|
||||
UserEmail, UserEmailVerification, UserRecoverySession,
|
||||
};
|
||||
use mas_i18n::DataLocale;
|
||||
use mas_router::{Account, GraphQL, PostAuthAction, UrlBuilder};
|
||||
use oauth2_types::scope::OPENID;
|
||||
use oauth2_types::scope::{Scope, OPENID};
|
||||
use rand::{
|
||||
distributions::{Alphanumeric, DistString},
|
||||
Rng,
|
||||
@@ -1277,8 +1279,10 @@ impl FormField for UpstreamRegisterFormField {
|
||||
|
||||
/// Context used by the `pages/upstream_oauth2/do_register.html`
|
||||
/// templates
|
||||
#[derive(Serialize, Default)]
|
||||
#[derive(Serialize)]
|
||||
pub struct UpstreamRegister {
|
||||
upstream_oauth_link: UpstreamOAuthLink,
|
||||
upstream_oauth_provider: UpstreamOAuthProvider,
|
||||
imported_localpart: Option<String>,
|
||||
force_localpart: bool,
|
||||
imported_display_name: Option<String>,
|
||||
@@ -1289,10 +1293,24 @@ pub struct UpstreamRegister {
|
||||
}
|
||||
|
||||
impl UpstreamRegister {
|
||||
/// Constructs a new context with an existing linked user
|
||||
/// Constructs a new context for registering a new user from an upstream
|
||||
/// provider
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub fn new(
|
||||
upstream_oauth_link: UpstreamOAuthLink,
|
||||
upstream_oauth_provider: UpstreamOAuthProvider,
|
||||
) -> Self {
|
||||
Self {
|
||||
upstream_oauth_link,
|
||||
upstream_oauth_provider,
|
||||
imported_localpart: None,
|
||||
force_localpart: false,
|
||||
imported_display_name: None,
|
||||
force_display_name: false,
|
||||
imported_email: None,
|
||||
force_email: false,
|
||||
form_state: FormState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the imported localpart
|
||||
@@ -1356,11 +1374,43 @@ impl UpstreamRegister {
|
||||
}
|
||||
|
||||
impl TemplateContext for UpstreamRegister {
|
||||
fn sample(_now: chrono::DateTime<Utc>, _rng: &mut impl Rng) -> Vec<Self>
|
||||
fn sample(now: chrono::DateTime<Utc>, _rng: &mut impl Rng) -> Vec<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
vec![Self::new()]
|
||||
vec![Self::new(
|
||||
UpstreamOAuthLink {
|
||||
id: Ulid::nil(),
|
||||
provider_id: Ulid::nil(),
|
||||
user_id: None,
|
||||
subject: "subject".to_owned(),
|
||||
human_account_name: Some("@john".to_owned()),
|
||||
created_at: now,
|
||||
},
|
||||
UpstreamOAuthProvider {
|
||||
id: Ulid::nil(),
|
||||
issuer: "https://example.com/".to_owned(),
|
||||
human_name: Some("Example Ltd.".to_owned()),
|
||||
brand_name: None,
|
||||
scope: Scope::from_iter([OPENID]),
|
||||
token_endpoint_auth_method: UpstreamOAuthProviderTokenAuthMethod::ClientSecretBasic,
|
||||
token_endpoint_signing_alg: None,
|
||||
client_id: "client-id".to_owned(),
|
||||
encrypted_client_secret: None,
|
||||
claims_imports: UpstreamOAuthProviderClaimsImports::default(),
|
||||
authorization_endpoint_override: None,
|
||||
token_endpoint_override: None,
|
||||
jwks_uri_override: None,
|
||||
userinfo_endpoint_override: None,
|
||||
fetch_userinfo: false,
|
||||
discovery_mode: UpstreamOAuthProviderDiscoveryMode::Oidc,
|
||||
pkce_mode: UpstreamOAuthProviderPkceMode::Auto,
|
||||
response_mode: UpstreamOAuthProviderResponseMode::Query,
|
||||
additional_authorization_parameters: Vec::new(),
|
||||
created_at: now,
|
||||
disabled_at: None,
|
||||
},
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2120,6 +2120,14 @@
|
||||
"$ref": "#/definitions/EmailImportPreference"
|
||||
}
|
||||
]
|
||||
},
|
||||
"account_name": {
|
||||
"description": "Set a human-readable name for the upstream account for display purposes",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AccountNameImportPreference"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2254,6 +2262,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AccountNameImportPreference": {
|
||||
"description": "What should be done for the account name attribute",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template": {
|
||||
"description": "The Jinja2 template to use for the account name. This name is only used for display purposes.\n\nIf not provided, it will be ignored.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BrandingConfig": {
|
||||
"description": "Configuration section for tweaking the branding of the service",
|
||||
"type": "object",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
}
|
||||
|
||||
& svg {
|
||||
height: var(--cpd-space-10x);
|
||||
width: var(--cpd-space-10x);
|
||||
height: var(--cpd-space-8x);
|
||||
width: var(--cpd-space-8x);
|
||||
color: var(--cpd-color-icon-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,3 +139,39 @@
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.upstream-oauth2-provider-account {
|
||||
display: flex;
|
||||
gap: var(--cpd-space-4x);
|
||||
background-color: var(--cpd-color-bg-text-on-solid-primary);
|
||||
border: solid 1px var(--cpd-color-gray-400);
|
||||
border-radius: var(--cpd-space-3x);
|
||||
padding: var(--cpd-space-4x);
|
||||
align-items: center;
|
||||
|
||||
& > svg {
|
||||
height: var(--cpd-space-6x);
|
||||
width: var(--cpd-space-6x);
|
||||
|
||||
&:not(.brand) {
|
||||
color: var(--cpd-color-icon-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
& .infos {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& .provider {
|
||||
font: var(--cpd-font-body-md-semibold);
|
||||
letter-spacing: var(--cpd-font-letter-spacing-body-md);
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
|
||||
& .account {
|
||||
font: var(--cpd-font-body-md-regular);
|
||||
letter-spacing: var(--cpd-font-letter-spacing-body-md);
|
||||
color: var(--cpd-color-text-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ Please see LICENSE in the repository root for full details.
|
||||
<path d="M9.04155 21C6.6153 21 4.35363 20.2943 2.45 19.0767C4.06624 19.1813 6.91855 18.9308 8.69268 17.2386C6.0238 17.1161 4.82019 15.0692 4.6632 14.1945C4.88997 14.2819 5.97147 14.3869 6.582 14.142C3.51192 13.3722 3.04094 10.678 3.1456 9.85573C3.72124 10.2581 4.69809 10.3981 5.08185 10.3631C2.22109 8.31618 3.25027 5.23707 3.75613 4.57226C5.80911 7.4165 8.8859 9.01393 12.6923 9.10278C12.6205 8.78802 12.5826 8.46032 12.5826 8.12373C12.5826 5.70819 14.5351 3.75 16.9435 3.75C18.2019 3.75 19.3358 4.28457 20.1318 5.13963C20.9727 4.94258 22.2382 4.4813 22.8569 4.0824C22.5451 5.20208 21.5742 6.13612 20.9869 6.48231C20.9918 6.49408 20.9821 6.47048 20.9869 6.48231C21.5028 6.40428 22.8986 6.13603 23.45 5.76192C23.1773 6.39094 22.148 7.4368 21.3033 8.02232C21.4604 14.9535 16.1574 21 9.04155 21Z" fill="#1D9BF0"/>
|
||||
</svg>
|
||||
{% elif brand == "github" %}
|
||||
<svg class="{{ class }}" xmlns="http://www.w3.org/2000/svg" width="25" height="24" viewBox="0 0 25 24" fill="none">
|
||||
<path d="M20.8421 7.10595C19.9703 5.6121 18.7876 4.42942 17.2939 3.55764C15.8 2.68581 14.169 2.25001 12.3999 2.25001C10.6311 2.25001 8.9996 2.68594 7.50597 3.55764C6.01212 4.42938 4.82953 5.6121 3.95765 7.10595C3.08592 8.59976 2.65002 10.231 2.65002 11.9997C2.65002 14.1242 3.26987 16.0346 4.50987 17.7315C5.74973 19.4284 7.35145 20.6027 9.3149 21.2543C9.54345 21.2967 9.71264 21.2669 9.82265 21.1656C9.9327 21.0641 9.98766 20.937 9.98766 20.7848C9.98766 20.7595 9.98548 20.531 9.98126 20.0993C9.9769 19.6676 9.97485 19.291 9.97485 18.9696L9.68285 19.0202C9.49667 19.0543 9.26181 19.0687 8.97826 19.0646C8.69484 19.0607 8.40061 19.031 8.09598 18.9757C7.79121 18.921 7.50775 18.7941 7.24536 18.5952C6.98311 18.3963 6.79693 18.1359 6.68688 17.8145L6.55993 17.5224C6.47531 17.3279 6.3421 17.1119 6.1601 16.875C5.97811 16.638 5.79406 16.4773 5.60789 16.3927L5.519 16.329C5.45978 16.2868 5.40482 16.2358 5.35399 16.1766C5.30321 16.1174 5.2652 16.0582 5.23981 15.9988C5.21437 15.9395 5.23545 15.8908 5.30326 15.8526C5.37107 15.8144 5.49361 15.7959 5.67143 15.7959L5.92524 15.8338C6.09451 15.8677 6.3039 15.9691 6.55366 16.1384C6.80329 16.3077 7.0085 16.5277 7.16933 16.7984C7.36408 17.1455 7.59873 17.4099 7.87392 17.5919C8.14889 17.7739 8.42613 17.8648 8.70537 17.8648C8.98461 17.8648 9.22579 17.8436 9.429 17.8015C9.63198 17.7592 9.82243 17.6955 10.0002 17.611C10.0764 17.0437 10.2838 16.6079 10.6222 16.3033C10.1399 16.2526 9.70619 16.1762 9.32099 16.0747C8.93601 15.9731 8.53818 15.8081 8.12777 15.5794C7.71714 15.3509 7.37649 15.0673 7.10574 14.7289C6.83495 14.3904 6.61271 13.9459 6.43934 13.3959C6.26588 12.8457 6.17913 12.211 6.17913 11.4916C6.17913 10.4674 6.51351 9.59578 7.18213 8.87633C6.86892 8.10629 6.89849 7.24304 7.27093 6.28668C7.51638 6.21043 7.88037 6.26765 8.36273 6.45801C8.84517 6.64845 9.1984 6.8116 9.42277 6.94686C9.64714 7.08208 9.82692 7.19666 9.96236 7.2896C10.7496 7.06963 11.562 6.95962 12.3998 6.95962C13.2377 6.95962 14.0503 7.06963 14.8376 7.2896L15.32 6.98505C15.6498 6.78185 16.0394 6.59563 16.4877 6.42635C16.9363 6.25716 17.2793 6.21056 17.5164 6.28682C17.8971 7.24322 17.931 8.10642 17.6177 8.87647C18.2863 9.59591 18.6208 10.4677 18.6208 11.4918C18.6208 12.2111 18.5337 12.8478 18.3605 13.4023C18.1871 13.9568 17.963 14.4008 17.688 14.7353C17.4127 15.0697 17.0699 15.3511 16.6595 15.5795C16.249 15.808 15.851 15.973 15.466 16.0747C15.0809 16.1763 14.6472 16.2527 14.1648 16.3035C14.6048 16.6842 14.8248 17.2851 14.8248 18.106V20.7845C14.8248 20.9367 14.8777 21.0637 14.9836 21.1652C15.0894 21.2665 15.2565 21.2964 15.485 21.2539C17.4487 20.6024 19.0505 19.4281 20.2903 17.7311C21.53 16.0343 22.15 14.1238 22.15 11.9993C22.1496 10.2309 21.7135 8.59976 20.8421 7.10595Z" fill="currentColor"/>
|
||||
<svg class="{{ class }}" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9642 0C5.34833 0 0 5.38776 0 12.0531C0 17.3811 3.42686 21.8912 8.18082 23.4874C8.77518 23.6074 8.9929 23.2281 8.9929 22.909C8.9929 22.6296 8.97331 21.6718 8.97331 20.6738C5.64514 21.3923 4.95208 19.237 4.95208 19.237C4.41722 17.8401 3.62473 17.4811 3.62473 17.4811C2.53543 16.7427 3.70408 16.7427 3.70408 16.7427C4.91241 16.8225 5.54645 17.9799 5.54645 17.9799C6.61592 19.8157 8.33926 19.297 9.03257 18.9776C9.13151 18.1993 9.44865 17.6606 9.78539 17.3613C7.13094 17.0819 4.33812 16.0442 4.33812 11.4144C4.33812 10.0974 4.81322 9.01984 5.56604 8.1818C5.44727 7.88253 5.03118 6.64506 5.68506 4.98882C5.68506 4.98882 6.69527 4.66947 8.97306 6.22604C9.94827 5.9622 10.954 5.82799 11.9642 5.82686C12.9744 5.82686 14.0042 5.96669 14.9552 6.22604C17.2332 4.66947 18.2434 4.98882 18.2434 4.98882C18.8973 6.64506 18.481 7.88253 18.3622 8.1818C19.1349 9.01984 19.5904 10.0974 19.5904 11.4144C19.5904 16.0442 16.7976 17.0618 14.1233 17.3613C14.5592 17.7404 14.9353 18.4587 14.9353 19.5962C14.9353 21.2126 14.9158 22.5098 14.9158 22.9087C14.9158 23.2281 15.1337 23.6074 15.7278 23.4877C20.4818 21.8909 23.9087 17.3811 23.9087 12.0531C23.9282 5.38776 18.5603 0 11.9642 0Z" fill="currentColor"/>
|
||||
</svg>
|
||||
{% elif brand == "facebook" %}
|
||||
<svg class="{{ class }}" xmlns="http://www.w3.org/2000/svg" width="25" height="24" viewBox="0 0 25 24" fill="none">
|
||||
|
||||
@@ -8,6 +8,8 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% from "components/idp_brand.html" import logo %}
|
||||
|
||||
{% block content %}
|
||||
{% if force_localpart %}
|
||||
<header class="page-heading">
|
||||
@@ -24,6 +26,18 @@ Please see LICENSE in the repository root for full details.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
{% elif upstream_oauth_provider.human_name %}
|
||||
<header class="page-heading">
|
||||
<div class="icon">
|
||||
{{ icon.user_profile_solid() }}
|
||||
</div>
|
||||
|
||||
<div class="header">
|
||||
<h1 class="title">
|
||||
{{ _("mas.upstream_oauth2.register.signup_with_upstream.heading", human_name=upstream_oauth_provider.human_name) }}
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
{% else %}
|
||||
<header class="page-heading">
|
||||
<div class="icon">
|
||||
@@ -41,6 +55,27 @@ Please see LICENSE in the repository root for full details.
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
{% if upstream_oauth_provider.human_name %}
|
||||
<section class="upstream-oauth2-provider-account">
|
||||
{% if upstream_oauth_provider.brand_name %}
|
||||
{{ logo(brand=upstream_oauth_provider.brand_name, class="brand") }}
|
||||
{% else %}
|
||||
{{ icon.user_profile() }}
|
||||
{% endif %}
|
||||
|
||||
<div class="infos">
|
||||
<h3 class="provider">
|
||||
{{- _("mas.upstream_oauth2.register.provider_name", human_name=upstream_oauth_provider.human_name) -}}
|
||||
</h3>
|
||||
{% if upstream_oauth_link.human_account_name %}
|
||||
<p class="account">
|
||||
{{- upstream_oauth_link.human_account_name -}}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" class="cpd-form-root">
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
<input type="hidden" name="action" value="register" />
|
||||
@@ -80,7 +115,11 @@ Please see LICENSE in the repository root for full details.
|
||||
<input {{ field.attributes(f) }} class="cpd-text-control" type="email" value="{{ imported_email }}" readonly aria-describedby="{{ f.id }}-help" />
|
||||
|
||||
<div class="cpd-form-message cpd-form-help-message" id="{{ f.id }}-help">
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream") -}}
|
||||
{% if upstream_oauth_provider.human_name %}
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream_with_name", human_name=upstream_oauth_provider.human_name) -}}
|
||||
{% else %}
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream") -}}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
@@ -108,7 +147,11 @@ Please see LICENSE in the repository root for full details.
|
||||
<input {{ field.attributes(f) }} class="cpd-text-control" type="text" value="{{ imported_display_name }}" readonly />
|
||||
|
||||
<div class="cpd-form-message cpd-form-help-message">
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream") -}}
|
||||
{% if upstream_oauth_provider.human_name %}
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream_with_name", human_name=upstream_oauth_provider.human_name) -}}
|
||||
{% else %}
|
||||
{{- _("mas.upstream_oauth2.register.imported_from_upstream") -}}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"create_account": "Create Account",
|
||||
"@create_account": {
|
||||
"context": "pages/login.html:68:35-61, pages/upstream_oauth2/do_register.html:149:26-52"
|
||||
"context": "pages/login.html:68:35-61, pages/upstream_oauth2/do_register.html:192:26-52"
|
||||
},
|
||||
"sign_in": "Sign in",
|
||||
"@sign_in": {
|
||||
@@ -71,11 +71,11 @@
|
||||
"common": {
|
||||
"display_name": "Display Name",
|
||||
"@display_name": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:107:37-61"
|
||||
"context": "pages/upstream_oauth2/do_register.html:146:37-61"
|
||||
},
|
||||
"email_address": "Email address",
|
||||
"@email_address": {
|
||||
"context": "pages/account/emails/add.html:33:33-58, pages/recovery/start.html:34:33-58, pages/register.html:40:35-60, pages/upstream_oauth2/do_register.html:79:37-62"
|
||||
"context": "pages/account/emails/add.html:33:33-58, pages/recovery/start.html:34:33-58, pages/register.html:40:35-60, pages/upstream_oauth2/do_register.html:114:37-62"
|
||||
},
|
||||
"loading": "Loading…",
|
||||
"@loading": {
|
||||
@@ -83,7 +83,7 @@
|
||||
},
|
||||
"mxid": "Matrix ID",
|
||||
"@mxid": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:58:35-51"
|
||||
"context": "pages/upstream_oauth2/do_register.html:93:35-51"
|
||||
},
|
||||
"password": "Password",
|
||||
"@password": {
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
"username": "Username",
|
||||
"@username": {
|
||||
"context": "pages/login.html:46:37-57, pages/register.html:36:35-55, pages/upstream_oauth2/do_register.html:66:35-55, pages/upstream_oauth2/do_register.html:71:39-59"
|
||||
"context": "pages/login.html:46:37-57, pages/register.html:36:35-55, pages/upstream_oauth2/do_register.html:101:35-55, pages/upstream_oauth2/do_register.html:106:39-59"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -519,7 +519,7 @@
|
||||
},
|
||||
"terms_of_service": "I agree to the <a href=\"%s\" data-kind=\"primary\" class=\"cpd-link\">Terms and Conditions</a>",
|
||||
"@terms_of_service": {
|
||||
"context": "pages/register.html:53:37-97, pages/upstream_oauth2/do_register.html:136:35-95"
|
||||
"context": "pages/register.html:53:37-97, pages/upstream_oauth2/do_register.html:179:35-95"
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
@@ -570,11 +570,11 @@
|
||||
"choose_username": {
|
||||
"description": "This cannot be changed later.",
|
||||
"@description": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:38:13-74"
|
||||
"context": "pages/upstream_oauth2/do_register.html:52:13-74"
|
||||
},
|
||||
"heading": "Choose your username",
|
||||
"@heading": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:35:13-70",
|
||||
"context": "pages/upstream_oauth2/do_register.html:49:13-70",
|
||||
"description": "Displayed when creating a new account from an SSO login, and the username is not forced"
|
||||
}
|
||||
},
|
||||
@@ -584,7 +584,7 @@
|
||||
},
|
||||
"enforced_by_policy": "Enforced by server policy",
|
||||
"@enforced_by_policy": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:62:14-66"
|
||||
"context": "pages/upstream_oauth2/do_register.html:97:14-66"
|
||||
},
|
||||
"forced_display_name": "Will use the following display name",
|
||||
"@forced_display_name": {
|
||||
@@ -601,21 +601,35 @@
|
||||
"import_data": {
|
||||
"description": "Confirm the information that will be linked to your new %(server_name)s account.",
|
||||
"@description": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:23:13-104"
|
||||
"context": "pages/upstream_oauth2/do_register.html:25:13-104"
|
||||
},
|
||||
"heading": "Import your data",
|
||||
"@heading": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:20:13-66"
|
||||
"context": "pages/upstream_oauth2/do_register.html:22:13-66"
|
||||
}
|
||||
},
|
||||
"imported_from_upstream": "Imported from your upstream account",
|
||||
"@imported_from_upstream": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:111:16-72, pages/upstream_oauth2/do_register.html:83:16-72"
|
||||
"context": "pages/upstream_oauth2/do_register.html:121:18-74, pages/upstream_oauth2/do_register.html:153:18-74"
|
||||
},
|
||||
"imported_from_upstream_with_name": "Imported from your %(human_name)s account",
|
||||
"@imported_from_upstream_with_name": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:119:18-131, pages/upstream_oauth2/do_register.html:151:18-131"
|
||||
},
|
||||
"link_existing": "Link to an existing account",
|
||||
"@link_existing": {
|
||||
"description": "Button to link an existing account after an SSO login"
|
||||
},
|
||||
"provider_name": "%(human_name)s account",
|
||||
"@provider_name": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:68:14-108"
|
||||
},
|
||||
"signup_with_upstream": {
|
||||
"heading": "Continue signing up with your %(human_name)s account",
|
||||
"@heading": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:37:13-122"
|
||||
}
|
||||
},
|
||||
"suggested_display_name": "Import display name",
|
||||
"@suggested_display_name": {
|
||||
"description": "Option to let the user import their display name after an SSO login"
|
||||
@@ -626,7 +640,7 @@
|
||||
},
|
||||
"use": "Use",
|
||||
"@use": {
|
||||
"context": "pages/upstream_oauth2/do_register.html:127:20-57, pages/upstream_oauth2/do_register.html:98:18-55"
|
||||
"context": "pages/upstream_oauth2/do_register.html:137:18-55, pages/upstream_oauth2/do_register.html:170:20-57"
|
||||
}
|
||||
},
|
||||
"suggest_link": {
|
||||
|
||||
Reference in New Issue
Block a user