Skip to content
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

Various fixes in login sources (#10428) #10429

Merged
merged 1 commit into from
Feb 23, 2020
Merged
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
15 changes: 15 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,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 {
}
Expand Down
27 changes: 13 additions & 14 deletions models/login_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"fmt"
"net/smtp"
"net/textproto"
"regexp"
"strings"

"code.gitea.io/gitea/modules/auth/ldap"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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}
}
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion models/org_team_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {

// Create org.
org := &User{
Name: "All repo",
Name: "All_repo",
IsActive: true,
Type: UserTypeOrganization,
Visibility: structs.VisibleTypePublic,
Expand Down
9 changes: 9 additions & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"image/png"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -870,6 +874,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)
}

Expand Down
10 changes: 6 additions & 4 deletions modules/auth/pam/pam.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
}
4 changes: 2 additions & 2 deletions modules/auth/pam/pam_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,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
Expand Down
3 changes: 3 additions & 0 deletions routers/admin/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,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)
}
Expand Down
1 change: 1 addition & 0 deletions routers/api/v1/admin/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,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 {
Expand Down
1 change: 1 addition & 0 deletions routers/api/v1/admin/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,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 {
Expand Down
1 change: 1 addition & 0 deletions routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,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 {
Expand Down
2 changes: 2 additions & 0 deletions routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,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:
Expand Down
4 changes: 4 additions & 0 deletions routers/user/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
}
Expand Down
4 changes: 4 additions & 0 deletions routers/user/auth_openid.go
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions routers/user/setting/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down