mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-22 12:54:53 -05:00
Various fixes in login sources (#10428)
This commit is contained in:
parent
542bd59239
commit
09dbd85a3a
15 changed files with 66 additions and 21 deletions
|
@ -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 a new issue