[chore] Warn about email/password change when using OIDC (#2975)

* [chore] Warn about email/password change when using OIDC

* go fmt
This commit is contained in:
tobi 2024-06-07 16:21:57 +02:00 committed by GitHub
parent cc4f773b0e
commit ce3b8aacf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 101 additions and 10 deletions

View file

@ -1541,6 +1541,10 @@ definitions:
$ref: '#/definitions/InstanceConfigurationEmojis' $ref: '#/definitions/InstanceConfigurationEmojis'
media_attachments: media_attachments:
$ref: '#/definitions/instanceConfigurationMediaAttachments' $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: polls:
$ref: '#/definitions/instanceConfigurationPolls' $ref: '#/definitions/instanceConfigurationPolls'
statuses: statuses:
@ -1656,6 +1660,10 @@ definitions:
$ref: '#/definitions/InstanceConfigurationEmojis' $ref: '#/definitions/InstanceConfigurationEmojis'
media_attachments: media_attachments:
$ref: '#/definitions/instanceConfigurationMediaAttachments' $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: polls:
$ref: '#/definitions/instanceConfigurationPolls' $ref: '#/definitions/instanceConfigurationPolls'
statuses: statuses:
@ -9044,6 +9052,8 @@ paths:
description: forbidden description: forbidden
"406": "406":
description: not acceptable description: not acceptable
"422":
description: unprocessable request because instance is running with OIDC backend
"500": "500":
description: internal error description: internal error
security: security:

View file

@ -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. 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). For more information on the way GoToSocial manages passwords, please see the [Password management document](./password_management.md).
### Email Change ### 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. 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 ## Migration

View file

@ -24,10 +24,13 @@
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 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/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth" "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 // PasswordChangePOSTHandler swagger:operation POST /api/v1/user/password_change userPasswordChange
// //
// Change the password of authenticated user. // Change the password of authenticated user.
@ -62,6 +65,8 @@
// description: forbidden // description: forbidden
// '406': // '406':
// description: not acceptable // description: not acceptable
// '422':
// description: unprocessable request because instance is running with OIDC backend
// '500': // '500':
// description: internal error // description: internal error
func (m *Module) PasswordChangePOSTHandler(c *gin.Context) { func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
@ -76,6 +81,12 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
return 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{} form := &apimodel.PasswordChangeRequest{}
if err := c.ShouldBind(form); err != nil { if err := c.ShouldBind(form); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)

View file

@ -127,4 +127,6 @@ type InstanceV1Configuration struct {
Accounts InstanceConfigurationAccounts `json:"accounts"` Accounts InstanceConfigurationAccounts `json:"accounts"`
// Instance configuration pertaining to emojis. // Instance configuration pertaining to emojis.
Emojis InstanceConfigurationEmojis `json:"emojis"` Emojis InstanceConfigurationEmojis `json:"emojis"`
// True if instance is running with OIDC as auth/identity backend, else omitted.
OIDCEnabled bool `json:"oidc_enabled,omitempty"`
} }

View file

