code.gitea.io/gitea@v1.21.7/services/auth/source/smtp/source_authenticate.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package smtp 5 6 import ( 7 "context" 8 "errors" 9 "net/smtp" 10 "net/textproto" 11 "strings" 12 13 auth_model "code.gitea.io/gitea/models/auth" 14 user_model "code.gitea.io/gitea/models/user" 15 "code.gitea.io/gitea/modules/util" 16 ) 17 18 // Authenticate queries if the provided login/password is authenticates against the SMTP server 19 // Users will be autoregistered as required 20 func (source *Source) Authenticate(ctx context.Context, user *user_model.User, userName, password string) (*user_model.User, error) { 21 // Verify allowed domains. 22 if len(source.AllowedDomains) > 0 { 23 idx := strings.Index(userName, "@") 24 if idx == -1 { 25 return nil, user_model.ErrUserNotExist{Name: userName} 26 } else if !util.SliceContainsString(strings.Split(source.AllowedDomains, ","), userName[idx+1:], true) { 27 return nil, user_model.ErrUserNotExist{Name: userName} 28 } 29 } 30 31 var auth smtp.Auth 32 switch source.Auth { 33 case PlainAuthentication: 34 auth = smtp.PlainAuth("", userName, password, source.Host) 35 case LoginAuthentication: 36 auth = &loginAuthenticator{userName, password} 37 case CRAMMD5Authentication: 38 auth = smtp.CRAMMD5Auth(userName, password) 39 default: 40 return nil, errors.New("unsupported SMTP auth type") 41 } 42 43 if err := Authenticate(auth, source); err != nil { 44 // Check standard error format first, 45 // then fallback to worse case. 46 tperr, ok := err.(*textproto.Error) 47 if (ok && tperr.Code == 535) || 48 strings.Contains(err.Error(), "Username and Password not accepted") { 49 return nil, user_model.ErrUserNotExist{Name: userName} 50 } 51 if (ok && tperr.Code == 534) || 52 strings.Contains(err.Error(), "Application-specific password required") { 53 return nil, user_model.ErrUserNotExist{Name: userName} 54 } 55 return nil, err 56 } 57 58 if user != nil { 59 return user, nil 60 } 61 62 username := userName 63 idx := strings.Index(userName, "@") 64 if idx > -1 { 65 username = userName[:idx] 66 } 67 68 user = &user_model.User{ 69 LowerName: strings.ToLower(username), 70 Name: strings.ToLower(username), 71 Email: userName, 72 Passwd: password, 73 LoginType: auth_model.SMTP, 74 LoginSource: source.authSource.ID, 75 LoginName: userName, 76 } 77 overwriteDefault := &user_model.CreateUserOverwriteOptions{ 78 IsActive: util.OptionalBoolTrue, 79 } 80 81 if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { 82 return user, err 83 } 84 85 return user, nil 86 } 87 88 // IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication 89 func (source *Source) IsSkipLocalTwoFA() bool { 90 return source.SkipLocalTwoFA 91 }