diff --git a/crates/handlers/src/graphql/mutations/compat_session.rs b/crates/handlers/src/graphql/mutations/compat_session.rs index 48bf23f81..3930b5670 100644 --- a/crates/handlers/src/graphql/mutations/compat_session.rs +++ b/crates/handlers/src/graphql/mutations/compat_session.rs @@ -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 { + 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 { + 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)) + } } diff --git a/crates/handlers/src/graphql/mutations/oauth2_session.rs b/crates/handlers/src/graphql/mutations/oauth2_session.rs index 058607536..1d0282014 100644 --- a/crates/handlers/src/graphql/mutations/oauth2_session.rs +++ b/crates/handlers/src/graphql/mutations/oauth2_session.rs @@ -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 { + 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 { + 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)) + } } diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 43499efbc..8018e4f5b 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -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. """ diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index 2f2d97e4a..d875ef2bf 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -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; }; +/** 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; + /** 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; + /** 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. */