@ -163,6 +163,8 @@ type InstanceV2Configuration struct {
Translation InstanceV2ConfigurationTranslation `json:"translation"` Translation InstanceV2ConfigurationTranslation `json:"translation"`
// Instance configuration pertaining to emojis. // Instance configuration pertaining to emojis.
Emojis InstanceConfigurationEmojis `json:"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. // Information about registering for this instance.

View file

@ -2,7 +2,7 @@
// GoToSocial // GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org // Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
// //
// This program is free software: you can redistribute it and/or modify // 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 // 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 // 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 // SetRequestIDHeader safely sets the value for global configuration 'RequestIDHeader' field
func SetRequestIDHeader(v string) { global.SetRequestIDHeader(v) } func SetRequestIDHeader(v string) { global.SetRequestIDHeader(v) }

View file

@ -1328,6 +1328,7 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
// URLs // URLs
instance.URLs.StreamingAPI = "wss://" + i.Domain 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.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
// registrations // registrations
instance.Registrations.Enabled = config.GetAccountsRegistrationOpen() instance.Registrations.Enabled = config.GetAccountsRegistrationOpen()

View file

@ -49,6 +49,7 @@ export interface InstanceConfiguration {
polls: InstancePolls; polls: InstancePolls;
accounts: InstanceAccounts; accounts: InstanceAccounts;
emojis: InstanceEmojis; emojis: InstanceEmojis;
oidc_enabled?: boolean;
} }
export interface InstanceAccounts { export interface InstanceAccounts {

View file

@ -28,6 +28,7 @@ import { useVerifyCredentialsQuery } from "../../lib/query/oauth";
import { useEmailChangeMutation, usePasswordChangeMutation, useUpdateCredentialsMutation, useUserQuery } from "../../lib/query/user"; import { useEmailChangeMutation, usePasswordChangeMutation, useUpdateCredentialsMutation, useUserQuery } from "../../lib/query/user";
import Loading from "../../components/loading"; import Loading from "../../components/loading";
import { User } from "../../lib/types/user"; import { User } from "../../lib/types/user";
import { useInstanceV1Query } from "../../lib/query/gts-api";
export default function UserSettings() { export default function UserSettings() {
return ( return (
@ -106,6 +107,24 @@ function UserSettingsForm({ data }) {
} }
function PasswordChange() { function PasswordChange() {
// Load instance data.
const {
data: instance,
isFetching: isFetchingInstance,
isLoading: isLoadingInstance
} = useInstanceV1Query();
if (isFetchingInstance || isLoadingInstance) {
return <Loading />;
}
if (instance === undefined) {
throw "could not fetch instance";
}
return <PasswordChangeForm oidcEnabled={instance.configuration.oidc_enabled} />;
}
function PasswordChangeForm({ oidcEnabled }: { oidcEnabled?: boolean }) {
const form = { const form = {
oldPassword: useTextInput("old_password"), oldPassword: useTextInput("old_password"),
newPassword: useTextInput("new_password", { newPassword: useTextInput("new_password", {
@ -133,6 +152,13 @@ function PasswordChange() {
<form className="change-password" onSubmit={submitForm}> <form className="change-password" onSubmit={submitForm}>
<div className="form-section-docs"> <div className="form-section-docs">
<h3>Change Password</h3> <h3>Change Password</h3>
{ oidcEnabled && <p>
This instance is running with OIDC as its authorization + identity provider.
<br/>
This means <strong>you cannot change your password using this settings panel</strong>.
<br/>
To change your password, you should instead contact your OIDC provider.
</p> }
<a <a
href="https://docs.gotosocial.org/en/latest/user_guide/settings/#password-change" href="https://docs.gotosocial.org/en/latest/user_guide/settings/#password-change"
target="_blank" target="_blank"
@ -142,12 +168,14 @@ function PasswordChange() {
Learn more about this (opens in a new tab) Learn more about this (opens in a new tab)
</a> </a>
</div> </div>
<TextInput <TextInput
type="password" type="password"
name="password" name="password"
field={form.oldPassword} field={form.oldPassword}
label="Current password" label="Current password"
autoComplete="current-password" autoComplete="current-password"
disabled={oidcEnabled}
/> />
<TextInput <TextInput
type="password" type="password"
@ -155,6 +183,7 @@ function PasswordChange() {
field={form.newPassword} field={form.newPassword}
label="New password" label="New password"
autoComplete="new-password" autoComplete="new-password"
disabled={oidcEnabled}
/> />
<TextInput <TextInput
type="password" type="password"
@ -162,20 +191,36 @@ function PasswordChange() {
field={verifyNewPassword} field={verifyNewPassword}
label="Confirm new password" label="Confirm new password"
autoComplete="new-password" autoComplete="new-password"
disabled={oidcEnabled}
/> />
<MutationButton <MutationButton
disabled={false}
label="Change password" label="Change password"
result={result} result={result}
disabled={oidcEnabled ?? false}
/> />
</form> </form>
); );
} }
function EmailChange() { function EmailChange() {
// Load existing user data. // Load instance data.
const { data: user, isFetching, isLoading } = useUserQuery(); const {
if (isFetching || isLoading) { data: instance,
isFetching: isFetchingInstance,
isLoading: isLoadingInstance
} = useInstanceV1Query();
// Load user data.
const {
data: user,
isFetching: isFetchingUser,
isLoading: isLoadingUser
} = useUserQuery();
if (
(isFetchingInstance || isLoadingInstance) ||
(isFetchingUser || isLoadingUser)
) {
return <Loading />; return <Loading />;
} }
@ -183,10 +228,14 @@ function EmailChange() {
throw "could not fetch user"; throw "could not fetch user";
} }
return <EmailChangeForm user={user} />; if (instance === undefined) {
throw "could not fetch instance";
}
return <EmailChangeForm user={user} oidcEnabled={instance.configuration.oidc_enabled} />;
} }
function EmailChangeForm({user}: {user: User}) { function EmailChangeForm({user, oidcEnabled}: { user: User, oidcEnabled?: boolean }) {
const form = { const form = {
currentEmail: useTextInput("current_email", { currentEmail: useTextInput("current_email", {
defaultValue: user.email, defaultValue: user.email,
@ -217,6 +266,15 @@ function EmailChangeForm({user}: {user: User}) {
<form className="change-email" onSubmit={submitForm}> <form className="change-email" onSubmit={submitForm}>
<div className="form-section-docs"> <div className="form-section-docs">
<h3>Change Email</h3> <h3>Change Email</h3>
{ oidcEnabled && <p>
This instance is running with OIDC as its authorization + identity provider.
<br/>
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.
<br/>
To change the email address you use to log in, contact your OIDC provider.
</p> }
<a <a
href="https://docs.gotosocial.org/en/latest/user_guide/settings/#email-change" href="https://docs.gotosocial.org/en/latest/user_guide/settings/#email-change"
target="_blank" target="_blank"
@ -227,7 +285,7 @@ function EmailChangeForm({user}: {user: User}) {
</a> </a>
</div> </div>
{ user.unconfirmed_email && <> { (user.unconfirmed_email && user.unconfirmed_email !== user.email) && <>
<div className="info"> <div className="info">
<i className="fa fa-fw fa-info-circle" aria-hidden="true"></i> <i className="fa fa-fw fa-info-circle" aria-hidden="true"></i>
<b> <b>