From ce3b8aacf73b841887f3eec631851d086a7578f1 Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:21:57 +0200 Subject: [PATCH] [chore] Warn about email/password change when using OIDC (#2975) * [chore] Warn about email/password change when using OIDC * go fmt --- docs/api/swagger.yaml | 10 +++ docs/user_guide/settings.md | 8 ++- internal/api/client/user/passwordchange.go | 11 ++++ internal/api/model/instancev1.go | 2 + internal/api/model/instancev2.go | 2 + internal/config/helpers.gen.go | 3 +- internal/typeutils/internaltofrontend.go | 2 + web/source/settings/lib/types/instance.ts | 1 + web/source/settings/views/user/settings.tsx | 72 +++++++++++++++++++-- 9 files changed, 101 insertions(+), 10 deletions(-) diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 6bb5b45ea..fe28847f7 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -1541,6 +1541,10 @@ definitions: $ref: '#/definitions/InstanceConfigurationEmojis' media_attachments: $ref: '#/definitions/instanceConfigurationMediaAttachments' + oidc_enabled: + description: True if instance is running with OIDC as auth/identity backend, else omitted. + type: boolean + x-go-name: OIDCEnabled polls: $ref: '#/definitions/instanceConfigurationPolls' statuses: @@ -1656,6 +1660,10 @@ definitions: $ref: '#/definitions/InstanceConfigurationEmojis' media_attachments: $ref: '#/definitions/instanceConfigurationMediaAttachments' + oidc_enabled: + description: True if instance is running with OIDC as auth/identity backend, else omitted. + type: boolean + x-go-name: OIDCEnabled polls: $ref: '#/definitions/instanceConfigurationPolls' statuses: @@ -9044,6 +9052,8 @@ paths: description: forbidden "406": description: not acceptable + "422": + description: unprocessable request because instance is running with OIDC backend "500": description: internal error security: diff --git a/docs/user_guide/settings.md b/docs/user_guide/settings.md index 1d0ec177d..1b243f166 100644 --- a/docs/user_guide/settings.md +++ b/docs/user_guide/settings.md @@ -157,13 +157,19 @@ When you are finished updating your post settings, remember to click the `Save p You can use the Password Change section of the panel to set a new password for your account. For security reasons, you must provide your current password to validate the change. +!!! info + If your instance is using OIDC as its authorization/identity provider, you will not be able to change your password via the GoToSocial settings panel, and you should contact your OIDC provider instead. + For more information on the way GoToSocial manages passwords, please see the [Password management document](./password_management.md). ### Email Change You can use the Email Change section of the panel to change the email address for your account. For security reasons, you must provide your current password to validate the change. -Once a new email address has been entered, and you have clicked "Change email address", you must open the inbox of the new email address and confirm your address via the link provided. Once you've done that, your email address change will be confirmed, and you should use the new email address to log in. +Once a new email address has been entered, and you have clicked "Change email address", you must open the inbox of the new email address and confirm your address via the link provided. Once you've done that, your email address change will be confirmed. + +!!! info + If your instance is using OIDC as its authorization/identity provider, you will be able to change your email address via the settings panel, but it will only affect the email address GoToSocial uses to contact you, it will not change the email address you need to use to log in to your account. To change that, you should contact your OIDC provider. ## Migration diff --git a/internal/api/client/user/passwordchange.go b/internal/api/client/user/passwordchange.go index c2928e9e5..df9f5b0c8 100644 --- a/internal/api/client/user/passwordchange.go +++ b/internal/api/client/user/passwordchange.go @@ -24,10 +24,13 @@ import ( "github.com/gin-gonic/gin" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) +const OIDCPasswordHelp = "password change request cannot be processed by GoToSocial as this instance is running with OIDC enabled; you must change password using your OIDC provider" + // PasswordChangePOSTHandler swagger:operation POST /api/v1/user/password_change userPasswordChange // // Change the password of authenticated user. @@ -62,6 +65,8 @@ import ( // description: forbidden // '406': // description: not acceptable +// '422': +// description: unprocessable request because instance is running with OIDC backend // '500': // description: internal error func (m *Module) PasswordChangePOSTHandler(c *gin.Context) { @@ -76,6 +81,12 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) { return } + if config.GetOIDCEnabled() { + err := errors.New("instance running with OIDC") + apiutil.ErrorHandler(c, gtserror.NewErrorUnprocessableEntity(err, OIDCPasswordHelp), m.processor.InstanceGetV1) + return + } + form := &apimodel.PasswordChangeRequest{} if err := c.ShouldBind(form); err != nil { apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) diff --git a/internal/api/model/instancev1.go b/internal/api/model/instancev1.go index 217edc08c..beb4f430d 100644 --- a/internal/api/model/instancev1.go +++ b/internal/api/model/instancev1.go @@ -127,4 +127,6 @@ type InstanceV1Configuration struct { Accounts InstanceConfigurationAccounts `json:"accounts"` // Instance configuration pertaining to emojis. Emojis InstanceConfigurationEmojis `json:"emojis"` + // True if instance is running with OIDC as auth/identity backend, else omitted. + OIDCEnabled bool `json:"oidc_enabled,omitempty"` } diff --git a/internal/api/model/instancev2.go b/internal/api/model/instancev2.go index a1b98ea65..fce801117 100644 --- a/internal/api/model/instancev2.go +++ b/internal/api/model/instancev2.go @@ -163,6 +163,8 @@ type InstanceV2Configuration struct { Translation InstanceV2ConfigurationTranslation `json:"translation"` // Instance configuration pertaining to emojis. Emojis InstanceConfigurationEmojis `json:"emojis"` + // True if instance is running with OIDC as auth/identity backend, else omitted. + OIDCEnabled bool `json:"oidc_enabled,omitempty"` } // Information about registering for this instance. diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index edfe96e57..71a77e753 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -2,7 +2,7 @@ // GoToSocial // Copyright (C) GoToSocial Authors admin@gotosocial.org // SPDX-License-Identifier: AGPL-3.0-or-later -// +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or @@ -4074,4 +4074,3 @@ func GetRequestIDHeader() string { return global.GetRequestIDHeader() } // SetRequestIDHeader safely sets the value for global configuration 'RequestIDHeader' field func SetRequestIDHeader(v string) { global.SetRequestIDHeader(v) } - diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index f3c027316..80f083ef1 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -1328,6 +1328,7 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) + instance.Configuration.OIDCEnabled = config.GetOIDCEnabled() // URLs instance.URLs.StreamingAPI = "wss://" + i.Domain @@ -1467,6 +1468,7 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) + instance.Configuration.OIDCEnabled = config.GetOIDCEnabled() // registrations instance.Registrations.Enabled = config.GetAccountsRegistrationOpen() diff --git a/web/source/settings/lib/types/instance.ts b/web/source/settings/lib/types/instance.ts index 4c6f5061b..11f75032c 100644 --- a/web/source/settings/lib/types/instance.ts +++ b/web/source/settings/lib/types/instance.ts @@ -49,6 +49,7 @@ export interface InstanceConfiguration { polls: InstancePolls; accounts: InstanceAccounts; emojis: InstanceEmojis; + oidc_enabled?: boolean; } export interface InstanceAccounts { diff --git a/web/source/settings/views/user/settings.tsx b/web/source/settings/views/user/settings.tsx index a27cc1ba3..5696144a0 100644 --- a/web/source/settings/views/user/settings.tsx +++ b/web/source/settings/views/user/settings.tsx @@ -28,6 +28,7 @@ import { useVerifyCredentialsQuery } from "../../lib/query/oauth"; import { useEmailChangeMutation, usePasswordChangeMutation, useUpdateCredentialsMutation, useUserQuery } from "../../lib/query/user"; import Loading from "../../components/loading"; import { User } from "../../lib/types/user"; +import { useInstanceV1Query } from "../../lib/query/gts-api"; export default function UserSettings() { return ( @@ -106,6 +107,24 @@ function UserSettingsForm({ data }) { } function PasswordChange() { + // Load instance data. + const { + data: instance, + isFetching: isFetchingInstance, + isLoading: isLoadingInstance + } = useInstanceV1Query(); + if (isFetchingInstance || isLoadingInstance) { + return ; + } + + if (instance === undefined) { + throw "could not fetch instance"; + } + + return ; +} + +function PasswordChangeForm({ oidcEnabled }: { oidcEnabled?: boolean }) { const form = { oldPassword: useTextInput("old_password"), newPassword: useTextInput("new_password", { @@ -133,6 +152,13 @@ function PasswordChange() {

Change Password

+ { oidcEnabled &&

+ This instance is running with OIDC as its authorization + identity provider. +
+ This means you cannot change your password using this settings panel. +
+ To change your password, you should instead contact your OIDC provider. +

}
+ ); } function EmailChange() { - // Load existing user data. - const { data: user, isFetching, isLoading } = useUserQuery(); - if (isFetching || isLoading) { + // Load instance data. + const { + data: instance, + isFetching: isFetchingInstance, + isLoading: isLoadingInstance + } = useInstanceV1Query(); + + // Load user data. + const { + data: user, + isFetching: isFetchingUser, + isLoading: isLoadingUser + } = useUserQuery(); + + if ( + (isFetchingInstance || isLoadingInstance) || + (isFetchingUser || isLoadingUser) + ) { return ; } @@ -183,10 +228,14 @@ function EmailChange() { throw "could not fetch user"; } - return ; + if (instance === undefined) { + throw "could not fetch instance"; + } + + return ; } -function EmailChangeForm({user}: {user: User}) { +function EmailChangeForm({user, oidcEnabled}: { user: User, oidcEnabled?: boolean }) { const form = { currentEmail: useTextInput("current_email", { defaultValue: user.email, @@ -217,6 +266,15 @@ function EmailChangeForm({user}: {user: User}) {

Change Email

+ { oidcEnabled &&

+ This instance is running with OIDC as its authorization + identity provider. +
+ You can still change your email address using this settings panel, + but it will only affect which address GoToSocial uses to contact you, + not the email address you use to log in. +
+ To change the email address you use to log in, contact your OIDC provider. +

}
- { user.unconfirmed_email && <> + { (user.unconfirmed_email && user.unconfirmed_email !== user.email) && <>