mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-10-23 00:03:24 +00:00
Expose the invite URL and the pinned username and email in the admin API
Registration tokens now report the `/register?token=…` deep link alongside the `username` and `email` they were issued for, and the create endpoint accepts both. All the token endpoints now take the `UrlBuilder` so they can build that link, and `Register` carries the token in its query string.
This commit is contained in:
11 files changed
+383
-82
No files matched your search
@@ -15,6 +15,7 @@ use mas_data_model::{
|
||||
session::{PersonalSession as DataModelPersonalSession, PersonalSessionOwner},
|
||||
},
|
||||
};
|
||||
use mas_router::UrlBuilder;
|
||||
use oauth2_types::requests::GrantType;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
@@ -631,6 +632,14 @@ pub struct UserRegistrationToken {
|
||||
/// Whether the token is valid
|
||||
valid: bool,
|
||||
|
||||
/// A username imposed on the registering user. If null, the user can
|
||||
/// choose their own username.
|
||||
username: Option<String>,
|
||||
|
||||
/// An email address imposed on the registering user. If null, the user
|
||||
/// can choose their own email.
|
||||
email: Option<String>,
|
||||
|
||||
/// Maximum number of times this token can be used
|
||||
usage_limit: Option<u32>,
|
||||
|
||||
@@ -648,20 +657,32 @@ pub struct UserRegistrationToken {
|
||||
|
||||
/// When the token was revoked. If null, the token is not revoked.
|
||||
revoked_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// URL to share with the user to register with this token
|
||||
invite_url: Url,
|
||||
}
|
||||
|
||||
impl UserRegistrationToken {
|
||||
pub fn new(token: mas_data_model::UserRegistrationToken, now: DateTime<Utc>) -> Self {
|
||||
pub fn new(
|
||||
token: mas_data_model::UserRegistrationToken,
|
||||
now: DateTime<Utc>,
|
||||
url_builder: &UrlBuilder,
|
||||
) -> Self {
|
||||
let invite_url = url_builder
|
||||
.absolute_url_for(&mas_router::Register::default().with_token(token.token.clone()));
|
||||
Self {
|
||||
id: token.id,
|
||||
valid: token.is_valid(now),
|
||||
token: token.token,
|
||||
username: token.username,
|
||||
email: token.email,
|
||||
usage_limit: token.usage_limit,
|
||||
times_used: token.times_used,
|
||||
created_at: token.created_at,
|
||||
last_used_at: token.last_used_at,
|
||||
expires_at: token.expires_at,
|
||||
revoked_at: token.revoked_at,
|
||||
invite_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,23 +704,29 @@ impl UserRegistrationToken {
|
||||
id: Ulid::from_bytes([0x01; 16]),
|
||||
token: "abc123def456".to_owned(),
|
||||
valid: true,
|
||||
username: None,
|
||||
email: None,
|
||||
usage_limit: Some(10),
|
||||
times_used: 5,
|
||||
created_at: DateTime::default(),
|
||||
last_used_at: Some(DateTime::default()),
|
||||
expires_at: Some(DateTime::default() + chrono::Duration::days(30)),
|
||||
revoked_at: None,
|
||||
invite_url: Url::parse("https://example.com/register?token=abc123def456").unwrap(),
|
||||
},
|
||||
Self {
|
||||
id: Ulid::from_bytes([0x02; 16]),
|
||||
token: "xyz789abc012".to_owned(),
|
||||
valid: false,
|
||||
username: None,
|
||||
email: None,
|
||||
usage_limit: None,
|
||||
times_used: 0,
|
||||
created_at: DateTime::default(),
|
||||
last_used_at: None,
|
||||
expires_at: None,
|
||||
revoked_at: Some(DateTime::default()),
|
||||
invite_url: Url::parse("https://example.com/register?token=xyz789abc012").unwrap(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use axum::extract::{FromRef, FromRequestParts};
|
||||
use mas_data_model::{AppVersion, BoxRng, SiteConfig};
|
||||
use mas_matrix::HomeserverConnection;
|
||||
use mas_policy::PolicyFactory;
|
||||
use mas_router::UrlBuilder;
|
||||
|
||||
use super::call_context::CallContext;
|
||||
use crate::passwords::PasswordManager;
|
||||
@@ -41,6 +42,7 @@ where
|
||||
SiteConfig: FromRef<S>,
|
||||
AppVersion: FromRef<S>,
|
||||
Arc<PolicyFactory>: FromRef<S>,
|
||||
UrlBuilder: FromRef<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
CallContext: FromRequestParts<S>,
|
||||
{
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{NoApi, OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_data_model::BoxRng;
|
||||
use mas_router::UrlBuilder;
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -89,6 +90,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
NoApi(mut rng): NoApi<BoxRng>,
|
||||
Json(params): Json<Request>,
|
||||
) -> Result<(StatusCode, Json<SingleResponse<UserRegistrationToken>>), RouteError> {
|
||||
@@ -123,6 +125,7 @@ pub async fn handler(
|
||||
Json(SingleResponse::new_canonical(UserRegistrationToken::new(
|
||||
registration_token,
|
||||
clock.now(),
|
||||
&url_builder,
|
||||
))),
|
||||
))
|
||||
}
|
||||
@@ -159,12 +162,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_token_123",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_token_123"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -177,6 +183,27 @@ mod tests {
|
||||
"#);
|
||||
}
|
||||
|
||||
/// The token travels in the query string of the invite URL, so one with
|
||||
/// reserved characters comes back percent-encoded
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_create_invite_url_encoding(pool: PgPool) {
|
||||
setup();
|
||||
let mut state = TestState::from_pool(pool).await.unwrap();
|
||||
let token = state.token_with_scope("urn:mas:admin").await;
|
||||
|
||||
let request = Request::post("/api/admin/v1/user-registration-tokens")
|
||||
.bearer(&token)
|
||||
.json(serde_json::json!({ "token": "a token&more" }));
|
||||
let response = state.request(request).await;
|
||||
response.assert_status(StatusCode::CREATED);
|
||||
let body: serde_json::Value = response.json();
|
||||
|
||||
assert_eq!(
|
||||
body["data"]["attributes"]["invite_url"],
|
||||
"https://example.com/register?token=a+token%26more"
|
||||
);
|
||||
}
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_create_auto_token(pool: PgPool) {
|
||||
setup();
|
||||
@@ -201,12 +228,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "42oTpLoieH5I",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 1,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=42oTpLoieH5I"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0QMGC989M0XSFVF2X"
|
||||
@@ -244,12 +274,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_token_123",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_token_123"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
// Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -5,9 +6,10 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_router::UrlBuilder;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{
|
||||
@@ -67,6 +69,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
id: UlidPathParam,
|
||||
) -> Result<Json<SingleResponse<UserRegistrationToken>>, RouteError> {
|
||||
let token = repo
|
||||
@@ -76,7 +79,7 @@ pub async fn handler(
|
||||
.ok_or(RouteError::NotFound(*id))?;
|
||||
|
||||
Ok(Json(SingleResponse::new_canonical(
|
||||
UserRegistrationToken::new(token, clock.now()),
|
||||
UserRegistrationToken::new(token, clock.now(), &url_builder),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -129,12 +132,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_token_123",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_token_123"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
// Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -5,11 +6,12 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use axum_extra::extract::{Query, QueryRejection};
|
||||
use axum_macros::FromRequestParts;
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::{Page, user::UserRegistrationTokenFilter};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -135,6 +137,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UserRegistrationToken>>, RouteError> {
|
||||
@@ -165,7 +168,7 @@ pub async fn handler(
|
||||
.user_registration_token()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(|token| UserRegistrationToken::new(token, now));
|
||||
.map(|token| UserRegistrationToken::new(token, now, &url_builder));
|
||||
let count = repo.user_registration_token().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
@@ -174,7 +177,7 @@ pub async fn handler(
|
||||
.user_registration_token()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(|token| UserRegistrationToken::new(token, now));
|
||||
.map(|token| UserRegistrationToken::new(token, now, &url_builder));
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
@@ -318,12 +321,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -340,12 +346,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -362,12 +371,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -384,12 +396,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -406,12 +421,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -459,12 +477,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -481,12 +502,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -526,12 +550,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -548,12 +575,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -570,12 +600,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -623,12 +656,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -645,12 +681,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -690,12 +729,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -712,12 +754,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -734,12 +779,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -787,12 +835,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -832,12 +883,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -854,12 +908,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -876,12 +933,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -898,12 +958,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -951,12 +1014,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -973,12 +1039,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -1018,12 +1087,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -1040,12 +1112,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -1062,12 +1137,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -1117,12 +1195,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -1170,12 +1251,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -1192,12 +1276,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -1238,12 +1325,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -1260,12 +1350,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -1306,12 +1399,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -1378,12 +1474,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_expired",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-01-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_expired"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
|
||||
@@ -1400,12 +1499,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -1422,12 +1524,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
|
||||
@@ -1444,12 +1549,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -1466,12 +1574,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used_revoked",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": "2022-01-16T14:40:00Z"
|
||||
"revoked_at": "2022-01-16T14:40:00Z",
|
||||
"invite_url": "https://example.com/register?token=token_used_revoked"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
|
||||
@@ -1526,12 +1637,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_used",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 1,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": "2022-01-16T14:40:00Z",
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_used"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
|
||||
@@ -1548,12 +1662,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "token_unused",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=token_unused"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
// Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -5,9 +6,10 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_router::UrlBuilder;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{
|
||||
@@ -76,6 +78,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
id: UlidPathParam,
|
||||
) -> Result<Json<SingleResponse<UserRegistrationToken>>, RouteError> {
|
||||
let id = *id;
|
||||
@@ -96,7 +99,7 @@ pub async fn handler(
|
||||
repo.save().await?;
|
||||
|
||||
Ok(Json(SingleResponse::new(
|
||||
UserRegistrationToken::new(token, clock.now()),
|
||||
UserRegistrationToken::new(token, clock.now(), &url_builder),
|
||||
format!("/api/admin/v1/user-registration-tokens/{id}/revoke"),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
// Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -5,9 +6,10 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_router::UrlBuilder;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{
|
||||
@@ -76,6 +78,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
id: UlidPathParam,
|
||||
) -> Result<Json<SingleResponse<UserRegistrationToken>>, RouteError> {
|
||||
let id = *id;
|
||||
@@ -96,7 +99,7 @@ pub async fn handler(
|
||||
repo.save().await?;
|
||||
|
||||
Ok(Json(SingleResponse::new(
|
||||
UserRegistrationToken::new(token, clock.now()),
|
||||
UserRegistrationToken::new(token, clock.now(), &url_builder),
|
||||
format!("/api/admin/v1/user-registration-tokens/{id}/unrevoke"),
|
||||
)))
|
||||
}
|
||||
@@ -160,12 +163,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_token_456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_token_456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
// Copyright 2025 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -5,10 +6,11 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
use aide::{OperationIo, transform::TransformOperation};
|
||||
use axum::{Json, response::IntoResponse};
|
||||
use axum::{Json, extract::State, response::IntoResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::record_error;
|
||||
use mas_router::UrlBuilder;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use ulid::Ulid;
|
||||
@@ -103,6 +105,7 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
id: UlidPathParam,
|
||||
Json(request): Json<Request>,
|
||||
) -> Result<Json<SingleResponse<UserRegistrationToken>>, RouteError> {
|
||||
@@ -134,7 +137,7 @@ pub async fn handler(
|
||||
repo.save().await?;
|
||||
|
||||
Ok(Json(SingleResponse::new(
|
||||
UserRegistrationToken::new(token, clock.now()),
|
||||
UserRegistrationToken::new(token, clock.now(), &url_builder),
|
||||
format!("/api/admin/v1/user-registration-tokens/{id}"),
|
||||
)))
|
||||
}
|
||||
@@ -198,12 +201,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_expiry",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": null,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-02-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_expiry"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -238,12 +244,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_expiry",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": null,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_expiry"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -304,12 +313,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_limit",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_limit"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -344,12 +356,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_limit",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": null,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_limit"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -412,12 +427,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_multiple",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 20,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-02-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_multiple"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
@@ -476,12 +494,15 @@ mod tests {
|
||||
"attributes": {
|
||||
"token": "test_update_none",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 5,
|
||||
"times_used": 0,
|
||||
"created_at": "2022-01-16T14:40:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": "2022-02-15T14:40:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=test_update_none"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
|
||||
|
||||
@@ -274,6 +274,17 @@ impl SimpleRoute for Logout {
|
||||
/// `GET|POST /register`
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Register {
|
||||
query: RegisterQuery,
|
||||
}
|
||||
|
||||
/// The query parameters of the registration page
|
||||
#[derive(Default, Debug, Clone, Serialize)]
|
||||
pub struct RegisterQuery {
|
||||
/// The registration token an invite link deep-links with
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
token: Option<String>,
|
||||
|
||||
#[serde(flatten)]
|
||||
post_auth_action: Option<PostAuthAction>,
|
||||
}
|
||||
|
||||
@@ -281,32 +292,38 @@ impl Register {
|
||||
#[must_use]
|
||||
pub fn and_then(action: PostAuthAction) -> Self {
|
||||
Self {
|
||||
post_auth_action: Some(action),
|
||||
query: RegisterQuery {
|
||||
token: None,
|
||||
post_auth_action: Some(action),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn and_continue_grant(data: Ulid) -> Self {
|
||||
Self {
|
||||
post_auth_action: Some(PostAuthAction::continue_grant(data)),
|
||||
}
|
||||
Self::and_then(PostAuthAction::continue_grant(data))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn and_continue_compat_sso_login(data: Ulid) -> Self {
|
||||
Self {
|
||||
post_auth_action: Some(PostAuthAction::continue_compat_sso_login(data)),
|
||||
}
|
||||
Self::and_then(PostAuthAction::continue_compat_sso_login(data))
|
||||
}
|
||||
|
||||
/// Deep-link to the registration page with a registration token
|
||||
#[must_use]
|
||||
pub fn with_token(mut self, token: String) -> Self {
|
||||
self.query.token = Some(token);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get a reference to the reauth's post auth action.
|
||||
#[must_use]
|
||||
pub fn post_auth_action(&self) -> Option<&PostAuthAction> {
|
||||
self.post_auth_action.as_ref()
|
||||
self.query.post_auth_action.as_ref()
|
||||
}
|
||||
|
||||
pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
|
||||
match &self.post_auth_action {
|
||||
match &self.query.post_auth_action {
|
||||
Some(action) => action.go_next(url_builder),
|
||||
None => url_builder.redirect(&Index),
|
||||
}
|
||||
@@ -314,20 +331,25 @@ impl Register {
|
||||
}
|
||||
|
||||
impl Route for Register {
|
||||
type Query = PostAuthAction;
|
||||
type Query = RegisterQuery;
|
||||
|
||||
fn route() -> &'static str {
|
||||
"/register"
|
||||
}
|
||||
|
||||
fn query(&self) -> Option<&Self::Query> {
|
||||
self.post_auth_action.as_ref()
|
||||
Some(&self.query)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<PostAuthAction>> for Register {
|
||||
fn from(post_auth_action: Option<PostAuthAction>) -> Self {
|
||||
Self { post_auth_action }
|
||||
Self {
|
||||
query: RegisterQuery {
|
||||
token: None,
|
||||
post_auth_action,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Copyright 2025, 2026 Element Creations Ltd.
|
||||
// Copyright 2024, 2025 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
@@ -32,6 +33,28 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_urls() {
|
||||
assert_eq!(
|
||||
Register::default().path_and_query(),
|
||||
Cow::Borrowed("/register")
|
||||
);
|
||||
assert_eq!(
|
||||
Register::default()
|
||||
.with_token("a token&more".to_owned())
|
||||
.path_and_query(),
|
||||
Cow::Borrowed("/register?token=a+token%26more")
|
||||
);
|
||||
assert_eq!(
|
||||
Register::and_continue_grant(Ulid::nil())
|
||||
.with_token("invite".to_owned())
|
||||
.path_and_query(),
|
||||
Cow::Borrowed(
|
||||
"/register?token=invite&kind=continue_authorization_grant&id=00000000000000000000000000"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_absolute_urls() {
|
||||
let base = Url::try_from("https://example.com/").unwrap();
|
||||
|
||||
+49
-8
@@ -3797,12 +3797,15 @@
|
||||
"attributes": {
|
||||
"token": "abc123def456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 5,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": "1970-01-01T00:00:00Z",
|
||||
"expires_at": "1970-01-31T00:00:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=abc123def456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01040G2081040G2081040G2081"
|
||||
@@ -3819,12 +3822,15 @@
|
||||
"attributes": {
|
||||
"token": "xyz789abc012",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": null,
|
||||
"times_used": 0,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "1970-01-01T00:00:00Z"
|
||||
"revoked_at": "1970-01-01T00:00:00Z",
|
||||
"invite_url": "https://example.com/register?token=xyz789abc012"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/02081040G2081040G2081040G2"
|
||||
@@ -3879,12 +3885,15 @@
|
||||
"attributes": {
|
||||
"token": "abc123def456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 5,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": "1970-01-01T00:00:00Z",
|
||||
"expires_at": "1970-01-31T00:00:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=abc123def456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01040G2081040G2081040G2081"
|
||||
@@ -3934,12 +3943,15 @@
|
||||
"attributes": {
|
||||
"token": "abc123def456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 5,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": "1970-01-01T00:00:00Z",
|
||||
"expires_at": "1970-01-31T00:00:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=abc123def456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01040G2081040G2081040G2081"
|
||||
@@ -4015,12 +4027,15 @@
|
||||
"attributes": {
|
||||
"token": "abc123def456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 5,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": "1970-01-01T00:00:00Z",
|
||||
"expires_at": "1970-01-31T00:00:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=abc123def456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01040G2081040G2081040G2081"
|
||||
@@ -4088,12 +4103,15 @@
|
||||
"attributes": {
|
||||
"token": "xyz789abc012",
|
||||
"valid": false,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": null,
|
||||
"times_used": 0,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": null,
|
||||
"expires_at": null,
|
||||
"revoked_at": "1970-01-01T00:00:00Z"
|
||||
"revoked_at": "1970-01-01T00:00:00Z",
|
||||
"invite_url": "https://example.com/register?token=xyz789abc012"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/02081040G2081040G2081040G2"
|
||||
@@ -4178,12 +4196,15 @@
|
||||
"attributes": {
|
||||
"token": "abc123def456",
|
||||
"valid": true,
|
||||
"username": null,
|
||||
"email": null,
|
||||
"usage_limit": 10,
|
||||
"times_used": 5,
|
||||
"created_at": "1970-01-01T00:00:00Z",
|
||||
"last_used_at": "1970-01-01T00:00:00Z",
|
||||
"expires_at": "1970-01-31T00:00:00Z",
|
||||
"revoked_at": null
|
||||
"revoked_at": null,
|
||||
"invite_url": "https://example.com/register?token=abc123def456"
|
||||
},
|
||||
"links": {
|
||||
"self": "/api/admin/v1/user-registration-tokens/01040G2081040G2081040G2081"
|
||||
@@ -7310,6 +7331,20 @@
|
||||
"description": "Whether the token is valid",
|
||||
"type": "boolean"
|
||||
},
|
||||
"username": {
|
||||
"description": "A username imposed on the registering user. If null, the user can\nchoose their own username.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"description": "An email address imposed on the registering user. If null, the user\ncan choose their own email.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"usage_limit": {
|
||||
"description": "Maximum number of times this token can be used",
|
||||
"type": [
|
||||
@@ -7353,13 +7388,19 @@
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"invite_url": {
|
||||
"description": "URL to share with the user to register with this token",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"token",
|
||||
"valid",
|
||||
"times_used",
|
||||
"created_at"
|
||||
"created_at",
|
||||
"invite_url"
|
||||
]
|
||||
},
|
||||
"AddUserRegistrationTokenRequest": {
|
||||
|
||||
Reference in new issue
Block a user