mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-08-31 06:34:00 +00:00
feat: add option to allow non-local users to change usernames (#8714)
Add a new config option for OAuth2 authentication sources: allow users to change their username. In the case where OAuth2 is more like a social OAuth2 login there's no need to not allow users to change their username. The information how the user is linked to the authentication source is stored in different fields. Resolves forgejo/forgejo#687 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8714 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
parent
16a0c97fbf
commit
b51f97e97d
13 changed files with 119 additions and 17 deletions
|
@ -29,6 +29,7 @@ type Source struct {
|
|||
GroupTeamMapRemoval bool
|
||||
RestrictedGroup string
|
||||
SkipLocalTwoFA bool `json:",omitempty"`
|
||||
AllowUsernameChange bool
|
||||
|
||||
// reference to the authSource
|
||||
authSource *auth.Source
|
||||
|
|
|
@ -79,6 +79,7 @@ type AuthenticationForm struct {
|
|||
SkipLocalTwoFA bool
|
||||
GroupTeamMap string `binding:"ValidGroupTeamMap"`
|
||||
GroupTeamMapRemoval bool
|
||||
AllowUsernameChange bool
|
||||
}
|
||||
|
||||
// Validate validates fields
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"forgejo.org/models"
|
||||
asymkey_model "forgejo.org/models/asymkey"
|
||||
"forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/organization"
|
||||
packages_model "forgejo.org/models/packages"
|
||||
|
@ -25,6 +26,7 @@ import (
|
|||
"forgejo.org/modules/storage"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/services/agit"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
org_service "forgejo.org/services/org"
|
||||
"forgejo.org/services/packages"
|
||||
container_service "forgejo.org/services/packages/container"
|
||||
|
@ -49,9 +51,26 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe
|
|||
// Non-local users are not allowed to change their username.
|
||||
// If the doer is an admin, then allow the rename - they know better.
|
||||
if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() {
|
||||
return user_model.ErrUserIsNotLocal{
|
||||
UID: u.ID,
|
||||
Name: u.Name,
|
||||
// If the user's authentication source is OAuth2 and that source allows for
|
||||
// username changes then don't make a fuzz about it.
|
||||
|
||||
if !u.IsOAuth2() {
|
||||
return user_model.ErrUserIsNotLocal{
|
||||
UID: u.ID,
|
||||
Name: u.Name,
|
||||
}
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(ctx, u.LoginSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sourceCfg := source.Cfg.(*oauth2.Source)
|
||||
if !sourceCfg.AllowUsernameChange {
|
||||
return user_model.ErrUserIsNotLocal{
|
||||
UID: u.ID,
|
||||
Name: u.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/modules/timeutil"
|
||||
"forgejo.org/services/auth/source/oauth2"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -140,23 +141,10 @@ func TestCreateUser(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRenameUser(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/user/fixtures/")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21})
|
||||
|
||||
t.Run("Non-Local", func(t *testing.T) {
|
||||
u := &user_model.User{
|
||||
ID: 2,
|
||||
Name: "old-name",
|
||||
Type: user_model.UserTypeIndividual,
|
||||
LoginType: auth.OAuth2,
|
||||
}
|
||||
require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename2"), user_model.ErrUserIsNotLocal{UID: 2, Name: "old-name"})
|
||||
|
||||
t.Run("Admin", func(t *testing.T) {
|
||||
require.NoError(t, AdminRenameUser(t.Context(), u, "user_rename2"))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Same username", func(t *testing.T) {
|
||||
require.NoError(t, RenameUser(db.DefaultContext, user, user.Name))
|
||||
})
|
||||
|
@ -225,6 +213,30 @@ func TestRenameUser(t *testing.T) {
|
|||
unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-1"})
|
||||
unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-2"})
|
||||
})
|
||||
|
||||
t.Run("Non-local", func(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1041, LoginSource: 1001})
|
||||
authSource := unittest.AssertExistsAndLoadBean(t, &auth.Source{ID: user.LoginSource})
|
||||
assert.False(t, user.IsLocal())
|
||||
assert.True(t, user.IsOAuth2())
|
||||
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
require.NoError(t, RenameUser(t.Context(), user, "I-am-a-local-username"))
|
||||
})
|
||||
|
||||
t.Run("Not allowed", func(t *testing.T) {
|
||||
authSourceCfg := authSource.Cfg.(*oauth2.Source)
|
||||
authSourceCfg.AllowUsernameChange = false
|
||||
authSource.Cfg = authSourceCfg
|
||||
_, err := db.GetEngine(t.Context()).Cols("cfg").ID(authSource.ID).Update(authSource)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.ErrorIs(t, RenameUser(t.Context(), user, "Another-username-change"), user_model.ErrUserIsNotLocal{UID: user.ID, Name: user.Name})
|
||||
t.Run("Admin", func(t *testing.T) {
|
||||
require.NoError(t, AdminRenameUser(t.Context(), user, "Another-username-change"))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateUser_Issue5882(t *testing.T) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue