Various fixes in login sources (#10428)
This commit is contained in:
		
							parent
							
								
									542bd59239
								
							
						
					
					
						commit
						09dbd85a3a
					
				| @ -57,6 +57,21 @@ func (err ErrNamePatternNotAllowed) Error() string { | |||||||
| 	return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern) | 	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.
 | // ErrSSHDisabled represents an "SSH disabled" error.
 | ||||||
| type ErrSSHDisabled struct { | type ErrSSHDisabled struct { | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/smtp" | 	"net/smtp" | ||||||
| 	"net/textproto" | 	"net/textproto" | ||||||
| 	"regexp" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/auth/ldap" | 	"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,
 | // LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
 | ||||||
| // and create a local user if success when enabled.
 | // and create a local user if success when enabled.
 | ||||||
| func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*User, error) { | 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 { | 	if len(sr.Username) == 0 { | ||||||
| 		sr.Username = login | 		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 { | 	if len(sr.Mail) == 0 { | ||||||
| 		sr.Mail = fmt.Sprintf("%s@localhost", sr.Username) | 		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,
 | // LoginViaPAM queries if login/password is valid against the PAM,
 | ||||||
| // and create a local user if success when enabled.
 | // and create a local user if success when enabled.
 | ||||||
| func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig) (*User, error) { | 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") { | 		if strings.Contains(err.Error(), "Authentication failure") { | ||||||
| 			return nil, ErrUserNotExist{0, login, 0} | 			return nil, ErrUserNotExist{0, login, 0} | ||||||
| 		} | 		} | ||||||
| @ -677,14 +669,21 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon | |||||||
| 		return user, nil | 		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{ | 	user = &User{ | ||||||
| 		LowerName:   strings.ToLower(login), | 		LowerName:   strings.ToLower(username), | ||||||
| 		Name:        login, | 		Name:        username, | ||||||
| 		Email:       login, | 		Email:       pamLogin, | ||||||
| 		Passwd:      password, | 		Passwd:      password, | ||||||
| 		LoginType:   LoginPAM, | 		LoginType:   LoginPAM, | ||||||
| 		LoginSource: sourceID, | 		LoginSource: sourceID, | ||||||
| 		LoginName:   login, | 		LoginName:   login, // This is what the user typed in
 | ||||||
| 		IsActive:    true, | 		IsActive:    true, | ||||||
| 	} | 	} | ||||||
| 	return user, CreateUser(user) | 	return user, CreateUser(user) | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import ( | |||||||
| 	"image/png" | 	"image/png" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | 	"regexp" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| @ -87,6 +88,9 @@ var ( | |||||||
| 
 | 
 | ||||||
| 	// ErrUnsupportedLoginType login source is unknown error
 | 	// ErrUnsupportedLoginType login source is unknown error
 | ||||||
| 	ErrUnsupportedLoginType = errors.New("Login source is unknown") | 	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.
 | // 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
 | // IsUsableUsername returns an error when a username is reserved
 | ||||||
| func IsUsableUsername(name string) error { | 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) | 	return isUsableName(reservedUsernames, reservedUserPatterns, name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Auth pam auth service
 | // 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) { | 	t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) { | ||||||
| 		switch s { | 		switch s { | ||||||
| 		case pam.PromptEchoOff: | 		case pam.PromptEchoOff: | ||||||
| @ -25,12 +25,14 @@ func Auth(serviceName, userName, passwd string) error { | |||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = t.Authenticate(0); err != nil { | 	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) | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,6 +11,6 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Auth not supported lack of pam tag
 | // Auth not supported lack of pam tag
 | ||||||
| func Auth(serviceName, userName, passwd string) error { | func Auth(serviceName, userName, passwd string) (string, error) { | ||||||
| 	return errors.New("PAM not supported") | 	return "", errors.New("PAM not supported") | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// Create org.
 | 	// Create org.
 | ||||||
| 	org := &models.User{ | 	org := &models.User{ | ||||||
| 		Name:       "All repo", | 		Name:       "All_repo", | ||||||
| 		IsActive:   true, | 		IsActive:   true, | ||||||
| 		Type:       models.UserTypeOrganization, | 		Type:       models.UserTypeOrganization, | ||||||
| 		Visibility: structs.VisibleTypePublic, | 		Visibility: structs.VisibleTypePublic, | ||||||
|  | |||||||
| @ -379,6 +379,7 @@ user_bio = Biography | |||||||
| 
 | 
 | ||||||
| form.name_reserved = The username '%s' is reserved. | form.name_reserved = The username '%s' is reserved. | ||||||
| form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username. | 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] | [settings] | ||||||
| profile = Profile | profile = Profile | ||||||
|  | |||||||
| @ -124,6 +124,9 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) { | |||||||
| 		case models.IsErrNamePatternNotAllowed(err): | 		case models.IsErrNamePatternNotAllowed(err): | ||||||
| 			ctx.Data["Err_UserName"] = true | 			ctx.Data["Err_UserName"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form) | 			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: | 		default: | ||||||
| 			ctx.ServerError("CreateUser", err) | 			ctx.ServerError("CreateUser", err) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -67,6 +67,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { | |||||||
| 	if err := models.CreateOrganization(org, u); err != nil { | 	if err := models.CreateOrganization(org, u); err != nil { | ||||||
| 		if models.IsErrUserAlreadyExist(err) || | 		if models.IsErrUserAlreadyExist(err) || | ||||||
| 			models.IsErrNameReserved(err) || | 			models.IsErrNameReserved(err) || | ||||||
|  | 			models.IsErrNameCharsNotAllowed(err) || | ||||||
| 			models.IsErrNamePatternNotAllowed(err) { | 			models.IsErrNamePatternNotAllowed(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", err) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
|  | |||||||
| @ -91,6 +91,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { | |||||||
| 		if models.IsErrUserAlreadyExist(err) || | 		if models.IsErrUserAlreadyExist(err) || | ||||||
| 			models.IsErrEmailAlreadyUsed(err) || | 			models.IsErrEmailAlreadyUsed(err) || | ||||||
| 			models.IsErrNameReserved(err) || | 			models.IsErrNameReserved(err) || | ||||||
|  | 			models.IsErrNameCharsNotAllowed(err) || | ||||||
| 			models.IsErrNamePatternNotAllowed(err) { | 			models.IsErrNamePatternNotAllowed(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", err) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
|  | |||||||
| @ -179,6 +179,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) { | |||||||
| 	if err := models.CreateOrganization(org, ctx.User); err != nil { | 	if err := models.CreateOrganization(org, ctx.User); err != nil { | ||||||
| 		if models.IsErrUserAlreadyExist(err) || | 		if models.IsErrUserAlreadyExist(err) || | ||||||
| 			models.IsErrNameReserved(err) || | 			models.IsErrNameReserved(err) || | ||||||
|  | 			models.IsErrNameCharsNotAllowed(err) || | ||||||
| 			models.IsErrNamePatternNotAllowed(err) { | 			models.IsErrNamePatternNotAllowed(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", err) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
|  | |||||||
| @ -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())) | 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit())) | ||||||
| 	case models.IsErrNameReserved(err): | 	case models.IsErrNameReserved(err): | ||||||
| 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name)) | 		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): | 	case models.IsErrNamePatternNotAllowed(err): | ||||||
| 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) | 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern)) | ||||||
| 	default: | 	default: | ||||||
|  | |||||||
| @ -928,6 +928,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au | |||||||
| 		LoginName:   gothUser.(goth.User).UserID, | 		LoginName:   gothUser.(goth.User).UserID, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	//nolint: dupl
 | ||||||
| 	if err := models.CreateUser(u); err != nil { | 	if err := models.CreateUser(u); err != nil { | ||||||
| 		switch { | 		switch { | ||||||
| 		case models.IsErrUserAlreadyExist(err): | 		case models.IsErrUserAlreadyExist(err): | ||||||
| @ -942,6 +943,9 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au | |||||||
| 		case models.IsErrNamePatternNotAllowed(err): | 		case models.IsErrNamePatternNotAllowed(err): | ||||||
| 			ctx.Data["Err_UserName"] = true | 			ctx.Data["Err_UserName"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplLinkAccount, &form) | 			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: | 		default: | ||||||
| 			ctx.ServerError("CreateUser", err) | 			ctx.ServerError("CreateUser", err) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -400,6 +400,7 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si | |||||||
| 		Passwd:   password, | 		Passwd:   password, | ||||||
| 		IsActive: !setting.Service.RegisterEmailConfirm, | 		IsActive: !setting.Service.RegisterEmailConfirm, | ||||||
| 	} | 	} | ||||||
|  | 	//nolint: dupl
 | ||||||
| 	if err := models.CreateUser(u); err != nil { | 	if err := models.CreateUser(u); err != nil { | ||||||
| 		switch { | 		switch { | ||||||
| 		case models.IsErrUserAlreadyExist(err): | 		case models.IsErrUserAlreadyExist(err): | ||||||
| @ -414,6 +415,9 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si | |||||||
| 		case models.IsErrNamePatternNotAllowed(err): | 		case models.IsErrNamePatternNotAllowed(err): | ||||||
| 			ctx.Data["Err_UserName"] = true | 			ctx.Data["Err_UserName"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSignUpOID, &form) | 			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: | 		default: | ||||||
| 			ctx.ServerError("CreateUser", err) | 			ctx.ServerError("CreateUser", err) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -58,6 +58,9 @@ func handleUsernameChange(ctx *context.Context, newName string) { | |||||||
| 			case models.IsErrNamePatternNotAllowed(err): | 			case models.IsErrNamePatternNotAllowed(err): | ||||||
| 				ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName)) | 				ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName)) | ||||||
| 				ctx.Redirect(setting.AppSubURL + "/user/settings") | 				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: | 			default: | ||||||
| 				ctx.ServerError("ChangeUserName", err) | 				ctx.ServerError("ChangeUserName", err) | ||||||
| 			} | 			} | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user