graphql: add mutation to update device name

This commit is contained in:
Quentin Gliech
2025-04-25 14:50:17 +02:00
parent 3b9d580b17
commit cf9d4599f9
4 changed files with 336 additions and 0 deletions

View File

@@ -64,6 +64,54 @@ impl EndCompatSessionPayload {
}
}
/// The input of the `setCompatSessionName` mutation.
#[derive(InputObject)]
pub struct SetCompatSessionNameInput {
/// The ID of the session to set the name of.
compat_session_id: ID,
/// The new name of the session.
human_name: String,
}
/// The payload of the `setCompatSessionName` mutation.
pub enum SetCompatSessionNamePayload {
/// The session was not found.
NotFound,
/// The session was updated.
Updated(mas_data_model::CompatSession),
}
/// The status of the `setCompatSessionName` mutation.
#[derive(Enum, Copy, Clone, PartialEq, Eq, Debug)]
enum SetCompatSessionNameStatus {
/// The session was updated.
Updated,
/// The session was not found.
NotFound,
}
#[Object]
impl SetCompatSessionNamePayload {
/// The status of the mutation.
async fn status(&self) -> SetCompatSessionNameStatus {
match self {
Self::Updated(_) => SetCompatSessionNameStatus::Updated,
Self::NotFound => SetCompatSessionNameStatus::NotFound,
}
}
/// The session that was updated.
async fn oauth2_session(&self) -> Option<CompatSession> {
match self {
Self::Updated(session) => Some(CompatSession::new(session.clone())),
Self::NotFound => None,
}
}
}
#[Object]
impl CompatSessionMutations {
async fn end_compat_session(
@@ -105,4 +153,50 @@ impl CompatSessionMutations {
Ok(EndCompatSessionPayload::Ended(Box::new(session)))
}
async fn set_compat_session_name(
&self,
ctx: &Context<'_>,
input: SetCompatSessionNameInput,
) -> Result<SetCompatSessionNamePayload, async_graphql::Error> {
let state = ctx.state();
let compat_session_id = NodeType::CompatSession.extract_ulid(&input.compat_session_id)?;
let requester = ctx.requester();
let mut repo = state.repository().await?;
let homeserver = state.homeserver_connection();
let session = repo.compat_session().lookup(compat_session_id).await?;
let Some(session) = session else {
return Ok(SetCompatSessionNamePayload::NotFound);
};
if !requester.is_owner_or_admin(&session) {
return Ok(SetCompatSessionNamePayload::NotFound);
}
let user = repo
.user()
.lookup(session.user_id)
.await?
.context("User not found")?;
let session = repo
.compat_session()
.set_human_name(session, Some(input.human_name.clone()))
.await?;
// Update the device on the homeserver side
let mxid = homeserver.mxid(&user.username);
if let Some(device) = session.device.as_ref() {
homeserver
.update_device_display_name(&mxid, device.as_str(), &input.human_name)
.await
.context("Failed to provision device")?;
}
repo.save().await?;
Ok(SetCompatSessionNamePayload::Updated(session))
}
}

View File

@@ -110,6 +110,54 @@ impl EndOAuth2SessionPayload {
}
}
/// The input of the `setOauth2SessionName` mutation.
#[derive(InputObject)]
pub struct SetOAuth2SessionNameInput {
/// The ID of the session to set the name of.
oauth2_session_id: ID,
/// The new name of the session.
human_name: String,
}
/// The payload of the `setOauth2SessionName` mutation.
pub enum SetOAuth2SessionNamePayload {
/// The session was not found.
NotFound,
/// The session was updated.
Updated(mas_data_model::Session),
}
/// The status of the `setOauth2SessionName` mutation.
#[derive(Enum, Copy, Clone, PartialEq, Eq, Debug)]
enum SetOAuth2SessionNameStatus {
/// The session was updated.
Updated,
/// The session was not found.
NotFound,
}
#[Object]
impl SetOAuth2SessionNamePayload {
/// The status of the mutation.
async fn status(&self) -> SetOAuth2SessionNameStatus {
match self {
Self::Updated(_) => SetOAuth2SessionNameStatus::Updated,
Self::NotFound => SetOAuth2SessionNameStatus::NotFound,
}
}
/// The session that was updated.
async fn oauth2_session(&self) -> Option<OAuth2Session> {
match self {
Self::Updated(session) => Some(OAuth2Session(session.clone())),
Self::NotFound => None,
}
}
}
#[Object]
impl OAuth2SessionMutations {
/// Create a new arbitrary OAuth 2.0 Session.
@@ -247,4 +295,54 @@ impl OAuth2SessionMutations {
Ok(EndOAuth2SessionPayload::Ended(session))
}
async fn set_oauth2_session_name(
&self,
ctx: &Context<'_>,
input: SetOAuth2SessionNameInput,
) -> Result<SetOAuth2SessionNamePayload, async_graphql::Error> {
let state = ctx.state();
let oauth2_session_id = NodeType::OAuth2Session.extract_ulid(&input.oauth2_session_id)?;
let requester = ctx.requester();
let mut repo = state.repository().await?;
let homeserver = state.homeserver_connection();
let session = repo.oauth2_session().lookup(oauth2_session_id).await?;
let Some(session) = session else {
return Ok(SetOAuth2SessionNamePayload::NotFound);
};
if !requester.is_owner_or_admin(&session) {
return Ok(SetOAuth2SessionNamePayload::NotFound);
}
let user_id = session.user_id.context("Session has no user")?;
let user = repo
.user()
.lookup(user_id)
.await?
.context("User not found")?;
let session = repo
.oauth2_session()
.set_human_name(session, Some(input.human_name.clone()))
.await?;
// Update the device on the homeserver side
let mxid = homeserver.mxid(&user.username);
for scope in &*session.scope {
if let Some(device) = Device::from_scope_token(scope) {
homeserver
.update_device_display_name(&mxid, device.as_str(), &input.human_name)
.await
.context("Failed to provision device")?;
}
}
repo.save().await?;
Ok(SetOAuth2SessionNamePayload::Updated(session))
}
}

