]> source.dussan.org Git - gitea.git/commitdiff
Various fixes in login sources (#10428)
authorguillep2k <18600385+guillep2k@users.noreply.github.com>
Sun, 23 Feb 2020 19:52:05 +0000 (16:52 -0300)
committerGitHub <noreply@github.com>
Sun, 23 Feb 2020 19:52:05 +0000 (13:52 -0600)
15 files changed:
models/error.go
models/login_source.go
models/user.go
modules/auth/pam/pam.go
modules/auth/pam/pam_stub.go
modules/repository/create_test.go
options/locale/locale_en-US.ini
routers/admin/users.go
routers/api/v1/admin/org.go
routers/api/v1/admin/user.go
routers/api/v1/org/org.go
routers/api/v1/repo/migrate.go
routers/user/auth.go
routers/user/auth_openid.go
routers/user/setting/profile.go

index a67937273751e147cba9c7064929f450a6a01e3e..675b7406b8bbd01db66f0d4012644d04b9b94bdb 100644 (file)
@@ -57,6 +57,21 @@ func (err ErrNamePatternNotAllowed) Error() string {
        return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
 }
 
+// ErrNameCharsNotAllowed represents a "character not allowed in name" error.
+type ErrNameCharsNotAllowed struct {
+       Name string
+}
+
+// IsErrNameCharsNotAllowed checks if an error is an ErrNameCharsNotAllowed.
+func IsErrNameCharsNotAllowed(err error) bool {
+       _, ok := err.(ErrNameCharsNotAllowed)
+       return ok
+}
+
+func (err ErrNameCharsNotAllowed) Error() string {
+       return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
+}
+
 // ErrSSHDisabled represents an "SSH disabled" error.
 type ErrSSHDisabled struct {
 }
index f5dae860f8b49fd14fbbba9901fa7248809d6215..2774d6f80d986475ac75071613c48dd5b0142224 100644 (file)
@@ -12,7 +12,6 @@ import (
        "fmt"
        "net/smtp"
        "net/textproto"
-       "regexp"
        "strings"
 
        "code.gitea.io/gitea/modules/auth/ldap"
@@ -455,10 +454,6 @@ func composeFullName(firstname, surname, username string) string {
        }
 }
 
-var (
-       alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
-)
-
 // LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
 // and create a local user if success when enabled.
 func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*User, error) {
@@ -503,10 +498,6 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use
        if len(sr.Username) == 0 {
                sr.Username = login
        }
-       // Validate username make sure it satisfies requirement.
-       if alphaDashDotPattern.MatchString(sr.Username) {
-               return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", sr.Username)
-       }
 
        if len(sr.Mail) == 0 {
                sr.Mail = fmt.Sprintf("%s@localhost", sr.Username)
@@ -666,7 +657,8 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
 // LoginViaPAM queries if login/password is valid against the PAM,
 // and create a local user if success when enabled.
 func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig) (*User, error) {
-       if err := pam.Auth(cfg.ServiceName, login, password); err != nil {
+       pamLogin, err := pam.Auth(cfg.ServiceName, login, password)
+       if err != nil {
                if strings.Contains(err.Error(), "Authentication failure") {
                        return nil, ErrUserNotExist{0, login, 0}
                }
@@ -677,14 +669,21 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
                return user, nil
        }
 
+       // Allow PAM sources with `@` in their name, like from Active Directory
+       username := pamLogin
+       idx := strings.Index(pamLogin, "@")
+       if idx > -1 {
+               username = pamLogin[:idx]
+       }
+
        user = &User{
-               LowerName:   strings.ToLower(login),
-               Name:        login,
-               Email:       login,
+               LowerName:   strings.ToLower(username),
+               Name:        username,
+               Email:       pamLogin,
                Passwd:      password,
                LoginType:   LoginPAM,
                LoginSource: sourceID,
-               LoginName:   login,
+               LoginName:   login, // This is what the user typed in
                IsActive:    true,
        }
        return user, CreateUser(user)
index 5031849f90e2285ad44b935cebc0141d3781f920..bf59c1240bc7b0e1ce266181f7c8a338586adf14 100644 (file)
@@ -18,6 +18,7 @@ import (
        "image/png"
        "os"
        "path/filepath"
+       "regexp"
        "strconv"
        "strings"
        "time"
@@ -87,6 +88,9 @@ var (
 
        // ErrUnsupportedLoginType login source is unknown error
        ErrUnsupportedLoginType = errors.New("Login source is unknown")
+
+       // Characters prohibited in a user name (anything except A-Za-z0-9_.-)
+       alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
 )
 
 // User represents the object of individual and member of organization.
@@ -906,6 +910,11 @@ func isUsableName(names, patterns []string, name string) error {
 
 // IsUsableUsername returns an error when a username is reserved
 func IsUsableUsername(name string) error {
+       // Validate username make sure it satisfies requirement.
+       if alphaDashDotPattern.MatchString(name) {
+               // Note: usually this error is normally caught up earlier in the UI
+               return ErrNameCharsNotAllowed{Name: name}
+       }
        return isUsableName(reservedUsernames, reservedUserPatterns, name)
 }
 
index 6f0f7240ae2a38d427a9e936d25b37b800ad44ac..ca299b08ba0be2244130c54e7cf50058398d49bd 100644 (file)
@@ -13,7 +13,7 @@ import (
 )
 
 // Auth pam auth service
-func Auth(serviceName, userName, passwd string) error {
+func Auth(serviceName, userName, passwd string) (string, error) {
        t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
                switch s {
                case pam.PromptEchoOff:
@@ -25,12 +25,14 @@ func Auth(serviceName, userName, passwd string) error {
        })
 
        if err != nil {
-               return err
+               return "", err
        }
 
        if err = t.Authenticate(0); err != nil {
-               return err
+               return "", err
        }
 
-       return nil
+       // PAM login names might suffer transformations in the PAM stack.
+       // We should take whatever the PAM stack returns for it.
+       return t.GetItem(pam.User)
 }
index ee2527dd894ceaaa6e75f254f588f3c68cacd79b..604799ca973bceff1991c53a87949b341fd26cb9 100644 (file)
@@ -11,6 +11,6 @@ import (
 )
 
 // Auth not supported lack of pam tag
-func Auth(serviceName, userName, passwd string) error {
-       return errors.New("PAM not supported")
+func Auth(serviceName, userName, passwd string) (string, error) {
+       return "", errors.New("PAM not supported")
 }
index ee76d2181bc44d7edf911dabe08369dcfd8ae83b..d3e8bf5af1ebdff1de0cae9d27a12e15755d935a 100644 (file)
@@ -35,7 +35,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 
        // Create org.
        org := &models.User{
-               Name:       "All repo",
+               Name:       "All_repo",
                IsActive:   true,
                Type:       models.UserTypeOrganization,
                Visibility: structs.VisibleTypePublic,
index 7fe7bf697dc550dc5372c0a6d254dd7b2d5b8f31..cbe8aaad7a49909b94fcc79cb7f956602a40e459 100644 (file)
@@ -379,6 +379,7 @@ user_bio = Biography
 
 form.name_reserved = The username '%s' is reserved.
 form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.
+form.name_chars_not_allowed = User name '%s' contains invalid characters.
 
 [settings]
 profile = Profile
index 675369969694adc470108f1aff5b5de903d76b38..10ae622c323c6c825ef40f5cc4ed1bf1fc7fe543 100644 (file)
@@ -124,6 +124,9 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
                case models.IsErrNamePatternNotAllowed(err):
                        ctx.Data["Err_UserName"] = true
                        ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
+               case models.IsErrNameCharsNotAllowed(err):
+                       ctx.Data["Err_UserName"] = true
+                       ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
                default:
                        ctx.ServerError("CreateUser", err)
                }
index 01c2c49734e57c023671b7dbc7b5c0e0674b55bb..11c2f1dc49e53eec2226e2652fa3390407d13da7 100644 (file)
@@ -67,6 +67,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
        if err := models.CreateOrganization(org, u); err != nil {
                if models.IsErrUserAlreadyExist(err) ||
                        models.IsErrNameReserved(err) ||
+                       models.IsErrNameCharsNotAllowed(err) ||
                        models.IsErrNamePatternNotAllowed(err) {
                        ctx.Error(http.StatusUnprocessableEntity, "", err)
                } else {
index 0fbb9cdfe2ac83b30061946868c5075364ae98ac..38237d67445d051508de70256632a92e12f3714a 100644 (file)
@@ -91,6 +91,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
                if models.IsErrUserAlreadyExist(err) ||
                        models.IsErrEmailAlreadyUsed(err) ||
                        models.IsErrNameReserved(err) ||
+                       models.IsErrNameCharsNotAllowed(err) ||
                        models.IsErrNamePatternNotAllowed(err) {
                        ctx.Error(http.StatusUnprocessableEntity, "", err)
                } else {
index fde58bd5c17ca32586083142eee61b569176f02d..b79761c8750ad075735159c18d52689462185e24 100644 (file)
@@ -179,6 +179,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
        if err := models.CreateOrganization(org, ctx.User); err != nil {
                if models.IsErrUserAlreadyExist(err) ||
                        models.IsErrNameReserved(err) ||
+                       models.IsErrNameCharsNotAllowed(err) ||
                        models.IsErrNamePatternNotAllowed(err) {
                        ctx.Error(http.StatusUnprocessableEntity, "", err)
                } else {
index 4b9d3958adc7bb3629233a5dc49338a9a04d2311..fd0db7814c79949aea51ca98d911f9f223a322b0 100644 (file)
@@ -199,6 +199,8 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteA
                ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
        case models.IsErrNameReserved(err):
                ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
+       case models.IsErrNameCharsNotAllowed(err):
+               ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(models.ErrNameCharsNotAllowed).Name))
        case models.IsErrNamePatternNotAllowed(err):
                ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern))
        default:
index 6395836480ab0e6b8e60f2f7ef4dd449ac2d56ba..be0396cce1d157135e03f1f9a7bfdb43500de56d 100644 (file)
@@ -928,6 +928,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
                LoginName:   gothUser.(goth.User).UserID,
        }
 
+       //nolint: dupl
        if err := models.CreateUser(u); err != nil {
                switch {
                case models.IsErrUserAlreadyExist(err):
@@ -942,6 +943,9 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
                case models.IsErrNamePatternNotAllowed(err):
                        ctx.Data["Err_UserName"] = true
                        ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplLinkAccount, &form)
+               case models.IsErrNameCharsNotAllowed(err):
+                       ctx.Data["Err_UserName"] = true
+                       ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplLinkAccount, &form)
                default:
                        ctx.ServerError("CreateUser", err)
                }
index ccaea8264f087aec96482e6769a60a8c8a809e6b..bd05538ad3b239a51356147dfdcb160c936b021c 100644 (file)
@@ -400,6 +400,7 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
                Passwd:   password,
                IsActive: !setting.Service.RegisterEmailConfirm,
        }
+       //nolint: dupl
        if err := models.CreateUser(u); err != nil {
                switch {
                case models.IsErrUserAlreadyExist(err):
@@ -414,6 +415,9 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
                case models.IsErrNamePatternNotAllowed(err):
                        ctx.Data["Err_UserName"] = true
                        ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSignUpOID, &form)
+               case models.IsErrNameCharsNotAllowed(err):
+                       ctx.Data["Err_UserName"] = true
+                       ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplSignUpOID, &form)
                default:
                        ctx.ServerError("CreateUser", err)
                }
index a5cc433a9aed8fa3a25566e97a525abe86baa3c8..d6f25f91358716179800c52ce63051797d5b6479 100644 (file)
@@ -58,6 +58,9 @@ func handleUsernameChange(ctx *context.Context, newName string) {
                        case models.IsErrNamePatternNotAllowed(err):
                                ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
                                ctx.Redirect(setting.AppSubURL + "/user/settings")
+                       case models.IsErrNameCharsNotAllowed(err):
+                               ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
+                               ctx.Redirect(setting.AppSubURL + "/user/settings")
                        default:
                                ctx.ServerError("ChangeUserName", err)
                        }