Skip to content

Allow to disable local user management #13068

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func runChangePassword(c *cli.Context) error {
}
user.HashPassword(c.String("password"))

if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
if err := models.UpdateUserCols(user, false, "passwd", "salt"); err != nil {
return err
}

Expand Down
2 changes: 2 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@ EMAIL_DOMAIN_WHITELIST=
DISABLE_REGISTRATION = false
; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
; Disable local user management (i.e. when user data and password comes from LDAP and should not be changed locally in gitea).
DISABLE_LOCAL_USER_MANAGEMENT = false
; User must sign in to view anything.
REQUIRE_SIGNIN_VIEW = false
; Mail notification
Expand Down
1 change: 1 addition & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ set name for unique queues. Individual queues will default to
- `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private".
- `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
- `DISABLE_LOCAL_USER_MANAGEMENT`: **false** Set to true to disable local user management in gitea (i.e. when users are managed in LDAP).
- `NO_REPLY_ADDRESS`: **DOMAIN** Default value for the domain part of the user's email address in the git log if he has set KeepEmailPrivate to true.
The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.

Expand Down
4 changes: 2 additions & 2 deletions models/login_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use
cols = append(cols, "is_restricted")
}
if len(cols) > 0 {
err = UpdateUserCols(user, cols...)
err = UpdateUserCols(user, false, cols...)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -768,7 +768,7 @@ func UserSignIn(username, password string) (*User, error) {
// Update password hash if server password hash algorithm have changed
if user.PasswdHashAlgo != setting.PasswordHashAlgo {
user.HashPassword(password)
if err := UpdateUserCols(user, "passwd", "passwd_hash_algo"); err != nil {
if err := UpdateUserCols(user, false, "passwd", "passwd_hash_algo"); err != nil {
return nil, err
}
}
Expand Down
2 changes: 1 addition & 1 deletion models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO

// Remember visibility preference.
u.LastRepoVisibility = repo.IsPrivate
if err = updateUserCols(ctx.e, u, "last_repo_visibility"); err != nil {
if err = updateUserCols(ctx.e, u, false, "last_repo_visibility"); err != nil {
return fmt.Errorf("updateUser: %v", err)
}

Expand Down
57 changes: 49 additions & 8 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ func (u *User) SetLastLogin() {
// UpdateDiffViewStyle updates the users diff view style
func (u *User) UpdateDiffViewStyle(style string) error {
u.DiffViewStyle = style
return UpdateUserCols(u, "diff_view_style")
return UpdateUserCols(u, false, "diff_view_style")
}

// UpdateTheme updates a users' theme irrespective of the site wide theme
func (u *User) UpdateTheme(themeName string) error {
u.Theme = themeName
return UpdateUserCols(u, "theme")
return UpdateUserCols(u, false, "theme")
}

// GetEmail returns an noreply email, if the user has set to keep his
Expand Down Expand Up @@ -824,7 +824,7 @@ func (u *User) EmailNotifications() string {
// SetEmailNotifications sets the user's email notification preference
func (u *User) SetEmailNotifications(set string) error {
u.EmailNotificationsPreference = set
if err := UpdateUserCols(u, "email_notifications_preference"); err != nil {
if err := UpdateUserCols(u, false, "email_notifications_preference"); err != nil {
log.Error("SetEmailNotifications: %v", err)
return err
}
Expand Down Expand Up @@ -1126,7 +1126,43 @@ func checkDupEmail(e Engine, u *User) error {
return nil
}

// updateUserAllowed is used to block updating selected user fields when local user managemement is disabled.
func updateUserAllowed(u *User) error {
// Don't allow changes of selected user fields if local user management is disabled.
if setting.Service.DisableLocalUserManagement && (u.Type == UserTypeIndividual) {
if currUser, err := GetUserByID(u.ID); err == nil {
if currUser.Name != u.Name {
return fmt.Errorf("cannot change user %s username; local user management disabled", u.Name)
}
if (currUser.LoginSource != u.LoginSource) || (currUser.LoginName != u.LoginName) {
return fmt.Errorf("cannot change user %s login; local user management disabled", u.Name)
}
if currUser.FullName != u.FullName {
return fmt.Errorf("cannot change user %s full name; local user management disabled", u.Name)
}
if currUser.Email != u.Email {
return fmt.Errorf("cannot change user %s e-mail; local user management disabled", u.Name)
}
if (currUser.Passwd != u.Passwd) || (currUser.PasswdHashAlgo != u.PasswdHashAlgo) {
return fmt.Errorf("cannot change user %s password; local user management disabled", u.Name)
}
if currUser.IsActive != u.IsActive {
return fmt.Errorf("cannot change user %s activity; local user management disabled", u.Name)
}
if currUser.IsAdmin != u.IsAdmin {
return fmt.Errorf("cannot change user %s admin permission; local user management disabled", u.Name)
}
} else {
return err
}
}
return nil
}

func updateUser(e Engine, u *User) error {
if err := updateUserAllowed(u); err != nil {
return err
}
_, err := e.ID(u.ID).AllCols().Update(u)
return err
}
Expand All @@ -1137,11 +1173,16 @@ func UpdateUser(u *User) error {
}

// UpdateUserCols update user according special columns
func UpdateUserCols(u *User, cols ...string) error {
return updateUserCols(x, u, cols...)
func UpdateUserCols(u *User, force bool, cols ...string) error {
return updateUserCols(x, u, force, cols...)
}

func updateUserCols(e Engine, u *User, cols ...string) error {
func updateUserCols(e Engine, u *User, force bool, cols ...string) error {
if !force {
if err := updateUserAllowed(u); err != nil {
return err
}
}
_, err := e.ID(u.ID).Cols(cols...).Update(u)
return err
}
Expand Down Expand Up @@ -1982,7 +2023,7 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error {
}
usr.IsActive = true

err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_restricted", "is_active")
err = UpdateUserCols(usr, true, "full_name", "email", "is_admin", "is_restricted", "is_active")
if err != nil {
log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err)
}
Expand Down Expand Up @@ -2019,7 +2060,7 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error {
log.Trace("SyncExternalUsers[%s]: Deactivating user %s", s.Name, usr.Name)

usr.IsActive = false
err = UpdateUserCols(usr, "is_active")
err = UpdateUserCols(usr, true, "is_active")
if err != nil {
log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", s.Name, usr.Name, err)
}
Expand Down
4 changes: 2 additions & 2 deletions models/user_mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (email *EmailAddress) updateActivation(e Engine, activate bool) error {
if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil {
return err
}
return updateUserCols(e, user, "rands")
return updateUserCols(e, user, false, "rands")
}

// DeleteEmailAddress deletes an email address of given user.
Expand Down Expand Up @@ -448,7 +448,7 @@ func ActivateUserEmail(userID int64, email string, primary, activate bool) (err
if user.Rands, err = GetUserSalt(); err != nil {
return fmt.Errorf("generate salt: %v", err)
}
if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil {
if err = updateUserCols(sess, &user, false, "is_active", "rands"); err != nil {
return fmt.Errorf("updateUserCols(): %v", err)
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion modules/auth/sso/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func handleSignIn(ctx *macaron.Context, sess session.Store, user *models.User) {
// If the user does not have a locale set, we save the current one.
if len(user.Language) == 0 {
user.Language = ctx.Locale.Language()
if err := models.UpdateUserCols(user, "language"); err != nil {
if err := models.UpdateUserCols(user, false, "language"); err != nil {
log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", user.ID, user.Language))
return
}
Expand Down
1 change: 1 addition & 0 deletions modules/auth/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type InstallForm struct {
EnableOpenIDSignUp bool
DisableRegistration bool
AllowOnlyExternalRegistration bool
DisableLocalUserManagement bool
EnableCaptcha bool
RequireSignInView bool
DefaultKeepEmailPrivate bool
Expand Down
2 changes: 2 additions & 0 deletions modules/setting/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var Service struct {
EmailDomainWhitelist []string
DisableRegistration bool
AllowOnlyExternalRegistration bool
DisableLocalUserManagement bool
ShowRegistrationButton bool
ShowMilestonesDashboardPage bool
RequireSignInView bool
Expand Down Expand Up @@ -61,6 +62,7 @@ func newService() {
Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
Service.AllowOnlyExternalRegistration = sec.Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").MustBool()
Service.DisableLocalUserManagement = sec.Key("DISABLE_LOCAL_USER_MANAGEMENT").MustBool()
Service.EmailDomainWhitelist = sec.Key("EMAIL_DOMAIN_WHITELIST").Strings(",")
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true)
Expand Down
3 changes: 3 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ func NewFuncMap() []template.FuncMap {
"DisableImportLocal": func() bool {
return !setting.ImportLocalPaths
},
"DisableLocalUserManagement": func() bool {
return setting.Service.DisableLocalUserManagement
},
"TrN": TrN,
"Dict": func(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
Expand Down
30 changes: 30 additions & 0 deletions routers/admin/auths.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func Authentications(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminAuthentications"] = true

// No access to this page if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("Authentications", fmt.Errorf("access to /admin/auths page denied; local user management disabled"))
return
}

var err error
ctx.Data["Sources"], err = models.LoginSources()
if err != nil {
Expand Down Expand Up @@ -96,6 +102,12 @@ func NewAuthSource(ctx *context.Context) {
ctx.Data["SSPISeparatorReplacement"] = "_"
ctx.Data["SSPIDefaultLanguage"] = ""

// No access to this page if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("NewAuthSource", fmt.Errorf("access to /admin/auths/new page denied; local user management disabled"))
return
}

// only the first as default
for key := range models.OAuth2Providers {
ctx.Data["oauth2_provider"] = key
Expand Down Expand Up @@ -218,6 +230,12 @@ func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
ctx.Data["SSPISeparatorReplacement"] = "_"
ctx.Data["SSPIDefaultLanguage"] = ""

// Don't allow to create auth source if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("NewAuthSourcePost", fmt.Errorf("cannot create auth source; local user management disabled"))
return
}

hasTLS := false
var config convert.Conversion
switch models.LoginType(form.Type) {
Expand Down Expand Up @@ -290,6 +308,12 @@ func EditAuthSource(ctx *context.Context) {
ctx.Data["OAuth2Providers"] = models.OAuth2Providers
ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings

// No access to this page if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("EditAuthSource", fmt.Errorf("access to /admin/auths page denied; local user management disabled"))
return
}

source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
if err != nil {
ctx.ServerError("GetLoginSourceByID", err)
Expand All @@ -314,6 +338,12 @@ func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
ctx.Data["OAuth2Providers"] = models.OAuth2Providers
ctx.Data["OAuth2DefaultCustomURLMappings"] = models.OAuth2DefaultCustomURLMappings

// Don't allow to update auth source if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("EditAuthSourcePost", fmt.Errorf("cannot update auth source; local user management disabled"))
return
}

source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
if err != nil {
ctx.ServerError("GetLoginSourceByID", err)
Expand Down
13 changes: 13 additions & 0 deletions routers/admin/emails.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package admin

import (
"bytes"
"fmt"
"net/url"

"code.gitea.io/gitea/models"
Expand All @@ -28,6 +29,12 @@ func Emails(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminEmails"] = true

// No access to this page if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("Emails", fmt.Errorf("access to /admin/emails page denied; local user management disabled"))
return
}

opts := &models.SearchEmailOptions{
ListOptions: models.ListOptions{
PageSize: setting.UI.Admin.UserPagingNum,
Expand Down Expand Up @@ -112,6 +119,12 @@ func isKeywordValid(keyword string) bool {
// ActivateEmail serves a POST request for activating/deactivating a user's email
func ActivateEmail(ctx *context.Context) {

// Don't allow to activate/deactivate emails if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("ActivateEmail", fmt.Errorf("cannot activate email; local user management disabled"))
return
}

truefalse := map[string]bool{"1": true, "0": false}

uid := com.StrTo(ctx.Query("uid")).MustInt64()
Expand Down
18 changes: 18 additions & 0 deletions routers/admin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package admin

import (
"fmt"
"strings"

"code.gitea.io/gitea/models"
Expand Down Expand Up @@ -50,6 +51,12 @@ func NewUser(ctx *context.Context) {

ctx.Data["login_type"] = "0-0"

// No access to this page if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("NewUser", fmt.Errorf("access to /admin/users/new page denied; local user management disabled"))
return
}

sources, err := models.LoginSources()
if err != nil {
ctx.ServerError("LoginSources", err)
Expand All @@ -67,6 +74,12 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminUsers"] = true

// Don't allow to create users if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("NewUserPost", fmt.Errorf("cannot create new user; local user management disabled"))
return
}

sources, err := models.LoginSources()
if err != nil {
ctx.ServerError("LoginSources", err)
Expand Down Expand Up @@ -226,6 +239,11 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {

if len(form.Password) > 0 {
var err error
// Don't allow password changes if local user management is disabled.
if setting.Service.DisableLocalUserManagement {
ctx.ServerError("UpdateUser", fmt.Errorf("cannot change %s password; local user management disabled", u.Name))
return
}
if len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserEdit, &form)
Expand Down
2 changes: 1 addition & 1 deletion routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
if form.Visibility != "" {
org.Visibility = api.VisibilityModes[form.Visibility]
}
if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil {
if err := models.UpdateUserCols(org, false, "full_name", "description", "website", "location", "visibility"); err != nil {
ctx.Error(http.StatusInternalServerError, "EditOrganization", err)
return
}
Expand Down
Loading