View File

@@ -941,7 +941,13 @@ type Mutation {
input: CreateOAuth2SessionInput!
): CreateOAuth2SessionPayload!
endOauth2Session(input: EndOAuth2SessionInput!): EndOAuth2SessionPayload!
setOauth2SessionName(
input: SetOAuth2SessionNameInput!
): SetOAuth2SessionNamePayload!
endCompatSession(input: EndCompatSessionInput!): EndCompatSessionPayload!
setCompatSessionName(
input: SetCompatSessionNameInput!
): SetCompatSessionNamePayload!
endBrowserSession(input: EndBrowserSessionInput!): EndBrowserSessionPayload!
"""
Set the display name of a user
@@ -1434,6 +1440,45 @@ type SetCanRequestAdminPayload {
user: User
}
"""
The input of the `setCompatSessionName` mutation.
"""
input SetCompatSessionNameInput {
"""
The ID of the session to set the name of.
"""
compatSessionId: ID!
"""
The new name of the session.
"""
humanName: String!
}
type SetCompatSessionNamePayload {
"""
The status of the mutation.
"""
status: SetCompatSessionNameStatus!
"""
The session that was updated.
"""
oauth2Session: CompatSession
}
"""
The status of the `setCompatSessionName` mutation.
"""
enum SetCompatSessionNameStatus {
"""
The session was updated.
"""
UPDATED
"""
The session was not found.
"""
NOT_FOUND
}
"""
The input for the `addEmail` mutation
"""
@@ -1476,6 +1521,45 @@ enum SetDisplayNameStatus {
INVALID
}
"""
The input of the `setOauth2SessionName` mutation.
"""
input SetOAuth2SessionNameInput {
"""
The ID of the session to set the name of.
"""
oauth2SessionId: ID!
"""
The new name of the session.
"""
humanName: String!
}
type SetOAuth2SessionNamePayload {
"""
The status of the mutation.
"""
status: SetOAuth2SessionNameStatus!
"""
The session that was updated.
"""
oauth2Session: Oauth2Session
}
"""
The status of the `setOauth2SessionName` mutation.
"""
enum SetOAuth2SessionNameStatus {
"""
The session was updated.
"""
UPDATED
"""
The session was not found.
"""
NOT_FOUND
}
"""
The input for the `setPasswordByRecovery` mutation.
"""

View File

@@ -582,8 +582,10 @@ export type Mutation = {
* administrators.
*/
setCanRequestAdmin: SetCanRequestAdminPayload;
setCompatSessionName: SetCompatSessionNamePayload;
/** Set the display name of a user */
setDisplayName: SetDisplayNamePayload;
setOauth2SessionName: SetOAuth2SessionNamePayload;
/**
* Set the password for a user.
*
@@ -691,12 +693,24 @@ export type MutationSetCanRequestAdminArgs = {
};
/** The mutations root of the GraphQL interface. */
export type MutationSetCompatSessionNameArgs = {
input: SetCompatSessionNameInput;
};
/** The mutations root of the GraphQL interface. */
export type MutationSetDisplayNameArgs = {
input: SetDisplayNameInput;
};
/** The mutations root of the GraphQL interface. */
export type MutationSetOauth2SessionNameArgs = {
input: SetOAuth2SessionNameInput;
};
/** The mutations root of the GraphQL interface. */
export type MutationSetPasswordArgs = {
input: SetPasswordInput;
@@ -1086,6 +1100,29 @@ export type SetCanRequestAdminPayload = {
user?: Maybe<User>;
};
/** The input of the `setCompatSessionName` mutation. */
export type SetCompatSessionNameInput = {
/** The ID of the session to set the name of. */
compatSessionId: Scalars['ID']['input'];
/** The new name of the session. */
humanName: Scalars['String']['input'];
};
export type SetCompatSessionNamePayload = {
__typename?: 'SetCompatSessionNamePayload';
/** The session that was updated. */
oauth2Session?: Maybe<CompatSession>;
/** The status of the mutation. */
status: SetCompatSessionNameStatus;
};
/** The status of the `setCompatSessionName` mutation. */
export type SetCompatSessionNameStatus =
/** The session was not found. */
| 'NOT_FOUND'
/** The session was updated. */
| 'UPDATED';
/** The input for the `addEmail` mutation */
export type SetDisplayNameInput = {
/** The display name to set. If `None`, the display name will be removed. */
@@ -1110,6 +1147,29 @@ export type SetDisplayNameStatus =
/** The display name was set */
| 'SET';
/** The input of the `setOauth2SessionName` mutation. */
export type SetOAuth2SessionNameInput = {
/** The new name of the session. */
humanName: Scalars['String']['input'];
/** The ID of the session to set the name of. */
oauth2SessionId: Scalars['ID']['input'];
};
export type SetOAuth2SessionNamePayload = {
__typename?: 'SetOAuth2SessionNamePayload';
/** The session that was updated. */
oauth2Session?: Maybe<Oauth2Session>;
/** The status of the mutation. */
status: SetOAuth2SessionNameStatus;
};
/** The status of the `setOauth2SessionName` mutation. */
export type SetOAuth2SessionNameStatus =
/** The session was not found. */
| 'NOT_FOUND'
/** The session was updated. */
| 'UPDATED';
/** The input for the `setPasswordByRecovery` mutation. */
export type SetPasswordByRecoveryInput = {
/** The new password for the user. */