You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-09 04:22:45 +03:00
"Can request admin" flag on user
This commit is contained in:
@@ -27,6 +27,7 @@ pub struct User {
|
||||
pub primary_user_email_id: Option<Ulid>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub locked_at: Option<DateTime<Utc>>,
|
||||
pub can_request_admin: bool,
|
||||
}
|
||||
|
||||
impl User {
|
||||
@@ -47,6 +48,7 @@ impl User {
|
||||
primary_user_email_id: None,
|
||||
created_at: now,
|
||||
locked_at: None,
|
||||
can_request_admin: false,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,11 @@ impl User {
|
||||
self.0.locked_at
|
||||
}
|
||||
|
||||
/// Whether the user can request admin privileges.
|
||||
pub async fn can_request_admin(&self) -> bool {
|
||||
self.0.can_request_admin
|
||||
}
|
||||
|
||||
/// Access to the user's Matrix account information.
|
||||
async fn matrix(&self, ctx: &Context<'_>) -> Result<MatrixUser, async_graphql::Error> {
|
||||
let state = ctx.state();
|
||||
|
@@ -126,6 +126,37 @@ impl LockUserPayload {
|
||||
}
|
||||
}
|
||||
|
||||
/// The input for the `setCanRequestAdmin` mutation.
|
||||
#[derive(InputObject)]
|
||||
struct SetCanRequestAdminInput {
|
||||
/// The ID of the user to update.
|
||||
user_id: ID,
|
||||
|
||||
/// Whether the user can request admin.
|
||||
can_request_admin: bool,
|
||||
}
|
||||
|
||||
/// The payload for the `setCanRequestAdmin` mutation.
|
||||
#[derive(Description)]
|
||||
enum SetCanRequestAdminPayload {
|
||||
/// The user was updated.
|
||||
Updated(mas_data_model::User),
|
||||
|
||||
/// The user was not found.
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[Object(use_type_description)]
|
||||
impl SetCanRequestAdminPayload {
|
||||
/// The user that was updated.
|
||||
async fn user(&self) -> Option<User> {
|
||||
match self {
|
||||
Self::Updated(user) => Some(User(user.clone())),
|
||||
Self::NotFound => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_username_character(c: char) -> bool {
|
||||
c.is_ascii_lowercase()
|
||||
|| c.is_ascii_digit()
|
||||
@@ -232,4 +263,37 @@ impl UserMutations {
|
||||
|
||||
Ok(LockUserPayload::Locked(user))
|
||||
}
|
||||
|
||||
/// Set whether a user can request admin. This is only available to
|
||||
/// administrators.
|
||||
async fn set_can_request_admin(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
input: SetCanRequestAdminInput,
|
||||
) -> Result<SetCanRequestAdminPayload, 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(SetCanRequestAdminPayload::NotFound);
|
||||
};
|
||||
|
||||
let user = repo
|
||||
.user()
|
||||
.set_can_request_admin(user, input.can_request_admin)
|
||||
.await?;
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
Ok(SetCanRequestAdminPayload::Updated(user))
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT user_id\n , username\n , primary_user_email_id\n , created_at\n , locked_at\n FROM users\n WHERE user_id = $1\n ",
|
||||
"query": "\n SELECT user_id\n , username\n , primary_user_email_id\n , created_at\n , locked_at\n , can_request_admin\n FROM users\n WHERE user_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,11 @@
|
||||
"ordinal": 4,
|
||||
"name": "locked_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "can_request_admin",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -39,8 +44,9 @@
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e0ea7d93ab3f565828b2faab4cc5e1a6ac868c95bfaee3a6960df1cf484d53da"
|
||||
"hash": "0d892dc8589ba54bb886972b6db00eaf7e41ff0db98fabdff5dcba0a7aa4e77d"
|
||||
}
|
15
crates/storage-pg/.sqlx/query-1dbc50cdab36da307c569891ab7b1ab4aaf128fed6be67ca0f139d697614c63b.json
generated
Normal file
15
crates/storage-pg/.sqlx/query-1dbc50cdab36da307c569891ab7b1ab4aaf128fed6be67ca0f139d697614c63b.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE users\n SET can_request_admin = $2\n WHERE user_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1dbc50cdab36da307c569891ab7b1ab4aaf128fed6be67ca0f139d697614c63b"
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT user_id\n , username\n , primary_user_email_id\n , created_at\n , locked_at\n FROM users\n WHERE username = $1\n ",
|
||||
"query": "\n SELECT user_id\n , username\n , primary_user_email_id\n , created_at\n , locked_at\n , can_request_admin\n FROM users\n WHERE username = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -27,6 +27,11 @@
|
||||
"ordinal": 4,
|
||||
"name": "locked_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "can_request_admin",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -39,8 +44,9 @@
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "bfa5eaeaa5b4574bb083c86711eb4599f6374c96bb4a6827d400acb22fb0fd39"
|
||||
"hash": "423e6aa88e0b8a01a90e108107a3d3998418fa43638b6510f28b56a2d6952222"
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT s.user_session_id\n , s.created_at AS \"user_session_created_at\"\n , s.finished_at AS \"user_session_finished_at\"\n , s.user_agent AS \"user_session_user_agent\"\n , s.last_active_at AS \"user_session_last_active_at\"\n , s.last_active_ip AS \"user_session_last_active_ip: IpAddr\"\n , u.user_id\n , u.username AS \"user_username\"\n , u.primary_user_email_id AS \"user_primary_user_email_id\"\n , u.created_at AS \"user_created_at\"\n , u.locked_at AS \"user_locked_at\"\n FROM user_sessions s\n INNER JOIN users u\n USING (user_id)\n WHERE s.user_session_id = $1\n ",
|
||||
"query": "\n SELECT s.user_session_id\n , s.created_at AS \"user_session_created_at\"\n , s.finished_at AS \"user_session_finished_at\"\n , s.user_agent AS \"user_session_user_agent\"\n , s.last_active_at AS \"user_session_last_active_at\"\n , s.last_active_ip AS \"user_session_last_active_ip: IpAddr\"\n , u.user_id\n , u.username AS \"user_username\"\n , u.primary_user_email_id AS \"user_primary_user_email_id\"\n , u.created_at AS \"user_created_at\"\n , u.locked_at AS \"user_locked_at\"\n , u.can_request_admin AS \"user_can_request_admin\"\n FROM user_sessions s\n INNER JOIN users u\n USING (user_id)\n WHERE s.user_session_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -57,6 +57,11 @@
|
||||
"ordinal": 10,
|
||||
"name": "user_locked_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "user_can_request_admin",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -75,8 +80,9 @@
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2b0d54c284dc4d946faae4190568bf597c04b40f010132dd7bf68462c47f9eac"
|
||||
"hash": "e602a7c76386f732de686694257e03f35c18643c91a06f9c4a3fa0a5f103df58"
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
-- 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.
|
||||
|
||||
-- Adds a `can_request_admin` column to the `users` table
|
||||
ALTER TABLE users
|
||||
ADD COLUMN can_request_admin BOOLEAN NOT NULL DEFAULT FALSE;
|
@@ -34,6 +34,7 @@ pub enum Users {
|
||||
PrimaryUserEmailId,
|
||||
CreatedAt,
|
||||
LockedAt,
|
||||
CanRequestAdmin,
|
||||
}
|
||||
|
||||
#[derive(sea_query::Iden)]
|
||||
|
@@ -57,6 +57,7 @@ struct UserLookup {
|
||||
primary_user_email_id: Option<Uuid>,
|
||||
created_at: DateTime<Utc>,
|
||||
locked_at: Option<DateTime<Utc>>,
|
||||
can_request_admin: bool,
|
||||
}
|
||||
|
||||
impl From<UserLookup> for User {
|
||||
@@ -69,6 +70,7 @@ impl From<UserLookup> for User {
|
||||
primary_user_email_id: value.primary_user_email_id.map(Into::into),
|
||||
created_at: value.created_at,
|
||||
locked_at: value.locked_at,
|
||||
can_request_admin: value.can_request_admin,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +97,7 @@ impl<'c> UserRepository for PgUserRepository<'c> {
|
||||
, primary_user_email_id
|
||||
, created_at
|
||||
, locked_at
|
||||
, can_request_admin
|
||||
FROM users
|
||||
WHERE user_id = $1
|
||||
"#,
|
||||
@@ -127,6 +130,7 @@ impl<'c> UserRepository for PgUserRepository<'c> {
|
||||
, primary_user_email_id
|
||||
, created_at
|
||||
, locked_at
|
||||
, can_request_admin
|
||||
FROM users
|
||||
WHERE username = $1
|
||||
"#,
|
||||
@@ -186,6 +190,7 @@ impl<'c> UserRepository for PgUserRepository<'c> {
|
||||
primary_user_email_id: None,
|
||||
created_at,
|
||||
locked_at: None,
|
||||
can_request_admin: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -281,4 +286,39 @@ impl<'c> UserRepository for PgUserRepository<'c> {
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "db.user.set_can_request_admin",
|
||||
skip_all,
|
||||
fields(
|
||||
db.statement,
|
||||
%user.id,
|
||||
user.can_request_admin = can_request_admin,
|
||||
),
|
||||
err,
|
||||
)]
|
||||
async fn set_can_request_admin(
|
||||
&mut self,
|
||||
mut user: User,
|
||||
can_request_admin: bool,
|
||||
) -> Result<User, Self::Error> {
|
||||
let res = sqlx::query!(
|
||||
r#"
|
||||
UPDATE users
|
||||
SET can_request_admin = $2
|
||||
WHERE user_id = $1
|
||||
"#,
|
||||
Uuid::from(user.id),
|
||||
can_request_admin,
|
||||
)
|
||||
.traced()
|
||||
.execute(&mut *self.conn)
|
||||
.await?;
|
||||
|
||||
DatabaseError::ensure_affected_rows(&res, 1)?;
|
||||
|
||||
user.can_request_admin = can_request_admin;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
@@ -63,6 +63,7 @@ struct SessionLookup {
|
||||
user_primary_user_email_id: Option<Uuid>,
|
||||
user_created_at: DateTime<Utc>,
|
||||
user_locked_at: Option<DateTime<Utc>>,
|
||||
user_can_request_admin: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<SessionLookup> for BrowserSession {
|
||||
@@ -77,6 +78,7 @@ impl TryFrom<SessionLookup> for BrowserSession {
|
||||
primary_user_email_id: value.user_primary_user_email_id.map(Into::into),
|
||||
created_at: value.user_created_at,
|
||||
locked_at: value.user_locked_at,
|
||||
can_request_admin: value.user_can_request_admin,
|
||||
};
|
||||
|
||||
Ok(BrowserSession {
|
||||
@@ -155,6 +157,7 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
||||
, u.primary_user_email_id AS "user_primary_user_email_id"
|
||||
, u.created_at AS "user_created_at"
|
||||
, u.locked_at AS "user_locked_at"
|
||||
, u.can_request_admin AS "user_can_request_admin"
|
||||
FROM user_sessions s
|
||||
INNER JOIN users u
|
||||
USING (user_id)
|
||||
@@ -313,6 +316,10 @@ impl<'c> BrowserSessionRepository for PgBrowserSessionRepository<'c> {
|
||||
Expr::col((Users::Table, Users::LockedAt)),
|
||||
SessionLookupIden::UserLockedAt,
|
||||
)
|
||||
.expr_as(
|
||||
Expr::col((Users::Table, Users::CanRequestAdmin)),
|
||||
SessionLookupIden::UserCanRequestAdmin,
|
||||
)
|
||||
.from(UserSessions::Table)
|
||||
.inner_join(
|
||||
Users::Table,
|
||||
|
@@ -95,6 +95,26 @@ async fn test_user_repo(pool: PgPool) {
|
||||
let user = repo.user().unlock(user).await.unwrap();
|
||||
assert!(user.is_valid());
|
||||
|
||||
// Set the can_request_admin flag
|
||||
let user = repo.user().set_can_request_admin(user, true).await.unwrap();
|
||||
assert!(user.can_request_admin);
|
||||
|
||||
// Check that the property is retrieved on lookup
|
||||
let user = repo.user().lookup(user.id).await.unwrap().unwrap();
|
||||
assert!(user.can_request_admin);
|
||||
|
||||
// Unset the can_request_admin flag
|
||||
let user = repo
|
||||
.user()
|
||||
.set_can_request_admin(user, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!user.can_request_admin);
|
||||
|
||||
// Check that the property is retrieved on lookup
|
||||
let user = repo.user().lookup(user.id).await.unwrap().unwrap();
|
||||
assert!(!user.can_request_admin);
|
||||
|
||||
repo.save().await.unwrap();
|
||||
}
|
||||
|
||||
|
@@ -123,6 +123,23 @@ pub trait UserRepository: Send + Sync {
|
||||
///
|
||||
/// Returns [`Self::Error`] if the underlying repository fails
|
||||
async fn unlock(&mut self, user: User) -> Result<User, Self::Error>;
|
||||
|
||||
/// Set whether a [`User`] can request admin
|
||||
///
|
||||
/// Returns the [`User`] with the new `can_request_admin` value
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `user`: The [`User`] to update
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Self::Error`] if the underlying repository fails
|
||||
async fn set_can_request_admin(
|
||||
&mut self,
|
||||
user: User,
|
||||
can_request_admin: bool,
|
||||
) -> Result<User, Self::Error>;
|
||||
}
|
||||
|
||||
repository_impl!(UserRepository:
|
||||
@@ -137,4 +154,9 @@ repository_impl!(UserRepository:
|
||||
async fn exists(&mut self, username: &str) -> Result<bool, Self::Error>;
|
||||
async fn lock(&mut self, clock: &dyn Clock, user: User) -> Result<User, Self::Error>;
|
||||
async fn unlock(&mut self, user: User) -> Result<User, Self::Error>;
|
||||
async fn set_can_request_admin(
|
||||
&mut self,
|
||||
user: User,
|
||||
can_request_admin: bool,
|
||||
) -> Result<User, Self::Error>;
|
||||
);
|
||||
|
@@ -643,6 +643,13 @@ type Mutation {
|
||||
"""
|
||||
lockUser(input: LockUserInput!): LockUserPayload!
|
||||
"""
|
||||
Set whether a user can request admin. This is only available to
|
||||
administrators.
|
||||
"""
|
||||
setCanRequestAdmin(
|
||||
input: SetCanRequestAdminInput!
|
||||
): SetCanRequestAdminPayload!
|
||||
"""
|
||||
Create a new arbitrary OAuth 2.0 Session.
|
||||
|
||||
Only available for administrators.
|
||||
@@ -1018,6 +1025,30 @@ enum SessionState {
|
||||
FINISHED
|
||||
}
|
||||
|
||||
"""
|
||||
The input for the `setCanRequestAdmin` mutation.
|
||||
"""
|
||||
input SetCanRequestAdminInput {
|
||||
"""
|
||||
The ID of the user to update.
|
||||
"""
|
||||
userId: ID!
|
||||
"""
|
||||
Whether the user can request admin.
|
||||
"""
|
||||
canRequestAdmin: Boolean!
|
||||
}
|
||||
|
||||
"""
|
||||
The payload for the `setCanRequestAdmin` mutation.
|
||||
"""
|
||||
type SetCanRequestAdminPayload {
|
||||
"""
|
||||
The user that was updated.
|
||||
"""
|
||||
user: User
|
||||
}
|
||||
|
||||
"""
|
||||
The input for the `addEmail` mutation
|
||||
"""
|
||||
@@ -1233,6 +1264,10 @@ type User implements Node {
|
||||
"""
|
||||
lockedAt: DateTime
|
||||
"""
|
||||
Whether the user can request admin privileges.
|
||||
"""
|
||||
canRequestAdmin: Boolean!
|
||||
"""
|
||||
Access to the user's Matrix account information.
|
||||
"""
|
||||
matrix: MatrixUser!
|
||||
|
@@ -436,6 +436,11 @@ export type Mutation = {
|
||||
removeEmail: RemoveEmailPayload;
|
||||
/** Send a verification code for an email address */
|
||||
sendVerificationEmail: SendVerificationEmailPayload;
|
||||
/**
|
||||
* Set whether a user can request admin. This is only available to
|
||||
* administrators.
|
||||
*/
|
||||
setCanRequestAdmin: SetCanRequestAdminPayload;
|
||||
/** Set the display name of a user */
|
||||
setDisplayName: SetDisplayNamePayload;
|
||||
/** Set an email address as primary */
|
||||
@@ -489,6 +494,11 @@ export type MutationSendVerificationEmailArgs = {
|
||||
input: SendVerificationEmailInput;
|
||||
};
|
||||
|
||||
/** The mutations root of the GraphQL interface. */
|
||||
export type MutationSetCanRequestAdminArgs = {
|
||||
input: SetCanRequestAdminInput;
|
||||
};
|
||||
|
||||
/** The mutations root of the GraphQL interface. */
|
||||
export type MutationSetDisplayNameArgs = {
|
||||
input: SetDisplayNameInput;
|
||||
@@ -762,6 +772,21 @@ export enum SessionState {
|
||||
Finished = "FINISHED",
|
||||
}
|
||||
|
||||
/** The input for the `setCanRequestAdmin` mutation. */
|
||||
export type SetCanRequestAdminInput = {
|
||||
/** Whether the user can request admin. */
|
||||
canRequestAdmin: Scalars["Boolean"]["input"];
|
||||
/** The ID of the user to update. */
|
||||
userId: Scalars["ID"]["input"];
|
||||
};
|
||||
|
||||
/** The payload for the `setCanRequestAdmin` mutation. */
|
||||
export type SetCanRequestAdminPayload = {
|
||||
__typename?: "SetCanRequestAdminPayload";
|
||||
/** The user that was updated. */
|
||||
user?: Maybe<User>;
|
||||
};
|
||||
|
||||
/** The input for the `addEmail` mutation */
|
||||
export type SetDisplayNameInput = {
|
||||
/** The display name to set. If `None`, the display name will be removed. */
|
||||
@@ -891,6 +916,8 @@ export type User = Node & {
|
||||
appSessions: AppSessionConnection;
|
||||
/** Get the list of active browser sessions, chronologically sorted */
|
||||
browserSessions: BrowserSessionConnection;
|
||||
/** Whether the user can request admin privileges. */
|
||||
canRequestAdmin: Scalars["Boolean"]["output"];
|
||||
/** Get the list of compatibility sessions, chronologically sorted */
|
||||
compatSessions: CompatSessionConnection;
|
||||
/** Get the list of compatibility SSO logins, chronologically sorted */
|
||||
|
@@ -1261,6 +1261,29 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "setCanRequestAdmin",
|
||||
type: {
|
||||
kind: "NON_NULL",
|
||||
ofType: {
|
||||
kind: "OBJECT",
|
||||
name: "SetCanRequestAdminPayload",
|
||||
ofType: null,
|
||||
},
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "input",
|
||||
type: {
|
||||
kind: "NON_NULL",
|
||||
ofType: {
|
||||
kind: "SCALAR",
|
||||
name: "Any",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "setDisplayName",
|
||||
type: {
|
||||
@@ -2140,6 +2163,22 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "OBJECT",
|
||||
name: "SetCanRequestAdminPayload",
|
||||
fields: [
|
||||
{
|
||||
name: "user",
|
||||
type: {
|
||||
kind: "OBJECT",
|
||||
name: "User",
|
||||
ofType: null,
|
||||
},
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
interfaces: [],
|
||||
},
|
||||
{
|
||||
kind: "OBJECT",
|
||||
name: "SetDisplayNamePayload",
|
||||
@@ -2623,6 +2662,17 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "canRequestAdmin",
|
||||
type: {
|
||||
kind: "NON_NULL",
|
||||
ofType: {
|
||||
kind: "SCALAR",
|
||||
name: "Any",
|
||||
},
|
||||
},
|
||||
args: [],
|
||||
},
|
||||
{
|
||||
name: "compatSessions",
|
||||
type: {
|
||||
|
@@ -11,6 +11,18 @@ allow {
|
||||
count(violation) == 0
|
||||
}
|
||||
|
||||
# Users can request admin scopes if either:
|
||||
# 1. They are in the admin_users list
|
||||
can_request_admin(user) {
|
||||
some admin_user in data.admin_users
|
||||
user.username == admin_user
|
||||
}
|
||||
|
||||
# 2. They have the can_request_admin flag set to true
|
||||
can_request_admin(user) {
|
||||
user.can_request_admin
|
||||
}
|
||||
|
||||
# Special case to make empty scope work
|
||||
allowed_scope("") = true
|
||||
|
||||
@@ -22,8 +34,7 @@ allowed_scope("email") = true
|
||||
allowed_scope("urn:synapse:admin:*") {
|
||||
# Synapse doesn't support user-less tokens yet, so access to the admin API can only be used with an authorization_code grant as the user is present
|
||||
input.grant_type == "authorization_code"
|
||||
some user in data.admin_users
|
||||
input.user.username == user
|
||||
can_request_admin(input.user)
|
||||
}
|
||||
|
||||
# This grants access to the /graphql API endpoint
|
||||
@@ -32,8 +43,7 @@ allowed_scope("urn:mas:graphql:*") = true
|
||||
# This makes it possible to query and do anything in the GraphQL API as an admin
|
||||
allowed_scope("urn:mas:admin") {
|
||||
input.grant_type == "authorization_code"
|
||||
some user in data.admin_users
|
||||
input.user.username == user
|
||||
can_request_admin(input.user)
|
||||
}
|
||||
|
||||
# This makes it possible to get the admin scope for clients that are allowed
|
||||
|
@@ -96,6 +96,20 @@ test_synapse_admin_scopes {
|
||||
with data.admin_users as []
|
||||
with input.grant_type as "authorization_code"
|
||||
with input.scope as "urn:synapse:admin:*"
|
||||
|
||||
allow with input.user as user
|
||||
with input.user.can_request_admin as true
|
||||
with input.client as client
|
||||
with data.admin_users as []
|
||||
with input.grant_type as "authorization_code"
|
||||
with input.scope as "urn:synapse:admin:*"
|
||||
|
||||
not allow with input.user as user
|
||||
with input.user.can_request_admin as false
|
||||
with input.client as client
|
||||
with data.admin_users as []
|
||||
with input.grant_type as "authorization_code"
|
||||
with input.scope as "urn:synapse:admin:*"
|
||||
}
|
||||
|
||||
test_mas_scopes {
|
||||
|
Reference in New Issue
Block a user