mirror of
https://github.com/go-gitea/gitea.git
synced 2025-05-01 13:15:12 +00:00
Fix #880 Design: 1. A global setting `security.TWO_FACTOR_AUTH`. * To support org-level config, we need to introduce a better "owner setting" system first (in the future) 2. A user without 2FA can login and may explore, but can NOT read or write to any repositories via API/web. 3. Keep things as simple as possible. * This option only aggressively suggest users to enable their 2FA at the moment, it does NOT guarantee that users must have 2FA before all other operations, it should be good enough for real world use cases. * Some details and tests could be improved in the future since this change only adds a check and seems won't affect too much. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package smtp
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/smtp"
|
|
"net/textproto"
|
|
"strings"
|
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/optional"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
// Authenticate queries if the provided login/password is authenticates against the SMTP server
|
|
// Users will be autoregistered as required
|
|
func (source *Source) Authenticate(ctx context.Context, user *user_model.User, userName, password string) (*user_model.User, error) {
|
|
// Verify allowed domains.
|
|
if len(source.AllowedDomains) > 0 {
|
|
idx := strings.Index(userName, "@")
|
|
if idx == -1 {
|
|
return nil, user_model.ErrUserNotExist{Name: userName}
|
|
} else if !util.SliceContainsString(strings.Split(source.AllowedDomains, ","), userName[idx+1:], true) {
|
|
return nil, user_model.ErrUserNotExist{Name: userName}
|
|
}
|
|
}
|
|
|
|
var auth smtp.Auth
|
|
switch source.Auth {
|
|
case PlainAuthentication:
|
|
auth = smtp.PlainAuth("", userName, password, source.Host)
|
|
case LoginAuthentication:
|
|
auth = &loginAuthenticator{userName, password}
|
|
case CRAMMD5Authentication:
|
|
auth = smtp.CRAMMD5Auth(userName, password)
|
|
default:
|
|
return nil, errors.New("unsupported SMTP auth type")
|
|
}
|
|
|
|
if err := Authenticate(auth, source); err != nil {
|
|
// Check standard error format first,
|
|
// then fallback to worse case.
|
|
tperr, ok := err.(*textproto.Error)
|
|
if (ok && tperr.Code == 535) ||
|
|
strings.Contains(err.Error(), "Username and Password not accepted") {
|
|
return nil, user_model.ErrUserNotExist{Name: userName}
|
|
}
|
|
if (ok && tperr.Code == 534) ||
|
|
strings.Contains(err.Error(), "Application-specific password required") {
|
|
return nil, user_model.ErrUserNotExist{Name: userName}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
if user != nil {
|
|
return user, nil
|
|
}
|
|
|
|
username := userName
|
|
idx := strings.Index(userName, "@")
|
|
if idx > -1 {
|
|
username = userName[:idx]
|
|
}
|
|
|
|
user = &user_model.User{
|
|
LowerName: strings.ToLower(username),
|
|
Name: strings.ToLower(username),
|
|
Email: userName,
|
|
Passwd: password,
|
|
LoginType: auth_model.SMTP,
|
|
LoginSource: source.AuthSource.ID,
|
|
LoginName: userName,
|
|
}
|
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
|
IsActive: optional.Some(true),
|
|
}
|
|
|
|
if err := user_model.CreateUser(ctx, user, &user_model.Meta{}, overwriteDefault); err != nil {
|
|
return user, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|