You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
graphql: admin API to add a user, lock them, and add emails without verification
This commit is contained in:
@ -63,6 +63,16 @@ impl User {
|
|||||||
&self.0.username
|
&self.0.username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When the object was created.
|
||||||
|
pub async fn created_at(&self) -> DateTime<Utc> {
|
||||||
|
self.0.created_at
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When the user was locked out.
|
||||||
|
pub async fn locked_at(&self) -> Option<DateTime<Utc>> {
|
||||||
|
self.0.locked_at
|
||||||
|
}
|
||||||
|
|
||||||
/// Access to the user's Matrix account information.
|
/// Access to the user's Matrix account information.
|
||||||
async fn matrix(&self, ctx: &Context<'_>) -> Result<MatrixUser, async_graphql::Error> {
|
async fn matrix(&self, ctx: &Context<'_>) -> Result<MatrixUser, async_graphql::Error> {
|
||||||
let state = ctx.state();
|
let state = ctx.state();
|
||||||
|
@ -16,6 +16,7 @@ mod browser_session;
|
|||||||
mod compat_session;
|
mod compat_session;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
mod oauth2_session;
|
mod oauth2_session;
|
||||||
|
mod user;
|
||||||
mod user_email;
|
mod user_email;
|
||||||
|
|
||||||
use async_graphql::MergedObject;
|
use async_graphql::MergedObject;
|
||||||
@ -24,6 +25,7 @@ use async_graphql::MergedObject;
|
|||||||
#[derive(Default, MergedObject)]
|
#[derive(Default, MergedObject)]
|
||||||
pub struct Mutation(
|
pub struct Mutation(
|
||||||
user_email::UserEmailMutations,
|
user_email::UserEmailMutations,
|
||||||
|
user::UserMutations,
|
||||||
oauth2_session::OAuth2SessionMutations,
|
oauth2_session::OAuth2SessionMutations,
|
||||||
compat_session::CompatSessionMutations,
|
compat_session::CompatSessionMutations,
|
||||||
browser_session::BrowserSessionMutations,
|
browser_session::BrowserSessionMutations,
|
||||||
|
235
crates/graphql/src/mutations/user.rs
Normal file
235
crates/graphql/src/mutations/user.rs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use async_graphql::{Context, Description, Enum, InputObject, Object, ID};
|
||||||
|
use mas_storage::{
|
||||||
|
job::{DeactivateUserJob, JobRepositoryExt, ProvisionUserJob},
|
||||||
|
user::UserRepository,
|
||||||
|
};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
model::{NodeType, User},
|
||||||
|
state::ContextExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct UserMutations {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The input for the `addUser` mutation.
|
||||||
|
#[derive(InputObject)]
|
||||||
|
struct AddUserInput {
|
||||||
|
/// The username of the user to add.
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The status of the `addUser` mutation.
|
||||||
|
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum AddUserStatus {
|
||||||
|
/// The user was added.
|
||||||
|
Added,
|
||||||
|
|
||||||
|
/// The user already exists.
|
||||||
|
Exists,
|
||||||
|
|
||||||
|
/// The username is invalid.
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The payload for the `addUser` mutation.
|
||||||
|
#[derive(Description)]
|
||||||
|
enum AddUserPayload {
|
||||||
|
Added(mas_data_model::User),
|
||||||
|
Exists(mas_data_model::User),
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object(use_type_description)]
|
||||||
|
impl AddUserPayload {
|
||||||
|
/// Status of the operation
|
||||||
|
async fn status(&self) -> AddUserStatus {
|
||||||
|
match self {
|
||||||
|
Self::Added(_) => AddUserStatus::Added,
|
||||||
|
Self::Exists(_) => AddUserStatus::Exists,
|
||||||
|
Self::Invalid => AddUserStatus::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The user that was added.
|
||||||
|
async fn user(&self) -> Option<User> {
|
||||||
|
match self {
|
||||||
|
Self::Added(user) | Self::Exists(user) => Some(User(user.clone())),
|
||||||
|
Self::Invalid => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The input for the `lockUser` mutation.
|
||||||
|
#[derive(InputObject)]
|
||||||
|
struct LockUserInput {
|
||||||
|
/// The ID of the user to lock.
|
||||||
|
user_id: ID,
|
||||||
|
|
||||||
|
/// Permanently lock the user.
|
||||||
|
deactivate: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The status of the `lockUser` mutation.
|
||||||
|
#[derive(Enum, Copy, Clone, Eq, PartialEq)]
|
||||||
|
enum LockUserStatus {
|
||||||
|
/// The user was locked.
|
||||||
|
Locked,
|
||||||
|
|
||||||
|
/// The user was not found.
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The payload for the `lockUser` mutation.
|
||||||
|
#[derive(Description)]
|
||||||
|
enum LockUserPayload {
|
||||||
|
/// The user was locked.
|
||||||
|
Locked(mas_data_model::User),
|
||||||
|
|
||||||
|
/// The user was not found.
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object(use_type_description)]
|
||||||
|
impl LockUserPayload {
|
||||||
|
/// Status of the operation
|
||||||
|
async fn status(&self) -> LockUserStatus {
|
||||||
|
match self {
|
||||||
|
Self::Locked(_) => LockUserStatus::Locked,
|
||||||
|
Self::NotFound => LockUserStatus::NotFound,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The user that was locked.
|
||||||
|
async fn user(&self) -> Option<User> {
|
||||||
|
match self {
|
||||||
|
Self::Locked(user) => Some(User(user.clone())),
|
||||||
|
Self::NotFound => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_username_character(c: char) -> bool {
|
||||||
|
c.is_ascii_lowercase()
|
||||||
|
|| c.is_ascii_digit()
|
||||||
|
|| c == '='
|
||||||
|
|| c == '_'
|
||||||
|
|| c == '-'
|
||||||
|
|| c == '.'
|
||||||
|
|| c == '/'
|
||||||
|
|| c == '+'
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: this should probably be moved somewhere else
|
||||||
|
fn username_valid(username: &str) -> bool {
|
||||||
|
if username.is_empty() || username.len() > 255 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not start with an underscore
|
||||||
|
if username.get(0..1) == Some("_") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should only contain valid characters
|
||||||
|
if !username.chars().all(valid_username_character) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl UserMutations {
|
||||||
|
/// Add a user. This is only available to administrators.
|
||||||
|
async fn add_user(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
input: AddUserInput,
|
||||||
|
) -> Result<AddUserPayload, async_graphql::Error> {
|
||||||
|
let state = ctx.state();
|
||||||
|
let requester = ctx.requester();
|
||||||
|
let clock = state.clock();
|
||||||
|
let mut rng = state.rng();
|
||||||
|
|
||||||
|
if !requester.is_admin() {
|
||||||
|
return Err(async_graphql::Error::new("Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
|
if let Some(user) = repo.user().find_by_username(&input.username).await? {
|
||||||
|
return Ok(AddUserPayload::Exists(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some basic check on the username
|
||||||
|
if !username_valid(&input.username) {
|
||||||
|
return Ok(AddUserPayload::Invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = repo.user().add(&mut rng, &clock, input.username).await?;
|
||||||
|
|
||||||
|
repo.job()
|
||||||
|
.schedule_job(ProvisionUserJob::new(&user))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
repo.save().await?;
|
||||||
|
|
||||||
|
Ok(AddUserPayload::Added(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock a user. This is only available to administrators.
|
||||||
|
async fn lock_user(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
input: LockUserInput,
|
||||||
|
) -> Result<LockUserPayload, async_graphql::Error> {
|
||||||
|
let state = ctx.state();
|
||||||
|
let requester = ctx.requester();
|
||||||
|
|
||||||
|
if !requester.is_admin() {
|
||||||
|
return Err(async_graphql::Error::new("Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
|
let user_id = NodeType::User.extract_ulid(&input.user_id)?;
|
||||||
|
let user = repo.user().lookup(user_id).await?;
|
||||||
|
|
||||||
|
let Some(user) = user else {
|
||||||
|
return Ok(LockUserPayload::NotFound);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deactivate = input.deactivate.unwrap_or(false);
|
||||||
|
|
||||||
|
let user = repo.user().lock(&state.clock(), user).await?;
|
||||||
|
|
||||||
|
if deactivate {
|
||||||
|
info!("Scheduling deactivation of user {}", user.id);
|
||||||
|
repo.job()
|
||||||
|
.schedule_job(DeactivateUserJob::new(&user, deactivate))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.save().await?;
|
||||||
|
|
||||||
|
Ok(LockUserPayload::Locked(user))
|
||||||
|
}
|
||||||
|
}
|
@ -36,8 +36,15 @@ pub struct UserEmailMutations {
|
|||||||
struct AddEmailInput {
|
struct AddEmailInput {
|
||||||
/// The email address to add
|
/// The email address to add
|
||||||
email: String,
|
email: String,
|
||||||
|
|
||||||
/// The ID of the user to add the email address to
|
/// The ID of the user to add the email address to
|
||||||
user_id: ID,
|
user_id: ID,
|
||||||
|
|
||||||
|
/// Skip the email address verification. Only allowed for admins.
|
||||||
|
skip_verification: Option<bool>,
|
||||||
|
|
||||||
|
/// Skip the email address policy check. Only allowed for admins.
|
||||||
|
skip_policy_check: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The status of the `addEmail` mutation
|
/// The status of the `addEmail` mutation
|
||||||
@ -382,6 +389,16 @@ impl UserEmailMutations {
|
|||||||
return Err(async_graphql::Error::new("Unauthorized"));
|
return Err(async_graphql::Error::new("Unauthorized"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only admins can skip validation
|
||||||
|
if (input.skip_verification.is_some() || input.skip_policy_check.is_some())
|
||||||
|
&& !requester.is_admin()
|
||||||
|
{
|
||||||
|
return Err(async_graphql::Error::new("Unauthorized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip_verification = input.skip_verification.unwrap_or(false);
|
||||||
|
let skip_policy_check = input.skip_policy_check.unwrap_or(false);
|
||||||
|
|
||||||
let mut repo = state.repository().await?;
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
let user = repo
|
let user = repo
|
||||||
@ -398,17 +415,19 @@ impl UserEmailMutations {
|
|||||||
return Ok(AddEmailPayload::Invalid);
|
return Ok(AddEmailPayload::Invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut policy = state.policy().await?;
|
if !skip_policy_check {
|
||||||
let res = policy.evaluate_email(&input.email).await?;
|
let mut policy = state.policy().await?;
|
||||||
if !res.valid() {
|
let res = policy.evaluate_email(&input.email).await?;
|
||||||
return Ok(AddEmailPayload::Denied {
|
if !res.valid() {
|
||||||
violations: res.violations,
|
return Ok(AddEmailPayload::Denied {
|
||||||
});
|
violations: res.violations,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find an existing email address
|
// Find an existing email address
|
||||||
let existing_user_email = repo.user_email().find(&user, &input.email).await?;
|
let existing_user_email = repo.user_email().find(&user, &input.email).await?;
|
||||||
let (added, user_email) = if let Some(user_email) = existing_user_email {
|
let (added, mut user_email) = if let Some(user_email) = existing_user_email {
|
||||||
(false, user_email)
|
(false, user_email)
|
||||||
} else {
|
} else {
|
||||||
let clock = state.clock();
|
let clock = state.clock();
|
||||||
@ -424,9 +443,16 @@ impl UserEmailMutations {
|
|||||||
|
|
||||||
// Schedule a job to verify the email address if needed
|
// Schedule a job to verify the email address if needed
|
||||||
if user_email.confirmed_at.is_none() {
|
if user_email.confirmed_at.is_none() {
|
||||||
repo.job()
|
if skip_verification {
|
||||||
.schedule_job(VerifyEmailJob::new(&user_email))
|
user_email = repo
|
||||||
.await?;
|
.user_email()
|
||||||
|
.mark_as_verified(&state.clock(), user_email)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
repo.job()
|
||||||
|
.schedule_job(VerifyEmailJob::new(&user_email))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.save().await?;
|
repo.save().await?;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use async_graphql::{Context, MergedObject, Object, ID};
|
use async_graphql::{Context, MergedObject, Object, ID};
|
||||||
|
use mas_storage::user::UserRepository;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model::{Anonymous, BrowserSession, Node, NodeType, OAuth2Client, User, UserEmail},
|
model::{Anonymous, BrowserSession, Node, NodeType, OAuth2Client, User, UserEmail},
|
||||||
@ -99,6 +100,30 @@ impl BaseQuery {
|
|||||||
Ok(user.map(User))
|
Ok(user.map(User))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch a user by its username.
|
||||||
|
async fn user_by_username(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
username: String,
|
||||||
|
) -> Result<Option<User>, async_graphql::Error> {
|
||||||
|
let requester = ctx.requester();
|
||||||
|
let state = ctx.state();
|
||||||
|
let mut repo = state.repository().await?;
|
||||||
|
|
||||||
|
let user = repo.user().find_by_username(&username).await?;
|
||||||
|
let Some(user) = user else {
|
||||||
|
// We don't want to leak the existence of a user
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Users can only see themselves, except for admins
|
||||||
|
if !requester.is_owner_or_admin(&user) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(User(user)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch a browser session by its ID.
|
/// Fetch a browser session by its ID.
|
||||||
async fn browser_session(
|
async fn browser_session(
|
||||||
&self,
|
&self,
|
||||||
|
@ -10,6 +10,14 @@ input AddEmailInput {
|
|||||||
The ID of the user to add the email address to
|
The ID of the user to add the email address to
|
||||||
"""
|
"""
|
||||||
userId: ID!
|
userId: ID!
|
||||||
|
"""
|
||||||
|
Skip the email address verification. Only allowed for admins.
|
||||||
|
"""
|
||||||
|
skipVerification: Boolean
|
||||||
|
"""
|
||||||
|
Skip the email address policy check. Only allowed for admins.
|
||||||
|
"""
|
||||||
|
skipPolicyCheck: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -56,6 +64,48 @@ enum AddEmailStatus {
|
|||||||
DENIED
|
DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The input for the `addUser` mutation.
|
||||||
|
"""
|
||||||
|
input AddUserInput {
|
||||||
|
"""
|
||||||
|
The username of the user to add.
|
||||||
|
"""
|
||||||
|
username: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The payload for the `addUser` mutation.
|
||||||
|
"""
|
||||||
|
type AddUserPayload {
|
||||||
|
"""
|
||||||
|
Status of the operation
|
||||||
|
"""
|
||||||
|
status: AddUserStatus!
|
||||||
|
"""
|
||||||
|
The user that was added.
|
||||||
|
"""
|
||||||
|
user: User
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The status of the `addUser` mutation.
|
||||||
|
"""
|
||||||
|
enum AddUserStatus {
|
||||||
|
"""
|
||||||
|
The user was added.
|
||||||
|
"""
|
||||||
|
ADDED
|
||||||
|
"""
|
||||||
|
The user already exists.
|
||||||
|
"""
|
||||||
|
EXISTS
|
||||||
|
"""
|
||||||
|
The username is invalid.
|
||||||
|
"""
|
||||||
|
INVALID
|
||||||
|
}
|
||||||
|
|
||||||
type Anonymous implements Node {
|
type Anonymous implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
@ -439,6 +489,48 @@ enum EndOAuth2SessionStatus {
|
|||||||
NOT_FOUND
|
NOT_FOUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The input for the `lockUser` mutation.
|
||||||
|
"""
|
||||||
|
input LockUserInput {
|
||||||
|
"""
|
||||||
|
The ID of the user to lock.
|
||||||
|
"""
|
||||||
|
userId: ID!
|
||||||
|
"""
|
||||||
|
Permanently lock the user.
|
||||||
|
"""
|
||||||
|
deactivate: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The payload for the `lockUser` mutation.
|
||||||
|
"""
|
||||||
|
type LockUserPayload {
|
||||||
|
"""
|
||||||
|
Status of the operation
|
||||||
|
"""
|
||||||
|
status: LockUserStatus!
|
||||||
|
"""
|
||||||
|
The user that was locked.
|
||||||
|
"""
|
||||||
|
user: User
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The status of the `lockUser` mutation.
|
||||||
|
"""
|
||||||
|
enum LockUserStatus {
|
||||||
|
"""
|
||||||
|
The user was locked.
|
||||||
|
"""
|
||||||
|
LOCKED
|
||||||
|
"""
|
||||||
|
The user was not found.
|
||||||
|
"""
|
||||||
|
NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
type MatrixUser {
|
type MatrixUser {
|
||||||
"""
|
"""
|
||||||
The Matrix ID of the user.
|
The Matrix ID of the user.
|
||||||
@ -480,6 +572,14 @@ type Mutation {
|
|||||||
Set an email address as primary
|
Set an email address as primary
|
||||||
"""
|
"""
|
||||||
setPrimaryEmail(input: SetPrimaryEmailInput!): SetPrimaryEmailPayload!
|
setPrimaryEmail(input: SetPrimaryEmailInput!): SetPrimaryEmailPayload!
|
||||||
|
"""
|
||||||
|
Add a user. This is only available to administrators.
|
||||||
|
"""
|
||||||
|
addUser(input: AddUserInput!): AddUserPayload!
|
||||||
|
"""
|
||||||
|
Lock a user. This is only available to administrators.
|
||||||
|
"""
|
||||||
|
lockUser(input: LockUserInput!): LockUserPayload!
|
||||||
endOauth2Session(input: EndOAuth2SessionInput!): EndOAuth2SessionPayload!
|
endOauth2Session(input: EndOAuth2SessionInput!): EndOAuth2SessionPayload!
|
||||||
endCompatSession(input: EndCompatSessionInput!): EndCompatSessionPayload!
|
endCompatSession(input: EndCompatSessionInput!): EndCompatSessionPayload!
|
||||||
endBrowserSession(input: EndBrowserSessionInput!): EndBrowserSessionPayload!
|
endBrowserSession(input: EndBrowserSessionInput!): EndBrowserSessionPayload!
|
||||||
@ -685,6 +785,10 @@ type Query {
|
|||||||
"""
|
"""
|
||||||
user(id: ID!): User
|
user(id: ID!): User
|
||||||
"""
|
"""
|
||||||
|
Fetch a user by its username.
|
||||||
|
"""
|
||||||
|
userByUsername(username: String!): User
|
||||||
|
"""
|
||||||
Fetch a browser session by its ID.
|
Fetch a browser session by its ID.
|
||||||
"""
|
"""
|
||||||
browserSession(id: ID!): BrowserSession
|
browserSession(id: ID!): BrowserSession
|
||||||
@ -1027,6 +1131,14 @@ type User implements Node {
|
|||||||
"""
|
"""
|
||||||
username: String!
|
username: String!
|
||||||
"""
|
"""
|
||||||
|
When the object was created.
|
||||||
|
"""
|
||||||
|
createdAt: DateTime!
|
||||||
|
"""
|
||||||
|
When the user was locked out.
|
||||||
|
"""
|
||||||
|
lockedAt: DateTime
|
||||||
|
"""
|
||||||
Access to the user's Matrix account information.
|
Access to the user's Matrix account information.
|
||||||
"""
|
"""
|
||||||
matrix: MatrixUser!
|
matrix: MatrixUser!
|
||||||
|
@ -41,6 +41,10 @@ export type Scalars = {
|
|||||||
export type AddEmailInput = {
|
export type AddEmailInput = {
|
||||||
/** The email address to add */
|
/** The email address to add */
|
||||||
email: Scalars["String"]["input"];
|
email: Scalars["String"]["input"];
|
||||||
|
/** Skip the email address policy check. Only allowed for admins. */
|
||||||
|
skipPolicyCheck?: InputMaybe<Scalars["Boolean"]["input"]>;
|
||||||
|
/** Skip the email address verification. Only allowed for admins. */
|
||||||
|
skipVerification?: InputMaybe<Scalars["Boolean"]["input"]>;
|
||||||
/** The ID of the user to add the email address to */
|
/** The ID of the user to add the email address to */
|
||||||
userId: Scalars["ID"]["input"];
|
userId: Scalars["ID"]["input"];
|
||||||
};
|
};
|
||||||
@ -70,6 +74,31 @@ export enum AddEmailStatus {
|
|||||||
Invalid = "INVALID",
|
Invalid = "INVALID",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The input for the `addUser` mutation. */
|
||||||
|
export type AddUserInput = {
|
||||||
|
/** The username of the user to add. */
|
||||||
|
username: Scalars["String"]["input"];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The payload for the `addUser` mutation. */
|
||||||
|
export type AddUserPayload = {
|
||||||
|
__typename?: "AddUserPayload";
|
||||||
|
/** Status of the operation */
|
||||||
|
status: AddUserStatus;
|
||||||
|
/** The user that was added. */
|
||||||
|
user?: Maybe<User>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The status of the `addUser` mutation. */
|
||||||
|
export enum AddUserStatus {
|
||||||
|
/** The user was added. */
|
||||||
|
Added = "ADDED",
|
||||||
|
/** The user already exists. */
|
||||||
|
Exists = "EXISTS",
|
||||||
|
/** The username is invalid. */
|
||||||
|
Invalid = "INVALID",
|
||||||
|
}
|
||||||
|
|
||||||
export type Anonymous = Node & {
|
export type Anonymous = Node & {
|
||||||
__typename?: "Anonymous";
|
__typename?: "Anonymous";
|
||||||
id: Scalars["ID"]["output"];
|
id: Scalars["ID"]["output"];
|
||||||
@ -313,6 +342,31 @@ export enum EndOAuth2SessionStatus {
|
|||||||
NotFound = "NOT_FOUND",
|
NotFound = "NOT_FOUND",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The input for the `lockUser` mutation. */
|
||||||
|
export type LockUserInput = {
|
||||||
|
/** Permanently lock the user. */
|
||||||
|
deactivate?: InputMaybe<Scalars["Boolean"]["input"]>;
|
||||||
|
/** The ID of the user to lock. */
|
||||||
|
userId: Scalars["ID"]["input"];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The payload for the `lockUser` mutation. */
|
||||||
|
export type LockUserPayload = {
|
||||||
|
__typename?: "LockUserPayload";
|
||||||
|
/** Status of the operation */
|
||||||
|
status: LockUserStatus;
|
||||||
|
/** The user that was locked. */
|
||||||
|
user?: Maybe<User>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The status of the `lockUser` mutation. */
|
||||||
|
export enum LockUserStatus {
|
||||||
|
/** The user was locked. */
|
||||||
|
Locked = "LOCKED",
|
||||||
|
/** The user was not found. */
|
||||||
|
NotFound = "NOT_FOUND",
|
||||||
|
}
|
||||||
|
|
||||||
export type MatrixUser = {
|
export type MatrixUser = {
|
||||||
__typename?: "MatrixUser";
|
__typename?: "MatrixUser";
|
||||||
/** The avatar URL of the user, if any. */
|
/** The avatar URL of the user, if any. */
|
||||||
@ -328,9 +382,13 @@ export type Mutation = {
|
|||||||
__typename?: "Mutation";
|
__typename?: "Mutation";
|
||||||
/** Add an email address to the specified user */
|
/** Add an email address to the specified user */
|
||||||
addEmail: AddEmailPayload;
|
addEmail: AddEmailPayload;
|
||||||
|
/** Add a user. This is only available to administrators. */
|
||||||
|
addUser: AddUserPayload;
|
||||||
endBrowserSession: EndBrowserSessionPayload;
|
endBrowserSession: EndBrowserSessionPayload;
|
||||||
endCompatSession: EndCompatSessionPayload;
|
endCompatSession: EndCompatSessionPayload;
|
||||||
endOauth2Session: EndOAuth2SessionPayload;
|
endOauth2Session: EndOAuth2SessionPayload;
|
||||||
|
/** Lock a user. This is only available to administrators. */
|
||||||
|
lockUser: LockUserPayload;
|
||||||
/** Remove an email address */
|
/** Remove an email address */
|
||||||
removeEmail: RemoveEmailPayload;
|
removeEmail: RemoveEmailPayload;
|
||||||
/** Send a verification code for an email address */
|
/** Send a verification code for an email address */
|
||||||
@ -348,6 +406,11 @@ export type MutationAddEmailArgs = {
|
|||||||
input: AddEmailInput;
|
input: AddEmailInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The mutations root of the GraphQL interface. */
|
||||||
|
export type MutationAddUserArgs = {
|
||||||
|
input: AddUserInput;
|
||||||
|
};
|
||||||
|
|
||||||
/** The mutations root of the GraphQL interface. */
|
/** The mutations root of the GraphQL interface. */
|
||||||
export type MutationEndBrowserSessionArgs = {
|
export type MutationEndBrowserSessionArgs = {
|
||||||
input: EndBrowserSessionInput;
|
input: EndBrowserSessionInput;
|
||||||
@ -363,6 +426,11 @@ export type MutationEndOauth2SessionArgs = {
|
|||||||
input: EndOAuth2SessionInput;
|
input: EndOAuth2SessionInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The mutations root of the GraphQL interface. */
|
||||||
|
export type MutationLockUserArgs = {
|
||||||
|
input: LockUserInput;
|
||||||
|
};
|
||||||
|
|
||||||
/** The mutations root of the GraphQL interface. */
|
/** The mutations root of the GraphQL interface. */
|
||||||
export type MutationRemoveEmailArgs = {
|
export type MutationRemoveEmailArgs = {
|
||||||
input: RemoveEmailInput;
|
input: RemoveEmailInput;
|
||||||
@ -521,6 +589,8 @@ export type Query = {
|
|||||||
upstreamOauth2Providers: UpstreamOAuth2ProviderConnection;
|
upstreamOauth2Providers: UpstreamOAuth2ProviderConnection;
|
||||||
/** Fetch a user by its ID. */
|
/** Fetch a user by its ID. */
|
||||||
user?: Maybe<User>;
|
user?: Maybe<User>;
|
||||||
|
/** Fetch a user by its username. */
|
||||||
|
userByUsername?: Maybe<User>;
|
||||||
/** Fetch a user email by its ID. */
|
/** Fetch a user email by its ID. */
|
||||||
userEmail?: Maybe<UserEmail>;
|
userEmail?: Maybe<UserEmail>;
|
||||||
/** Get the viewer */
|
/** Get the viewer */
|
||||||
@ -573,6 +643,11 @@ export type QueryUserArgs = {
|
|||||||
id: Scalars["ID"]["input"];
|
id: Scalars["ID"]["input"];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The query root of the GraphQL interface. */
|
||||||
|
export type QueryUserByUsernameArgs = {
|
||||||
|
username: Scalars["String"]["input"];
|
||||||
|
};
|
||||||
|
|
||||||
/** The query root of the GraphQL interface. */
|
/** The query root of the GraphQL interface. */
|
||||||
export type QueryUserEmailArgs = {
|
export type QueryUserEmailArgs = {
|
||||||
id: Scalars["ID"]["input"];
|
id: Scalars["ID"]["input"];
|
||||||
@ -761,10 +836,14 @@ export type User = Node & {
|
|||||||
compatSessions: CompatSessionConnection;
|
compatSessions: CompatSessionConnection;
|
||||||
/** Get the list of compatibility SSO logins, chronologically sorted */
|
/** Get the list of compatibility SSO logins, chronologically sorted */
|
||||||
compatSsoLogins: CompatSsoLoginConnection;
|
compatSsoLogins: CompatSsoLoginConnection;
|
||||||
|
/** When the object was created. */
|
||||||
|
createdAt: Scalars["DateTime"]["output"];
|
||||||
/** Get the list of emails, chronologically sorted */
|
/** Get the list of emails, chronologically sorted */
|
||||||
emails: UserEmailConnection;
|
emails: UserEmailConnection;
|
||||||
/** ID of the object. */
|
/** ID of the object. */
|
||||||
id: Scalars["ID"]["output"];
|
id: Scalars["ID"]["output"];
|
||||||
|
/** When the user was locked out. */
|
||||||
|
lockedAt?: Maybe<Scalars["DateTime"]["output"]>;
|
||||||
/** Access to the user's Matrix account information. */
|
/** Access to the user's Matrix account information. */
|
||||||
matrix: MatrixUser;
|
matrix: MatrixUser;
|
||||||
/** Get the list of OAuth 2.0 sessions, chronologically sorted */
|
/** Get the list of OAuth 2.0 sessions, chronologically sorted */
|
||||||
|
@ -59,6 +59,33 @@ export default {
|
|||||||
],
|
],
|
||||||
interfaces: [],
|
interfaces: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "AddUserPayload",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "status",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user",
|
||||||
|
type: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "User",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
interfaces: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
kind: "OBJECT",
|
kind: "OBJECT",
|
||||||
name: "Anonymous",
|
name: "Anonymous",
|
||||||
@ -782,6 +809,33 @@ export default {
|
|||||||
],
|
],
|
||||||
interfaces: [],
|
interfaces: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "LockUserPayload",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "status",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user",
|
||||||
|
type: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "User",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
interfaces: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
kind: "OBJECT",
|
kind: "OBJECT",
|
||||||
name: "MatrixUser",
|
name: "MatrixUser",
|
||||||
@ -843,6 +897,29 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "addUser",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "AddUserPayload",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "input",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "endBrowserSession",
|
name: "endBrowserSession",
|
||||||
type: {
|
type: {
|
||||||
@ -912,6 +989,29 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "lockUser",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "LockUserPayload",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "input",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "removeEmail",
|
name: "removeEmail",
|
||||||
type: {
|
type: {
|
||||||
@ -1657,6 +1757,26 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "userByUsername",
|
||||||
|
type: {
|
||||||
|
kind: "OBJECT",
|
||||||
|
name: "User",
|
||||||
|
ofType: null,
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "username",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "userEmail",
|
name: "userEmail",
|
||||||
type: {
|
type: {
|
||||||
@ -2320,6 +2440,17 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "createdAt",
|
||||||
|
type: {
|
||||||
|
kind: "NON_NULL",
|
||||||
|
ofType: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "emails",
|
name: "emails",
|
||||||
type: {
|
type: {
|
||||||
@ -2379,6 +2510,14 @@ export default {
|
|||||||
},
|
},
|
||||||
args: [],
|
args: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "lockedAt",
|
||||||
|
type: {
|
||||||
|
kind: "SCALAR",
|
||||||
|
name: "Any",
|
||||||
|
},
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "matrix",
|
name: "matrix",
|
||||||
type: {
|
type: {
|
||||||
|
Reference in New Issue
Block a user