1// Copyright 2014 The Gogs Authors. All rights reserved.
2// Copyright 2019 The Gitea Authors. All rights reserved.
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file.
5
6package user
7
8import (
9	"context"
10	"crypto/sha256"
11	"crypto/subtle"
12	"encoding/hex"
13	"fmt"
14	"net/url"
15	"os"
16	"path/filepath"
17	"strings"
18	"time"
19
20	_ "image/jpeg" // Needed for jpeg support
21
22	"code.gitea.io/gitea/models/auth"
23	"code.gitea.io/gitea/models/db"
24	"code.gitea.io/gitea/modules/auth/openid"
25	"code.gitea.io/gitea/modules/base"
26	"code.gitea.io/gitea/modules/git"
27	"code.gitea.io/gitea/modules/log"
28	"code.gitea.io/gitea/modules/setting"
29	"code.gitea.io/gitea/modules/structs"
30	"code.gitea.io/gitea/modules/timeutil"
31	"code.gitea.io/gitea/modules/util"
32
33	"golang.org/x/crypto/argon2"
34	"golang.org/x/crypto/bcrypt"
35	"golang.org/x/crypto/pbkdf2"
36	"golang.org/x/crypto/scrypt"
37	"xorm.io/builder"
38)
39
40// UserType defines the user type
41type UserType int //revive:disable-line:exported
42
43const (
44	// UserTypeIndividual defines an individual user
45	UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
46
47	// UserTypeOrganization defines an organization
48	UserTypeOrganization
49)
50
51const (
52	algoBcrypt = "bcrypt"
53	algoScrypt = "scrypt"
54	algoArgon2 = "argon2"
55	algoPbkdf2 = "pbkdf2"
56)
57
58// AvailableHashAlgorithms represents the available password hashing algorithms
59var AvailableHashAlgorithms = []string{
60	algoPbkdf2,
61	algoArgon2,
62	algoScrypt,
63	algoBcrypt,
64}
65
66const (
67	// EmailNotificationsEnabled indicates that the user would like to receive all email notifications
68	EmailNotificationsEnabled = "enabled"
69	// EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned.
70	EmailNotificationsOnMention = "onmention"
71	// EmailNotificationsDisabled indicates that the user would not like to be notified via email.
72	EmailNotificationsDisabled = "disabled"
73)
74
75// User represents the object of individual and member of organization.
76type User struct {
77	ID        int64  `xorm:"pk autoincr"`
78	LowerName string `xorm:"UNIQUE NOT NULL"`
79	Name      string `xorm:"UNIQUE NOT NULL"`
80	FullName  string
81	// Email is the primary email address (to be used for communication)
82	Email                        string `xorm:"NOT NULL"`
83	KeepEmailPrivate             bool
84	EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"`
85	Passwd                       string `xorm:"NOT NULL"`
86	PasswdHashAlgo               string `xorm:"NOT NULL DEFAULT 'argon2'"`
87
88	// MustChangePassword is an attribute that determines if a user
89	// is to change his/her password after registration.
90	MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
91
92	LoginType   auth.Type
93	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
94	LoginName   string
95	Type        UserType
96	Location    string
97	Website     string
98	Rands       string `xorm:"VARCHAR(32)"`
99	Salt        string `xorm:"VARCHAR(32)"`
100	Language    string `xorm:"VARCHAR(5)"`
101	Description string
102
103	CreatedUnix   timeutil.TimeStamp `xorm:"INDEX created"`
104	UpdatedUnix   timeutil.TimeStamp `xorm:"INDEX updated"`
105	LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"`
106
107	// Remember visibility choice for convenience, true for private
108	LastRepoVisibility bool
109	// Maximum repository creation limit, -1 means use global default
110	MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
111
112	// IsActive true: primary email is activated, user can access Web UI and Git SSH.
113	// false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access.
114	IsActive bool `xorm:"INDEX"`
115	// the user is a Gitea admin, who can access all repositories and the admin pages.
116	IsAdmin bool
117	// true: the user is only allowed to see organizations/repositories that they has explicit rights to.
118	// (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public)
119	IsRestricted bool `xorm:"NOT NULL DEFAULT false"`
120
121	AllowGitHook            bool
122	AllowImportLocal        bool // Allow migrate repository by local path
123	AllowCreateOrganization bool `xorm:"DEFAULT true"`
124
125	// true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents)
126	ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"`
127
128	// Avatar
129	Avatar          string `xorm:"VARCHAR(2048) NOT NULL"`
130	AvatarEmail     string `xorm:"NOT NULL"`
131	UseCustomAvatar bool
132
133	// Counters
134	NumFollowers int
135	NumFollowing int `xorm:"NOT NULL DEFAULT 0"`
136	NumStars     int
137	NumRepos     int
138
139	// For organization
140	NumTeams                  int
141	NumMembers                int
142	Visibility                structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
143	RepoAdminChangeTeamAccess bool                `xorm:"NOT NULL DEFAULT false"`
144
145	// Preferences
146	DiffViewStyle       string `xorm:"NOT NULL DEFAULT ''"`
147	Theme               string `xorm:"NOT NULL DEFAULT ''"`
148	KeepActivityPrivate bool   `xorm:"NOT NULL DEFAULT false"`
149}
150
151func init() {
152	db.RegisterModel(new(User))
153}
154
155// SearchOrganizationsOptions options to filter organizations
156type SearchOrganizationsOptions struct {
157	db.ListOptions
158	All bool
159}
160
161// ColorFormat writes a colored string to identify this struct
162func (u *User) ColorFormat(s fmt.State) {
163	if u == nil {
164		log.ColorFprintf(s, "%d:%s",
165			log.NewColoredIDValue(0),
166			log.NewColoredValue("<nil>"))
167		return
168	}
169	log.ColorFprintf(s, "%d:%s",
170		log.NewColoredIDValue(u.ID),
171		log.NewColoredValue(u.Name))
172}
173
174// BeforeUpdate is invoked from XORM before updating this object.
175func (u *User) BeforeUpdate() {
176	if u.MaxRepoCreation < -1 {
177		u.MaxRepoCreation = -1
178	}
179
180	// Organization does not need email
181	u.Email = strings.ToLower(u.Email)
182	if !u.IsOrganization() {
183		if len(u.AvatarEmail) == 0 {
184			u.AvatarEmail = u.Email
185		}
186	}
187
188	u.LowerName = strings.ToLower(u.Name)
189	u.Location = base.TruncateString(u.Location, 255)
190	u.Website = base.TruncateString(u.Website, 255)
191	u.Description = base.TruncateString(u.Description, 255)
192}
193
194// AfterLoad is invoked from XORM after filling all the fields of this object.
195func (u *User) AfterLoad() {
196	if u.Theme == "" {
197		u.Theme = setting.UI.DefaultTheme
198	}
199}
200
201// SetLastLogin set time to last login
202func (u *User) SetLastLogin() {
203	u.LastLoginUnix = timeutil.TimeStampNow()
204}
205
206// UpdateUserDiffViewStyle updates the users diff view style
207func UpdateUserDiffViewStyle(u *User, style string) error {
208	u.DiffViewStyle = style
209	return UpdateUserCols(db.DefaultContext, u, "diff_view_style")
210}
211
212// UpdateUserTheme updates a users' theme irrespective of the site wide theme
213func UpdateUserTheme(u *User, themeName string) error {
214	u.Theme = themeName
215	return UpdateUserCols(db.DefaultContext, u, "theme")
216}
217
218// GetEmail returns an noreply email, if the user has set to keep his
219// email address private, otherwise the primary email address.
220func (u *User) GetEmail() string {
221	if u.KeepEmailPrivate {
222		return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
223	}
224	return u.Email
225}
226
227// GetAllUsers returns a slice of all individual users found in DB.
228func GetAllUsers() ([]*User, error) {
229	users := make([]*User, 0)
230	return users, db.GetEngine(db.DefaultContext).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
231}
232
233// IsLocal returns true if user login type is LoginPlain.
234func (u *User) IsLocal() bool {
235	return u.LoginType <= auth.Plain
236}
237
238// IsOAuth2 returns true if user login type is LoginOAuth2.
239func (u *User) IsOAuth2() bool {
240	return u.LoginType == auth.OAuth2
241}
242
243// MaxCreationLimit returns the number of repositories a user is allowed to create
244func (u *User) MaxCreationLimit() int {
245	if u.MaxRepoCreation <= -1 {
246		return setting.Repository.MaxCreationLimit
247	}
248	return u.MaxRepoCreation
249}
250
251// CanCreateRepo returns if user login can create a repository
252// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
253func (u *User) CanCreateRepo() bool {
254	if u.IsAdmin {
255		return true
256	}
257	if u.MaxRepoCreation <= -1 {
258		if setting.Repository.MaxCreationLimit <= -1 {
259			return true
260		}
261		return u.NumRepos < setting.Repository.MaxCreationLimit
262	}
263	return u.NumRepos < u.MaxRepoCreation
264}
265
266// CanCreateOrganization returns true if user can create organisation.
267func (u *User) CanCreateOrganization() bool {
268	return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation)
269}
270
271// CanEditGitHook returns true if user can edit Git hooks.
272func (u *User) CanEditGitHook() bool {
273	return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
274}
275
276// CanImportLocal returns true if user can migrate repository by local path.
277func (u *User) CanImportLocal() bool {
278	if !setting.ImportLocalPaths || u == nil {
279		return false
280	}
281	return u.IsAdmin || u.AllowImportLocal
282}
283
284// DashboardLink returns the user dashboard page link.
285func (u *User) DashboardLink() string {
286	if u.IsOrganization() {
287		return u.OrganisationLink() + "/dashboard"
288	}
289	return setting.AppSubURL + "/"
290}
291
292// HomeLink returns the user or organization home page link.
293func (u *User) HomeLink() string {
294	return setting.AppSubURL + "/" + url.PathEscape(u.Name)
295}
296
297// HTMLURL returns the user or organization's full link.
298func (u *User) HTMLURL() string {
299	return setting.AppURL + url.PathEscape(u.Name)
300}
301
302// OrganisationLink returns the organization sub page link.
303func (u *User) OrganisationLink() string {
304	return setting.AppSubURL + "/org/" + url.PathEscape(u.Name)
305}
306
307// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
308func (u *User) GenerateEmailActivateCode(email string) string {
309	code := base.CreateTimeLimitCode(
310		fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands),
311		setting.Service.ActiveCodeLives, nil)
312
313	// Add tail hex username
314	code += hex.EncodeToString([]byte(u.LowerName))
315	return code
316}
317
318// GetUserFollowers returns range of user's followers.
319func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) {
320	sess := db.GetEngine(db.DefaultContext).
321		Where("follow.follow_id=?", u.ID).
322		Join("LEFT", "follow", "`user`.id=follow.user_id")
323
324	if listOptions.Page != 0 {
325		sess = db.SetSessionPagination(sess, &listOptions)
326
327		users := make([]*User, 0, listOptions.PageSize)
328		return users, sess.Find(&users)
329	}
330
331	users := make([]*User, 0, 8)
332	return users, sess.Find(&users)
333}
334
335// GetUserFollowing returns range of user's following.
336func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) {
337	sess := db.GetEngine(db.DefaultContext).
338		Where("follow.user_id=?", u.ID).
339		Join("LEFT", "follow", "`user`.id=follow.follow_id")
340
341	if listOptions.Page != 0 {
342		sess = db.SetSessionPagination(sess, &listOptions)
343
344		users := make([]*User, 0, listOptions.PageSize)
345		return users, sess.Find(&users)
346	}
347
348	users := make([]*User, 0, 8)
349	return users, sess.Find(&users)
350}
351
352// NewGitSig generates and returns the signature of given user.
353func (u *User) NewGitSig() *git.Signature {
354	return &git.Signature{
355		Name:  u.GitName(),
356		Email: u.GetEmail(),
357		When:  time.Now(),
358	}
359}
360
361func hashPassword(passwd, salt, algo string) (string, error) {
362	var tempPasswd []byte
363	var saltBytes []byte
364
365	// There are two formats for the Salt value:
366	// * The new format is a (32+)-byte hex-encoded string
367	// * The old format was a 10-byte binary format
368	// We have to tolerate both here but Authenticate should
369	// regenerate the Salt following a successful validation.
370	if len(salt) == 10 {
371		saltBytes = []byte(salt)
372	} else {
373		var err error
374		saltBytes, err = hex.DecodeString(salt)
375		if err != nil {
376			return "", err
377		}
378	}
379
380	switch algo {
381	case algoBcrypt:
382		tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
383		return string(tempPasswd), nil
384	case algoScrypt:
385		tempPasswd, _ = scrypt.Key([]byte(passwd), saltBytes, 65536, 16, 2, 50)
386	case algoArgon2:
387		tempPasswd = argon2.IDKey([]byte(passwd), saltBytes, 2, 65536, 8, 50)
388	case algoPbkdf2:
389		fallthrough
390	default:
391		tempPasswd = pbkdf2.Key([]byte(passwd), saltBytes, 10000, 50, sha256.New)
392	}
393
394	return fmt.Sprintf("%x", tempPasswd), nil
395}
396
397// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO
398// change passwd, salt and passwd_hash_algo fields
399func (u *User) SetPassword(passwd string) (err error) {
400	if len(passwd) == 0 {
401		u.Passwd = ""
402		u.Salt = ""
403		u.PasswdHashAlgo = ""
404		return nil
405	}
406
407	if u.Salt, err = GetUserSalt(); err != nil {
408		return err
409	}
410	if u.Passwd, err = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo); err != nil {
411		return err
412	}
413	u.PasswdHashAlgo = setting.PasswordHashAlgo
414
415	return nil
416}
417
418// ValidatePassword checks if given password matches the one belongs to the user.
419func (u *User) ValidatePassword(passwd string) bool {
420	tempHash, err := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)
421	if err != nil {
422		return false
423	}
424
425	if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 {
426		return true
427	}
428	if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
429		return true
430	}
431	return false
432}
433
434// IsPasswordSet checks if the password is set or left empty
435func (u *User) IsPasswordSet() bool {
436	return len(u.Passwd) != 0
437}
438
439// IsOrganization returns true if user is actually a organization.
440func (u *User) IsOrganization() bool {
441	return u.Type == UserTypeOrganization
442}
443
444// DisplayName returns full name if it's not empty,
445// returns username otherwise.
446func (u *User) DisplayName() string {
447	trimmed := strings.TrimSpace(u.FullName)
448	if len(trimmed) > 0 {
449		return trimmed
450	}
451	return u.Name
452}
453
454// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
455// returns username otherwise.
456func (u *User) GetDisplayName() string {
457	if setting.UI.DefaultShowFullName {
458		trimmed := strings.TrimSpace(u.FullName)
459		if len(trimmed) > 0 {
460			return trimmed
461		}
462	}
463	return u.Name
464}
465
466func gitSafeName(name string) string {
467	return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name))
468}
469
470// GitName returns a git safe name
471func (u *User) GitName() string {
472	gitName := gitSafeName(u.FullName)
473	if len(gitName) > 0 {
474		return gitName
475	}
476	// Although u.Name should be safe if created in our system
477	// LDAP users may have bad names
478	gitName = gitSafeName(u.Name)
479	if len(gitName) > 0 {
480		return gitName
481	}
482	// Totally pathological name so it's got to be:
483	return fmt.Sprintf("user-%d", u.ID)
484}
485
486// ShortName ellipses username to length
487func (u *User) ShortName(length int) string {
488	return base.EllipsisString(u.Name, length)
489}
490
491// IsMailable checks if a user is eligible
492// to receive emails.
493func (u *User) IsMailable() bool {
494	return u.IsActive
495}
496
497// EmailNotifications returns the User's email notification preference
498func (u *User) EmailNotifications() string {
499	return u.EmailNotificationsPreference
500}
501
502// SetEmailNotifications sets the user's email notification preference
503func SetEmailNotifications(u *User, set string) error {
504	u.EmailNotificationsPreference = set
505	if err := UpdateUserCols(db.DefaultContext, u, "email_notifications_preference"); err != nil {
506		log.Error("SetEmailNotifications: %v", err)
507		return err
508	}
509	return nil
510}
511
512func isUserExist(e db.Engine, uid int64, name string) (bool, error) {
513	if len(name) == 0 {
514		return false, nil
515	}
516	return e.
517		Where("id!=?", uid).
518		Get(&User{LowerName: strings.ToLower(name)})
519}
520
521// IsUserExist checks if given user name exist,
522// the user name should be noncased unique.
523// If uid is presented, then check will rule out that one,
524// it is used when update a user name in settings page.
525func IsUserExist(uid int64, name string) (bool, error) {
526	return isUserExist(db.GetEngine(db.DefaultContext), uid, name)
527}
528
529// Note: As of the beginning of 2022, it is recommended to use at least
530// 64 bits of salt, but NIST is already recommending to use to 128 bits.
531// (16 bytes = 16 * 8 = 128 bits)
532const SaltByteLength = 16
533
534// GetUserSalt returns a random user salt token.
535func GetUserSalt() (string, error) {
536	rBytes, err := util.RandomBytes(SaltByteLength)
537	if err != nil {
538		return "", err
539	}
540	// Returns a 32 bytes long string.
541	return hex.EncodeToString(rBytes), nil
542}
543
544// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
545func NewGhostUser() *User {
546	return &User{
547		ID:        -1,
548		Name:      "Ghost",
549		LowerName: "ghost",
550	}
551}
552
553// NewReplaceUser creates and returns a fake user for external user
554func NewReplaceUser(name string) *User {
555	return &User{
556		ID:        -1,
557		Name:      name,
558		LowerName: strings.ToLower(name),
559	}
560}
561
562// IsGhost check if user is fake user for a deleted account
563func (u *User) IsGhost() bool {
564	if u == nil {
565		return false
566	}
567	return u.ID == -1 && u.Name == "Ghost"
568}
569
570var (
571	reservedUsernames = []string{
572		".",
573		"..",
574		".well-known",
575		"admin",
576		"api",
577		"assets",
578		"attachments",
579		"avatars",
580		"captcha",
581		"commits",
582		"debug",
583		"error",
584		"explore",
585		"favicon.ico",
586		"ghost",
587		"help",
588		"install",
589		"issues",
590		"less",
591		"login",
592		"manifest.json",
593		"metrics",
594		"milestones",
595		"new",
596		"notifications",
597		"org",
598		"plugins",
599		"pulls",
600		"raw",
601		"repo",
602		"robots.txt",
603		"search",
604		"serviceworker.js",
605		"stars",
606		"template",
607		"user",
608	}
609
610	reservedUserPatterns = []string{"*.keys", "*.gpg", "*.rss", "*.atom"}
611)
612
613// IsUsableUsername returns an error when a username is reserved
614func IsUsableUsername(name string) error {
615	// Validate username make sure it satisfies requirement.
616	if db.AlphaDashDotPattern.MatchString(name) {
617		// Note: usually this error is normally caught up earlier in the UI
618		return db.ErrNameCharsNotAllowed{Name: name}
619	}
620	return db.IsUsableName(reservedUsernames, reservedUserPatterns, name)
621}
622
623// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
624type CreateUserOverwriteOptions struct {
625	Visibility structs.VisibleType
626}
627
628// CreateUser creates record of a new user.
629func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
630	if err = IsUsableUsername(u.Name); err != nil {
631		return err
632	}
633
634	// set system defaults
635	u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
636	u.Visibility = setting.Service.DefaultUserVisibilityMode
637	u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
638	u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
639	u.MaxRepoCreation = -1
640	u.Theme = setting.UI.DefaultTheme
641
642	// overwrite defaults if set
643	if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
644		u.Visibility = overwriteDefault[0].Visibility
645	}
646
647	// validate data
648	if err := validateUser(u); err != nil {
649		return err
650	}
651
652	if err := ValidateEmail(u.Email); err != nil {
653		return err
654	}
655
656	ctx, committer, err := db.TxContext()
657	if err != nil {
658		return err
659	}
660	defer committer.Close()
661
662	sess := db.GetEngine(ctx)
663
664	isExist, err := isUserExist(sess, 0, u.Name)
665	if err != nil {
666		return err
667	} else if isExist {
668		return ErrUserAlreadyExist{u.Name}
669	}
670
671	isExist, err = IsEmailUsed(ctx, u.Email)
672	if err != nil {
673		return err
674	} else if isExist {
675		return ErrEmailAlreadyUsed{
676			Email: u.Email,
677		}
678	}
679
680	// prepare for database
681
682	u.LowerName = strings.ToLower(u.Name)
683	u.AvatarEmail = u.Email
684	if u.Rands, err = GetUserSalt(); err != nil {
685		return err
686	}
687	if err = u.SetPassword(u.Passwd); err != nil {
688		return err
689	}
690
691	// save changes to database
692
693	if err = DeleteUserRedirect(ctx, u.Name); err != nil {
694		return err
695	}
696
697	if err = db.Insert(ctx, u); err != nil {
698		return err
699	}
700
701	// insert email address
702	if err := db.Insert(ctx, &EmailAddress{
703		UID:         u.ID,
704		Email:       u.Email,
705		LowerEmail:  strings.ToLower(u.Email),
706		IsActivated: u.IsActive,
707		IsPrimary:   true,
708	}); err != nil {
709		return err
710	}
711
712	return committer.Commit()
713}
714
715func countUsers(e db.Engine) int64 {
716	count, _ := e.
717		Where("type=0").
718		Count(new(User))
719	return count
720}
721
722// CountUsers returns number of users.
723func CountUsers() int64 {
724	return countUsers(db.GetEngine(db.DefaultContext))
725}
726
727// GetVerifyUser get user by verify code
728func GetVerifyUser(code string) (user *User) {
729	if len(code) <= base.TimeLimitCodeLength {
730		return nil
731	}
732
733	// use tail hex username query user
734	hexStr := code[base.TimeLimitCodeLength:]
735	if b, err := hex.DecodeString(hexStr); err == nil {
736		if user, err = GetUserByName(string(b)); user != nil {
737			return user
738		}
739		log.Error("user.getVerifyUser: %v", err)
740	}
741
742	return nil
743}
744
745// VerifyUserActiveCode verifies active code when active account
746func VerifyUserActiveCode(code string) (user *User) {
747	minutes := setting.Service.ActiveCodeLives
748
749	if user = GetVerifyUser(code); user != nil {
750		// time limit code
751		prefix := code[:base.TimeLimitCodeLength]
752		data := fmt.Sprintf("%d%s%s%s%s", user.ID, user.Email, user.LowerName, user.Passwd, user.Rands)
753
754		if base.VerifyTimeLimitCode(data, minutes, prefix) {
755			return user
756		}
757	}
758	return nil
759}
760
761// ChangeUserName changes all corresponding setting from old user name to new one.
762func ChangeUserName(u *User, newUserName string) (err error) {
763	oldUserName := u.Name
764	if err = IsUsableUsername(newUserName); err != nil {
765		return err
766	}
767
768	ctx, committer, err := db.TxContext()
769	if err != nil {
770		return err
771	}
772	defer committer.Close()
773	sess := db.GetEngine(ctx)
774
775	isExist, err := isUserExist(sess, 0, newUserName)
776	if err != nil {
777		return err
778	} else if isExist {
779		return ErrUserAlreadyExist{newUserName}
780	}
781
782	if _, err = sess.Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil {
783		return fmt.Errorf("Change repo owner name: %v", err)
784	}
785
786	// Do not fail if directory does not exist
787	if err = util.Rename(UserPath(oldUserName), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
788		return fmt.Errorf("Rename user directory: %v", err)
789	}
790
791	if err = NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil {
792		return err
793	}
794
795	if err = committer.Commit(); err != nil {
796		if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) {
797			log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2)
798			return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2)
799		}
800		return err
801	}
802
803	return nil
804}
805
806// checkDupEmail checks whether there are the same email with the user
807func checkDupEmail(e db.Engine, u *User) error {
808	u.Email = strings.ToLower(u.Email)
809	has, err := e.
810		Where("id!=?", u.ID).
811		And("type=?", u.Type).
812		And("email=?", u.Email).
813		Get(new(User))
814	if err != nil {
815		return err
816	} else if has {
817		return ErrEmailAlreadyUsed{
818			Email: u.Email,
819		}
820	}
821	return nil
822}
823
824// validateUser check if user is valid to insert / update into database
825func validateUser(u *User) error {
826	if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() {
827		return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String())
828	}
829
830	u.Email = strings.ToLower(u.Email)
831	return ValidateEmail(u.Email)
832}
833
834func updateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...string) error {
835	err := validateUser(u)
836	if err != nil {
837		return err
838	}
839
840	e := db.GetEngine(ctx)
841
842	if changePrimaryEmail {
843		var emailAddress EmailAddress
844		has, err := e.Where("lower_email=?", strings.ToLower(u.Email)).Get(&emailAddress)
845		if err != nil {
846			return err
847		}
848		if !has {
849			// 1. Update old primary email
850			if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
851				IsPrimary: false,
852			}); err != nil {
853				return err
854			}
855
856			emailAddress.Email = u.Email
857			emailAddress.UID = u.ID
858			emailAddress.IsActivated = true
859			emailAddress.IsPrimary = true
860			if _, err := e.Insert(&emailAddress); err != nil {
861				return err
862			}
863		} else if _, err := e.ID(emailAddress.ID).Cols("is_primary").Update(&EmailAddress{
864			IsPrimary: true,
865		}); err != nil {
866			return err
867		}
868	} else if !u.IsOrganization() { // check if primary email in email_address table
869		primaryEmailExist, err := e.Where("uid=? AND is_primary=?", u.ID, true).Exist(&EmailAddress{})
870		if err != nil {
871			return err
872		}
873
874		if !primaryEmailExist {
875			if _, err = e.Insert(&EmailAddress{
876				Email:       u.Email,
877				UID:         u.ID,
878				IsActivated: true,
879				IsPrimary:   true,
880			}); err != nil {
881				return err
882			}
883		}
884	}
885
886	if len(cols) == 0 {
887		_, err = e.ID(u.ID).AllCols().Update(u)
888	} else {
889		_, err = e.ID(u.ID).Cols(cols...).Update(u)
890	}
891	return err
892}
893
894// UpdateUser updates user's information.
895func UpdateUser(u *User, emailChanged bool, cols ...string) error {
896	return updateUser(db.DefaultContext, u, emailChanged, cols...)
897}
898
899// UpdateUserCols update user according special columns
900func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
901	return updateUserCols(db.GetEngine(ctx), u, cols...)
902}
903
904// UpdateUserColsEngine update user according special columns
905func UpdateUserColsEngine(e db.Engine, u *User, cols ...string) error {
906	return updateUserCols(e, u, cols...)
907}
908
909func updateUserCols(e db.Engine, u *User, cols ...string) error {
910	if err := validateUser(u); err != nil {
911		return err
912	}
913
914	_, err := e.ID(u.ID).Cols(cols...).Update(u)
915	return err
916}
917
918// UpdateUserSetting updates user's settings.
919func UpdateUserSetting(u *User) (err error) {
920	ctx, committer, err := db.TxContext()
921	if err != nil {
922		return err
923	}
924	defer committer.Close()
925
926	if !u.IsOrganization() {
927		if err = checkDupEmail(db.GetEngine(ctx), u); err != nil {
928			return err
929		}
930	}
931	if err = updateUser(ctx, u, false); err != nil {
932		return err
933	}
934	return committer.Commit()
935}
936
937// GetInactiveUsers gets all inactive users
938func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) {
939	var cond builder.Cond = builder.Eq{"is_active": false}
940
941	if olderThan > 0 {
942		cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()})
943	}
944
945	users := make([]*User, 0, 10)
946	return users, db.GetEngine(ctx).
947		Where(cond).
948		Find(&users)
949}
950
951// UserPath returns the path absolute path of user repositories.
952func UserPath(userName string) string { //revive:disable-line:exported
953	return filepath.Join(setting.RepoRootPath, strings.ToLower(userName))
954}
955
956// GetUserByIDEngine returns the user object by given ID if exists.
957func GetUserByIDEngine(e db.Engine, id int64) (*User, error) {
958	u := new(User)
959	has, err := e.ID(id).Get(u)
960	if err != nil {
961		return nil, err
962	} else if !has {
963		return nil, ErrUserNotExist{id, "", 0}
964	}
965	return u, nil
966}
967
968// GetUserByID returns the user object by given ID if exists.
969func GetUserByID(id int64) (*User, error) {
970	return GetUserByIDCtx(db.DefaultContext, id)
971}
972
973// GetUserByIDCtx returns the user object by given ID if exists.
974func GetUserByIDCtx(ctx context.Context, id int64) (*User, error) {
975	return GetUserByIDEngine(db.GetEngine(ctx), id)
976}
977
978// GetUserByName returns user by given name.
979func GetUserByName(name string) (*User, error) {
980	return GetUserByNameCtx(db.DefaultContext, name)
981}
982
983// GetUserByNameCtx returns user by given name.
984func GetUserByNameCtx(ctx context.Context, name string) (*User, error) {
985	if len(name) == 0 {
986		return nil, ErrUserNotExist{0, name, 0}
987	}
988	u := &User{LowerName: strings.ToLower(name)}
989	has, err := db.GetEngine(ctx).Get(u)
990	if err != nil {
991		return nil, err
992	} else if !has {
993		return nil, ErrUserNotExist{0, name, 0}
994	}
995	return u, nil
996}
997
998// GetUserEmailsByNames returns a list of e-mails corresponds to names of users
999// that have their email notifications set to enabled or onmention.
1000func GetUserEmailsByNames(names []string) []string {
1001	return getUserEmailsByNames(db.DefaultContext, names)
1002}
1003
1004func getUserEmailsByNames(ctx context.Context, names []string) []string {
1005	mails := make([]string, 0, len(names))
1006	for _, name := range names {
1007		u, err := GetUserByNameCtx(ctx, name)
1008		if err != nil {
1009			continue
1010		}
1011		if u.IsMailable() && u.EmailNotifications() != EmailNotificationsDisabled {
1012			mails = append(mails, u.Email)
1013		}
1014	}
1015	return mails
1016}
1017
1018// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails
1019func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) {
1020	if len(ids) == 0 {
1021		return nil, nil
1022	}
1023	ous := make([]*User, 0, len(ids))
1024
1025	if isMention {
1026		return ous, db.GetEngine(db.DefaultContext).In("id", ids).
1027			Where("`type` = ?", UserTypeIndividual).
1028			And("`prohibit_login` = ?", false).
1029			And("`is_active` = ?", true).
1030			And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention).
1031			Find(&ous)
1032	}
1033
1034	return ous, db.GetEngine(db.DefaultContext).In("id", ids).
1035		Where("`type` = ?", UserTypeIndividual).
1036		And("`prohibit_login` = ?", false).
1037		And("`is_active` = ?", true).
1038		And("`email_notifications_preference` = ?", EmailNotificationsEnabled).
1039		Find(&ous)
1040}
1041
1042// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids.
1043func GetUserNamesByIDs(ids []int64) ([]string, error) {
1044	unames := make([]string, 0, len(ids))
1045	err := db.GetEngine(db.DefaultContext).In("id", ids).
1046		Table("user").
1047		Asc("name").
1048		Cols("name").
1049		Find(&unames)
1050	return unames, err
1051}
1052
1053// GetUserIDsByNames returns a slice of ids corresponds to names.
1054func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) {
1055	ids := make([]int64, 0, len(names))
1056	for _, name := range names {
1057		u, err := GetUserByName(name)
1058		if err != nil {
1059			if ignoreNonExistent {
1060				continue
1061			} else {
1062				return nil, err
1063			}
1064		}
1065		ids = append(ids, u.ID)
1066	}
1067	return ids, nil
1068}
1069
1070// GetUsersBySource returns a list of Users for a login source
1071func GetUsersBySource(s *auth.Source) ([]*User, error) {
1072	var users []*User
1073	err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users)
1074	return users, err
1075}
1076
1077// UserCommit represents a commit with validation of user.
1078type UserCommit struct { //revive:disable-line:exported
1079	User *User
1080	*git.Commit
1081}
1082
1083// ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user.
1084func ValidateCommitWithEmail(c *git.Commit) *User {
1085	if c.Author == nil {
1086		return nil
1087	}
1088	u, err := GetUserByEmail(c.Author.Email)
1089	if err != nil {
1090		return nil
1091	}
1092	return u
1093}
1094
1095// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
1096func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit {
1097	var (
1098		emails     = make(map[string]*User)
1099		newCommits = make([]*UserCommit, 0, len(oldCommits))
1100	)
1101	for _, c := range oldCommits {
1102		var u *User
1103		if c.Author != nil {
1104			if v, ok := emails[c.Author.Email]; !ok {
1105				u, _ = GetUserByEmail(c.Author.Email)
1106				emails[c.Author.Email] = u
1107			} else {
1108				u = v
1109			}
1110		}
1111
1112		newCommits = append(newCommits, &UserCommit{
1113			User:   u,
1114			Commit: c,
1115		})
1116	}
1117	return newCommits
1118}
1119
1120// GetUserByEmail returns the user object by given e-mail if exists.
1121func GetUserByEmail(email string) (*User, error) {
1122	return GetUserByEmailContext(db.DefaultContext, email)
1123}
1124
1125// GetUserByEmailContext returns the user object by given e-mail if exists with db context
1126func GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
1127	if len(email) == 0 {
1128		return nil, ErrUserNotExist{0, email, 0}
1129	}
1130
1131	email = strings.ToLower(email)
1132	// Otherwise, check in alternative list for activated email addresses
1133	emailAddress := &EmailAddress{LowerEmail: email, IsActivated: true}
1134	has, err := db.GetEngine(ctx).Get(emailAddress)
1135	if err != nil {
1136		return nil, err
1137	}
1138	if has {
1139		return GetUserByIDCtx(ctx, emailAddress.UID)
1140	}
1141
1142	// Finally, if email address is the protected email address:
1143	if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
1144		username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
1145		user := &User{}
1146		has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user)
1147		if err != nil {
1148			return nil, err
1149		}
1150		if has {
1151			return user, nil
1152		}
1153	}
1154
1155	return nil, ErrUserNotExist{0, email, 0}
1156}
1157
1158// GetUser checks if a user already exists
1159func GetUser(user *User) (bool, error) {
1160	return db.GetEngine(db.DefaultContext).Get(user)
1161}
1162
1163// GetUserByOpenID returns the user object by given OpenID if exists.
1164func GetUserByOpenID(uri string) (*User, error) {
1165	if len(uri) == 0 {
1166		return nil, ErrUserNotExist{0, uri, 0}
1167	}
1168
1169	uri, err := openid.Normalize(uri)
1170	if err != nil {
1171		return nil, err
1172	}
1173
1174	log.Trace("Normalized OpenID URI: " + uri)
1175
1176	// Otherwise, check in openid table
1177	oid := &UserOpenID{}
1178	has, err := db.GetEngine(db.DefaultContext).Where("uri=?", uri).Get(oid)
1179	if err != nil {
1180		return nil, err
1181	}
1182	if has {
1183		return GetUserByID(oid.UID)
1184	}
1185
1186	return nil, ErrUserNotExist{0, uri, 0}
1187}
1188
1189// GetAdminUser returns the first administrator
1190func GetAdminUser() (*User, error) {
1191	var admin User
1192	has, err := db.GetEngine(db.DefaultContext).Where("is_admin=?", true).Get(&admin)
1193	if err != nil {
1194		return nil, err
1195	} else if !has {
1196		return nil, ErrUserNotExist{}
1197	}
1198
1199	return &admin, nil
1200}
1201