2014-04-10 23:50:58 +05:30
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-01-24 04:00:19 +05:30
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-04-10 23:50:58 +05:30
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2019-12-15 15:21:28 +05:30
"context"
2014-04-10 23:50:58 +05:30
"crypto/sha256"
2016-12-03 11:19:17 +05:30
"crypto/subtle"
2014-04-10 23:50:58 +05:30
"encoding/hex"
"errors"
"fmt"
2021-11-16 23:48:25 +05:30
"net/url"
2014-04-10 23:50:58 +05:30
"os"
"path/filepath"
2020-02-24 01:22:05 +05:30
"regexp"
2014-04-10 23:50:58 +05:30
"strings"
"time"
2016-07-23 16:28:18 +05:30
"unicode/utf8"
2014-04-10 23:50:58 +05:30
2021-11-17 18:04:35 +05:30
_ "image/jpeg" // Needed for jpeg support
2021-09-19 17:19:59 +05:30
"code.gitea.io/gitea/models/db"
2021-09-24 17:02:56 +05:30
"code.gitea.io/gitea/models/login"
2021-11-10 01:27:58 +05:30
"code.gitea.io/gitea/models/unit"
2021-11-11 12:33:30 +05:30
user_model "code.gitea.io/gitea/models/user"
2021-11-17 15:28:31 +05:30
"code.gitea.io/gitea/modules/auth/openid"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/base"
2019-03-27 15:03:00 +05:30
"code.gitea.io/gitea/modules/git"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-02-18 21:30:27 +05:30
"code.gitea.io/gitea/modules/structs"
2019-08-15 20:16:21 +05:30
"code.gitea.io/gitea/modules/timeutil"
2017-10-24 23:06:19 +05:30
"code.gitea.io/gitea/modules/util"
2019-02-18 21:30:27 +05:30
2019-07-07 11:31:01 +05:30
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
2019-02-18 21:30:27 +05:30
"golang.org/x/crypto/pbkdf2"
2019-07-07 11:31:01 +05:30
"golang.org/x/crypto/scrypt"
2019-06-23 20:52:43 +05:30
"xorm.io/builder"
2021-10-12 23:41:35 +05:30
"xorm.io/xorm"
2014-04-10 23:50:58 +05:30
)
2016-11-28 22:17:46 +05:30
// UserType defines the user type
2014-06-25 10:14:48 +05:30
type UserType int
2014-04-10 23:50:58 +05:30
const (
2016-11-28 22:17:46 +05:30
// UserTypeIndividual defines an individual user
2016-11-07 22:23:22 +05:30
UserTypeIndividual UserType = iota // Historic reason to make it starts at 0.
2016-11-28 22:17:46 +05:30
// UserTypeOrganization defines an organization
2016-11-07 22:23:22 +05:30
UserTypeOrganization
2014-04-10 23:50:58 +05:30
)
2019-07-07 11:31:01 +05:30
const (
algoBcrypt = "bcrypt"
algoScrypt = "scrypt"
algoArgon2 = "argon2"
algoPbkdf2 = "pbkdf2"
2021-02-17 04:07:20 +05:30
)
// AvailableHashAlgorithms represents the available password hashing algorithms
var AvailableHashAlgorithms = [ ] string {
algoPbkdf2 ,
algoArgon2 ,
algoScrypt ,
algoBcrypt ,
}
2019-08-29 19:35:42 +05:30
2021-02-17 04:07:20 +05:30
const (
2019-08-29 19:35:42 +05:30
// EmailNotificationsEnabled indicates that the user would like to receive all email notifications
EmailNotificationsEnabled = "enabled"
// EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned.
EmailNotificationsOnMention = "onmention"
// EmailNotificationsDisabled indicates that the user would not like to be notified via email.
EmailNotificationsDisabled = "disabled"
2019-07-07 11:31:01 +05:30
)
2014-04-10 23:50:58 +05:30
var (
2016-11-28 22:17:46 +05:30
// ErrUserNameIllegal user name contains illegal characters error
ErrUserNameIllegal = errors . New ( "User name contains illegal characters" )
// ErrLoginSourceNotActived login source is not actived error
2014-05-11 11:42:45 +05:30
ErrLoginSourceNotActived = errors . New ( "Login source is not actived" )
2016-11-28 22:17:46 +05:30
// ErrUnsupportedLoginType login source is unknown error
ErrUnsupportedLoginType = errors . New ( "Login source is unknown" )
2020-02-24 01:22:05 +05:30
// Characters prohibited in a user name (anything except A-Za-z0-9_.-)
alphaDashDotPattern = regexp . MustCompile ( ` [^\w-\.] ` )
2014-04-10 23:50:58 +05:30
)
// User represents the object of individual and member of organization.
type User struct {
2016-07-23 22:38:22 +05:30
ID int64 ` xorm:"pk autoincr" `
2014-12-17 13:56:19 +05:30
LowerName string ` xorm:"UNIQUE NOT NULL" `
Name string ` xorm:"UNIQUE NOT NULL" `
FullName string
2015-12-21 17:54:11 +05:30
// Email is the primary email address (to be used for communication)
2019-08-29 19:35:42 +05:30
Email string ` xorm:"NOT NULL" `
KeepEmailPrivate bool
EmailNotificationsPreference string ` xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'" `
Passwd string ` xorm:"NOT NULL" `
2020-09-04 00:28:31 +05:30
PasswdHashAlgo string ` xorm:"NOT NULL DEFAULT 'argon2'" `
2018-09-13 17:34:25 +05:30
// MustChangePassword is an attribute that determines if a user
// is to change his/her password after registration.
MustChangePassword bool ` xorm:"NOT NULL DEFAULT false" `
2021-09-24 17:02:56 +05:30
LoginType login . Type
2018-09-13 17:34:25 +05:30
LoginSource int64 ` xorm:"NOT NULL DEFAULT 0" `
LoginName string
Type UserType
Location string
Website string
Rands string ` xorm:"VARCHAR(10)" `
Salt string ` xorm:"VARCHAR(10)" `
Language string ` xorm:"VARCHAR(5)" `
2019-03-19 07:58:10 +05:30
Description string
2016-03-10 06:23:30 +05:30
2019-08-15 20:16:21 +05:30
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"INDEX updated" `
LastLoginUnix timeutil . TimeStamp ` xorm:"INDEX" `
2014-11-21 21:28:08 +05:30
2015-10-25 13:56:26 +05:30
// Remember visibility choice for convenience, true for private
2015-08-28 14:14:04 +05:30
LastRepoVisibility bool
2016-11-27 17:29:12 +05:30
// Maximum repository creation limit, -1 means use global default
2015-12-10 23:18:45 +05:30
MaxRepoCreation int ` xorm:"NOT NULL DEFAULT -1" `
2015-08-28 14:14:04 +05:30
2021-11-09 16:12:58 +05:30
// IsActive true: primary email is activated, user can access Web UI and Git SSH.
// false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access.
IsActive bool ` xorm:"INDEX" `
// the user is a Gitea admin, who can access all repositories and the admin pages.
IsAdmin bool
// true: the user is only allowed to see organizations/repositories that they has explicit rights to.
// (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public)
IsRestricted bool ` xorm:"NOT NULL DEFAULT false" `
2016-12-31 08:03:30 +05:30
AllowGitHook bool
AllowImportLocal bool // Allow migrate repository by local path
AllowCreateOrganization bool ` xorm:"DEFAULT true" `
2021-11-09 16:12:58 +05:30
// 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)
ProhibitLogin bool ` xorm:"NOT NULL DEFAULT false" `
2014-11-21 21:28:08 +05:30
2015-12-21 17:54:11 +05:30
// Avatar
2014-11-21 21:28:08 +05:30
Avatar string ` xorm:"VARCHAR(2048) NOT NULL" `
AvatarEmail string ` xorm:"NOT NULL" `
UseCustomAvatar bool
2015-12-21 17:54:11 +05:30
// Counters
NumFollowers int
2015-12-22 05:39:28 +05:30
NumFollowing int ` xorm:"NOT NULL DEFAULT 0" `
2015-12-21 17:54:11 +05:30
NumStars int
NumRepos int
2014-06-25 10:14:48 +05:30
2015-12-21 17:54:11 +05:30
// For organization
2019-09-24 01:38:03 +05:30
NumTeams int
NumMembers int
Visibility structs . VisibleType ` xorm:"NOT NULL DEFAULT 0" `
RepoAdminChangeTeamAccess bool ` xorm:"NOT NULL DEFAULT false" `
2016-11-13 08:24:04 +05:30
// Preferences
2020-06-06 01:31:53 +05:30
DiffViewStyle string ` xorm:"NOT NULL DEFAULT ''" `
Theme string ` xorm:"NOT NULL DEFAULT ''" `
KeepActivityPrivate bool ` xorm:"NOT NULL DEFAULT false" `
2014-04-10 23:50:58 +05:30
}
2021-09-19 17:19:59 +05:30
func init ( ) {
db . RegisterModel ( new ( User ) )
}
2020-01-25 00:30:29 +05:30
// SearchOrganizationsOptions options to filter organizations
type SearchOrganizationsOptions struct {
2021-09-24 17:02:56 +05:30
db . ListOptions
2020-01-25 00:30:29 +05:30
All bool
}
2019-04-23 02:10:51 +05:30
// ColorFormat writes a colored string to identify this struct
func ( u * User ) ColorFormat ( s fmt . State ) {
log . ColorFprintf ( s , "%d:%s" ,
log . NewColoredIDValue ( u . ID ) ,
log . NewColoredValue ( u . Name ) )
}
2016-11-28 22:17:46 +05:30
// BeforeUpdate is invoked from XORM before updating this object.
2015-12-10 23:07:53 +05:30
func ( u * User ) BeforeUpdate ( ) {
2015-12-10 23:16:05 +05:30
if u . MaxRepoCreation < - 1 {
u . MaxRepoCreation = - 1
2015-12-10 23:07:53 +05:30
}
2018-01-13 15:15:16 +05:30
// Organization does not need email
u . Email = strings . ToLower ( u . Email )
if ! u . IsOrganization ( ) {
if len ( u . AvatarEmail ) == 0 {
u . AvatarEmail = u . Email
}
}
u . LowerName = strings . ToLower ( u . Name )
u . Location = base . TruncateString ( u . Location , 255 )
u . Website = base . TruncateString ( u . Website , 255 )
u . Description = base . TruncateString ( u . Description , 255 )
2015-12-10 23:07:53 +05:30
}
2019-01-09 22:52:57 +05:30
// AfterLoad is invoked from XORM after filling all the fields of this object.
func ( u * User ) AfterLoad ( ) {
if u . Theme == "" {
u . Theme = setting . UI . DefaultTheme
}
}
2016-11-28 22:17:46 +05:30
// SetLastLogin set time to last login
2016-11-09 16:23:45 +05:30
func ( u * User ) SetLastLogin ( ) {
2019-08-15 20:16:21 +05:30
u . LastLoginUnix = timeutil . TimeStampNow ( )
2016-11-09 16:23:45 +05:30
}
2021-11-22 20:51:55 +05:30
// UpdateUserDiffViewStyle updates the users diff view style
func UpdateUserDiffViewStyle ( u * User , style string ) error {
2016-11-13 08:24:04 +05:30
u . DiffViewStyle = style
2021-11-22 20:51:55 +05:30
return UpdateUserCols ( db . DefaultContext , u , "diff_view_style" )
2016-11-13 08:24:04 +05:30
}
2021-11-22 20:51:55 +05:30
// UpdateUserTheme updates a users' theme irrespective of the site wide theme
func UpdateUserTheme ( u * User , themeName string ) error {
2019-01-09 22:52:57 +05:30
u . Theme = themeName
2021-11-22 20:51:55 +05:30
return UpdateUserCols ( db . DefaultContext , u , "theme" )
2019-01-09 22:52:57 +05:30
}
2019-07-27 18:45:30 +05:30
// GetEmail returns an noreply email, if the user has set to keep his
2017-01-08 08:42:03 +05:30
// email address private, otherwise the primary email address.
2019-07-27 18:45:30 +05:30
func ( u * User ) GetEmail ( ) string {
2017-01-08 08:42:03 +05:30
if u . KeepEmailPrivate {
return fmt . Sprintf ( "%s@%s" , u . LowerName , setting . Service . NoReplyAddress )
}
return u . Email
}
2021-04-09 13:46:10 +05:30
// GetAllUsers returns a slice of all individual users found in DB.
2020-10-16 08:18:38 +05:30
func GetAllUsers ( ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 )
2021-09-23 21:15:36 +05:30
return users , db . GetEngine ( db . DefaultContext ) . OrderBy ( "id" ) . Where ( "type = ?" , UserTypeIndividual ) . Find ( & users )
2020-10-16 08:18:38 +05:30
}
2016-11-28 22:17:46 +05:30
// IsLocal returns true if user login type is LoginPlain.
2015-12-11 05:32:57 +05:30
func ( u * User ) IsLocal ( ) bool {
2021-09-24 17:02:56 +05:30
return u . LoginType <= login . Plain
2015-12-11 05:32:57 +05:30
}
2017-02-22 12:44:37 +05:30
// IsOAuth2 returns true if user login type is LoginOAuth2.
func ( u * User ) IsOAuth2 ( ) bool {
2021-09-24 17:02:56 +05:30
return u . LoginType == login . OAuth2
2017-02-22 12:44:37 +05:30
}
2017-05-24 05:57:08 +05:30
// MaxCreationLimit returns the number of repositories a user is allowed to create
func ( u * User ) MaxCreationLimit ( ) int {
2015-12-10 23:16:05 +05:30
if u . MaxRepoCreation <= - 1 {
2015-12-10 23:07:53 +05:30
return setting . Repository . MaxCreationLimit
}
return u . MaxRepoCreation
}
2016-11-28 22:17:46 +05:30
// CanCreateRepo returns if user login can create a repository
2020-04-30 20:41:56 +05:30
// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
2015-12-10 23:07:53 +05:30
func ( u * User ) CanCreateRepo ( ) bool {
2017-05-20 09:21:19 +05:30
if u . IsAdmin {
return true
}
2015-12-10 23:16:05 +05:30
if u . MaxRepoCreation <= - 1 {
2015-12-12 01:41:13 +05:30
if setting . Repository . MaxCreationLimit <= - 1 {
2015-12-11 02:57:47 +05:30
return true
}
2015-12-10 23:07:53 +05:30
return u . NumRepos < setting . Repository . MaxCreationLimit
}
return u . NumRepos < u . MaxRepoCreation
}
2016-12-31 08:03:30 +05:30
// CanCreateOrganization returns true if user can create organisation.
func ( u * User ) CanCreateOrganization ( ) bool {
2017-02-14 17:46:00 +05:30
return u . IsAdmin || ( u . AllowCreateOrganization && ! setting . Admin . DisableRegularOrgCreation )
2016-12-31 08:03:30 +05:30
}
2015-11-04 05:10:52 +05:30
// CanEditGitHook returns true if user can edit Git hooks.
func ( u * User ) CanEditGitHook ( ) bool {
2017-09-12 14:55:42 +05:30
return ! setting . DisableGitHooks && ( u . IsAdmin || u . AllowGitHook )
2015-11-04 05:10:52 +05:30
}
// CanImportLocal returns true if user can migrate repository by local path.
func ( u * User ) CanImportLocal ( ) bool {
2021-03-16 03:22:11 +05:30
if ! setting . ImportLocalPaths || u == nil {
2017-01-23 06:49:50 +05:30
return false
}
2015-11-04 05:10:52 +05:30
return u . IsAdmin || u . AllowImportLocal
}
2014-07-26 09:54:27 +05:30
// DashboardLink returns the user dashboard page link.
func ( u * User ) DashboardLink ( ) string {
if u . IsOrganization ( ) {
2021-10-07 23:57:38 +05:30
return u . OrganisationLink ( ) + "/dashboard"
2014-07-26 09:54:27 +05:30
}
2016-11-27 15:44:25 +05:30
return setting . AppSubURL + "/"
2014-07-26 09:54:27 +05:30
}
2015-08-26 19:15:51 +05:30
// HomeLink returns the user or organization home page link.
2014-06-25 10:14:48 +05:30
func ( u * User ) HomeLink ( ) string {
2021-11-16 23:48:25 +05:30
return setting . AppSubURL + "/" + url . PathEscape ( u . Name )
2014-04-10 23:50:58 +05:30
}
2015-09-17 11:24:12 +05:30
2017-02-11 18:27:33 +05:30
// HTMLURL returns the user or organization's full link.
func ( u * User ) HTMLURL ( ) string {
2021-11-16 23:48:25 +05:30
return setting . AppURL + url . PathEscape ( u . Name )
2017-02-11 18:27:33 +05:30
}
2021-04-30 22:55:13 +05:30
// OrganisationLink returns the organization sub page link.
func ( u * User ) OrganisationLink ( ) string {
2021-11-16 23:48:25 +05:30
return setting . AppSubURL + "/org/" + url . PathEscape ( u . Name )
2021-04-30 22:55:13 +05:30
}
2015-09-17 11:24:12 +05:30
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
func ( u * User ) GenerateEmailActivateCode ( email string ) string {
code := base . CreateTimeLimitCode (
2020-12-25 15:29:32 +05:30
fmt . Sprintf ( "%d%s%s%s%s" , u . ID , email , u . LowerName , u . Passwd , u . Rands ) ,
2015-09-17 11:24:12 +05:30
setting . Service . ActiveCodeLives , nil )
// Add tail hex username
code += hex . EncodeToString ( [ ] byte ( u . LowerName ) )
return code
}
2021-11-22 20:51:55 +05:30
// GetUserFollowers returns range of user's followers.
func GetUserFollowers ( u * User , listOptions db . ListOptions ) ( [ ] * User , error ) {
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) .
2018-10-19 22:19:36 +05:30
Where ( "follow.follow_id=?" , u . ID ) .
Join ( "LEFT" , "follow" , "`user`.id=follow.user_id" )
2020-01-25 00:30:29 +05:30
if listOptions . Page != 0 {
2021-09-24 17:02:56 +05:30
sess = db . SetSessionPagination ( sess , & listOptions )
2020-01-25 00:30:29 +05:30
users := make ( [ ] * User , 0 , listOptions . PageSize )
return users , sess . Find ( & users )
}
users := make ( [ ] * User , 0 , 8 )
2015-12-21 17:54:11 +05:30
return users , sess . Find ( & users )
}
2021-11-22 20:51:55 +05:30
// GetUserFollowing returns range of user's following.
func GetUserFollowing ( u * User , listOptions db . ListOptions ) ( [ ] * User , error ) {
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) .
2018-10-19 22:19:36 +05:30
Where ( "follow.user_id=?" , u . ID ) .
Join ( "LEFT" , "follow" , "`user`.id=follow.follow_id" )
2020-01-25 00:30:29 +05:30
if listOptions . Page != 0 {
2021-09-24 17:02:56 +05:30
sess = db . SetSessionPagination ( sess , & listOptions )
2020-01-25 00:30:29 +05:30
users := make ( [ ] * User , 0 , listOptions . PageSize )
return users , sess . Find ( & users )
}
users := make ( [ ] * User , 0 , 8 )
2015-12-21 17:54:11 +05:30
return users , sess . Find ( & users )
}
2014-04-10 23:50:58 +05:30
// NewGitSig generates and returns the signature of given user.
2014-06-25 10:14:48 +05:30
func ( u * User ) NewGitSig ( ) * git . Signature {
2014-04-10 23:50:58 +05:30
return & git . Signature {
2019-01-24 19:42:17 +05:30
Name : u . GitName ( ) ,
2019-07-27 18:45:30 +05:30
Email : u . GetEmail ( ) ,
2014-04-10 23:50:58 +05:30
When : time . Now ( ) ,
}
}
2019-07-07 11:31:01 +05:30
func hashPassword ( passwd , salt , algo string ) string {
var tempPasswd [ ] byte
switch algo {
case algoBcrypt :
tempPasswd , _ = bcrypt . GenerateFromPassword ( [ ] byte ( passwd ) , bcrypt . DefaultCost )
return string ( tempPasswd )
case algoScrypt :
tempPasswd , _ = scrypt . Key ( [ ] byte ( passwd ) , [ ] byte ( salt ) , 65536 , 16 , 2 , 50 )
case algoArgon2 :
tempPasswd = argon2 . IDKey ( [ ] byte ( passwd ) , [ ] byte ( salt ) , 2 , 65536 , 8 , 50 )
case algoPbkdf2 :
fallthrough
default :
tempPasswd = pbkdf2 . Key ( [ ] byte ( passwd ) , [ ] byte ( salt ) , 10000 , 50 , sha256 . New )
}
2018-01-12 03:49:38 +05:30
return fmt . Sprintf ( "%x" , tempPasswd )
}
2021-01-10 23:35:18 +05:30
// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO
// change passwd, salt and passwd_hash_algo fields
func ( u * User ) SetPassword ( passwd string ) ( err error ) {
if len ( passwd ) == 0 {
u . Passwd = ""
u . Salt = ""
u . PasswdHashAlgo = ""
return nil
}
if u . Salt , err = GetUserSalt ( ) ; err != nil {
return err
}
2019-07-07 11:31:01 +05:30
u . PasswdHashAlgo = setting . PasswordHashAlgo
u . Passwd = hashPassword ( passwd , u . Salt , setting . PasswordHashAlgo )
2021-01-10 23:35:18 +05:30
return nil
2014-06-25 10:14:48 +05:30
}
2015-04-17 00:10:39 +05:30
// ValidatePassword checks if given password matches the one belongs to the user.
2015-04-17 00:06:32 +05:30
func ( u * User ) ValidatePassword ( passwd string ) bool {
2019-07-07 11:31:01 +05:30
tempHash := hashPassword ( passwd , u . Salt , u . PasswdHashAlgo )
if u . PasswdHashAlgo != algoBcrypt && subtle . ConstantTimeCompare ( [ ] byte ( u . Passwd ) , [ ] byte ( tempHash ) ) == 1 {
return true
}
if u . PasswdHashAlgo == algoBcrypt && bcrypt . CompareHashAndPassword ( [ ] byte ( u . Passwd ) , [ ] byte ( passwd ) ) == nil {
return true
}
return false
2014-08-02 23:17:33 +05:30
}
2017-02-22 12:44:37 +05:30
// IsPasswordSet checks if the password is set or left empty
func ( u * User ) IsPasswordSet ( ) bool {
2021-01-10 23:35:18 +05:30
return len ( u . Passwd ) != 0
2017-02-22 12:44:37 +05:30
}
2021-11-22 20:51:55 +05:30
// IsUserVisibleToViewer check if viewer is able to see user profile
func IsUserVisibleToViewer ( u * User , viewer * User ) bool {
return isUserVisibleToViewer ( db . GetEngine ( db . DefaultContext ) , u , viewer )
2021-06-27 01:23:14 +05:30
}
2021-11-22 20:51:55 +05:30
func isUserVisibleToViewer ( e db . Engine , u * User , viewer * User ) bool {
2021-06-27 01:23:14 +05:30
if viewer != nil && viewer . IsAdmin {
return true
}
switch u . Visibility {
case structs . VisibleTypePublic :
return true
case structs . VisibleTypeLimited :
if viewer == nil || viewer . IsRestricted {
return false
}
return true
case structs . VisibleTypePrivate :
if viewer == nil || viewer . IsRestricted {
return false
}
// If they follow - they see each over
2021-11-17 15:28:31 +05:30
follower := user_model . IsFollowing ( u . ID , viewer . ID )
2021-06-27 01:23:14 +05:30
if follower {
return true
}
// Now we need to check if they in some organization together
2021-11-09 14:24:46 +05:30
count , err := e . Table ( "team_user" ) .
2021-06-27 01:23:14 +05:30
Where (
builder . And (
builder . Eq { "uid" : viewer . ID } ,
builder . Or (
builder . Eq { "org_id" : u . ID } ,
builder . In ( "org_id" ,
builder . Select ( "org_id" ) .
From ( "team_user" , "t2" ) .
Where ( builder . Eq { "uid" : u . ID } ) ) ) ) ) .
Count ( new ( TeamUser ) )
if err != nil {
return false
}
if count < 0 {
// No common organization
return false
}
// they are in an organization together
return true
}
return false
}
2014-06-28 10:10:07 +05:30
// IsOrganization returns true if user is actually a organization.
2014-06-25 10:14:48 +05:30
func ( u * User ) IsOrganization ( ) bool {
2016-11-07 22:23:22 +05:30
return u . Type == UserTypeOrganization
2014-06-25 10:14:48 +05:30
}
2021-11-18 23:12:27 +05:30
// GetOrganizationCount returns count of membership of organization of the user.
func GetOrganizationCount ( ctx context . Context , u * User ) ( int64 , error ) {
return db . GetEngine ( ctx ) .
2016-11-10 20:46:32 +05:30
Where ( "uid=?" , u . ID ) .
Count ( new ( OrgUser ) )
2015-09-06 18:24:08 +05:30
}
2019-10-08 23:25:16 +05:30
// GetRepositoryIDs returns repositories IDs where user owned and has unittypes
2020-01-17 13:04:37 +05:30
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetRepositoryIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2019-10-08 23:25:16 +05:30
var ids [ ] int64
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) . Table ( "repository" ) . Cols ( "repository.id" )
2018-06-21 21:30:13 +05:30
if len ( units ) > 0 {
2019-10-08 23:25:16 +05:30
sess = sess . Join ( "INNER" , "repo_unit" , "repository.id = repo_unit.repo_id" )
sess = sess . In ( "repo_unit.type" , units )
2018-06-21 21:30:13 +05:30
}
2019-10-08 23:25:16 +05:30
return ids , sess . Where ( "owner_id = ?" , u . ID ) . Find ( & ids )
2017-02-17 06:28:19 +05:30
}
2021-01-13 09:49:17 +05:30
// GetActiveRepositoryIDs returns non-archived repositories IDs where user owned and has unittypes
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetActiveRepositoryIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2021-01-13 09:49:17 +05:30
var ids [ ] int64
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) . Table ( "repository" ) . Cols ( "repository.id" )
2021-01-13 09:49:17 +05:30
if len ( units ) > 0 {
sess = sess . Join ( "INNER" , "repo_unit" , "repository.id = repo_unit.repo_id" )
sess = sess . In ( "repo_unit.type" , units )
}
sess . Where ( builder . Eq { "is_archived" : false } )
return ids , sess . Where ( "owner_id = ?" , u . ID ) . GroupBy ( "repository.id" ) . Find ( & ids )
}
2019-10-08 23:25:16 +05:30
// GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes
2020-01-17 13:04:37 +05:30
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetOrgRepositoryIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2019-10-08 23:25:16 +05:30
var ids [ ] int64
2021-09-23 21:15:36 +05:30
if err := db . GetEngine ( db . DefaultContext ) . Table ( "repository" ) .
2019-10-08 23:25:16 +05:30
Cols ( "repository.id" ) .
Join ( "INNER" , "team_user" , "repository.owner_id = team_user.org_id" ) .
2020-01-13 23:03:46 +05:30
Join ( "INNER" , "team_repo" , "(? != ? and repository.is_private != ?) OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)" , true , u . IsRestricted , true ) .
2020-01-05 06:53:29 +05:30
Where ( "team_user.uid = ?" , u . ID ) .
GroupBy ( "repository.id" ) . Find ( & ids ) ; err != nil {
return nil , err
}
2019-10-08 23:25:16 +05:30
2018-06-21 21:30:13 +05:30
if len ( units ) > 0 {
2020-01-05 06:53:29 +05:30
return FilterOutRepoIdsWithoutUnitAccess ( u , ids , units ... )
2019-10-08 23:25:16 +05:30
}
2020-01-05 06:53:29 +05:30
return ids , nil
2019-10-08 23:25:16 +05:30
}
2021-01-13 09:49:17 +05:30
// GetActiveOrgRepositoryIDs returns non-archived repositories IDs where user's team owned and has unittypes
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetActiveOrgRepositoryIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2021-01-13 09:49:17 +05:30
var ids [ ] int64
2021-09-23 21:15:36 +05:30
if err := db . GetEngine ( db . DefaultContext ) . Table ( "repository" ) .
2021-01-13 09:49:17 +05:30
Cols ( "repository.id" ) .
Join ( "INNER" , "team_user" , "repository.owner_id = team_user.org_id" ) .
Join ( "INNER" , "team_repo" , "(? != ? and repository.is_private != ?) OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)" , true , u . IsRestricted , true ) .
Where ( "team_user.uid = ?" , u . ID ) .
Where ( builder . Eq { "is_archived" : false } ) .
GroupBy ( "repository.id" ) . Find ( & ids ) ; err != nil {
return nil , err
}
if len ( units ) > 0 {
return FilterOutRepoIdsWithoutUnitAccess ( u , ids , units ... )
}
return ids , nil
}
2019-10-08 23:25:16 +05:30
// GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
2020-01-17 13:04:37 +05:30
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetAccessRepoIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2019-10-08 23:25:16 +05:30
ids , err := u . GetRepositoryIDs ( units ... )
if err != nil {
return nil , err
}
ids2 , err := u . GetOrgRepositoryIDs ( units ... )
if err != nil {
return nil , err
}
return append ( ids , ids2 ... ) , nil
2017-02-17 06:28:19 +05:30
}
2021-01-13 09:49:17 +05:30
// GetActiveAccessRepoIDs returns all non-archived repositories IDs where user's or user is a team member organizations
// Caller shall check that units is not globally disabled
2021-11-10 01:27:58 +05:30
func ( u * User ) GetActiveAccessRepoIDs ( units ... unit . Type ) ( [ ] int64 , error ) {
2021-01-13 09:49:17 +05:30
ids , err := u . GetActiveRepositoryIDs ( units ... )
if err != nil {
return nil , err
}
ids2 , err := u . GetActiveOrgRepositoryIDs ( units ... )
if err != nil {
return nil , err
}
return append ( ids , ids2 ... ) , nil
}
2015-08-27 10:56:38 +05:30
// DisplayName returns full name if it's not empty,
// returns username otherwise.
func ( u * User ) DisplayName ( ) string {
2019-01-24 19:42:17 +05:30
trimmed := strings . TrimSpace ( u . FullName )
if len ( trimmed ) > 0 {
return trimmed
2014-09-17 18:41:51 +05:30
}
2015-08-27 10:56:38 +05:30
return u . Name
2014-09-17 18:41:51 +05:30
}
2019-05-08 14:11:35 +05:30
// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
// returns username otherwise.
func ( u * User ) GetDisplayName ( ) string {
2020-02-27 03:38:24 +05:30
if setting . UI . DefaultShowFullName {
trimmed := strings . TrimSpace ( u . FullName )
if len ( trimmed ) > 0 {
return trimmed
}
2019-05-08 14:11:35 +05:30
}
return u . Name
}
2019-01-24 19:42:17 +05:30
func gitSafeName ( name string ) string {
return strings . TrimSpace ( strings . NewReplacer ( "\n" , "" , "<" , "" , ">" , "" ) . Replace ( name ) )
}
// GitName returns a git safe name
func ( u * User ) GitName ( ) string {
gitName := gitSafeName ( u . FullName )
if len ( gitName ) > 0 {
return gitName
}
// Although u.Name should be safe if created in our system
// LDAP users may have bad names
gitName = gitSafeName ( u . Name )
if len ( gitName ) > 0 {
return gitName
}
// Totally pathological name so it's got to be:
return fmt . Sprintf ( "user-%d" , u . ID )
}
2016-11-28 22:17:46 +05:30
// ShortName ellipses username to length
2015-11-19 04:12:20 +05:30
func ( u * User ) ShortName ( length int ) string {
2016-01-11 18:11:43 +05:30
return base . EllipsisString ( u . Name , length )
2015-11-19 04:12:20 +05:30
}
2017-03-15 06:22:01 +05:30
// IsMailable checks if a user is eligible
2017-02-02 18:03:36 +05:30
// to receive emails.
func ( u * User ) IsMailable ( ) bool {
return u . IsActive
}
2019-08-29 19:35:42 +05:30
// EmailNotifications returns the User's email notification preference
func ( u * User ) EmailNotifications ( ) string {
return u . EmailNotificationsPreference
}
// SetEmailNotifications sets the user's email notification preference
2021-11-22 20:51:55 +05:30
func SetEmailNotifications ( u * User , set string ) error {
2019-08-29 19:35:42 +05:30
u . EmailNotificationsPreference = set
2021-11-22 20:51:55 +05:30
if err := UpdateUserCols ( db . DefaultContext , u , "email_notifications_preference" ) ; err != nil {
2019-08-29 19:35:42 +05:30
log . Error ( "SetEmailNotifications: %v" , err )
return err
}
return nil
}
2021-09-19 17:19:59 +05:30
func isUserExist ( e db . Engine , uid int64 , name string ) ( bool , error ) {
2014-04-10 23:50:58 +05:30
if len ( name ) == 0 {
return false , nil
}
2017-10-03 11:59:26 +05:30
return e .
2016-11-10 20:46:32 +05:30
Where ( "id!=?" , uid ) .
Get ( & User { LowerName : strings . ToLower ( name ) } )
2014-04-10 23:50:58 +05:30
}
2017-10-03 11:59:26 +05:30
// IsUserExist checks if given user name exist,
// the user name should be noncased unique.
// If uid is presented, then check will rule out that one,
// it is used when update a user name in settings page.
func IsUserExist ( uid int64 , name string ) ( bool , error ) {
2021-09-23 21:15:36 +05:30
return isUserExist ( db . GetEngine ( db . DefaultContext ) , uid , name )
2017-10-03 11:59:26 +05:30
}
2017-03-15 06:22:01 +05:30
// GetUserSalt returns a random user salt token.
2016-12-20 18:02:02 +05:30
func GetUserSalt ( ) ( string , error ) {
2021-05-10 12:15:17 +05:30
return util . RandomString ( 10 )
2014-04-10 23:50:58 +05:30
}
2016-08-14 16:02:24 +05:30
// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
func NewGhostUser ( ) * User {
2015-08-15 00:18:05 +05:30
return & User {
2016-07-23 22:38:22 +05:30
ID : - 1 ,
2016-08-14 16:02:24 +05:30
Name : "Ghost" ,
LowerName : "ghost" ,
2015-08-15 00:18:05 +05:30
}
}
2020-01-15 16:44:07 +05:30
// NewReplaceUser creates and returns a fake user for external user
func NewReplaceUser ( name string ) * User {
return & User {
ID : - 1 ,
Name : name ,
LowerName : strings . ToLower ( name ) ,
}
}
2020-01-02 04:21:10 +05:30
// IsGhost check if user is fake user for a deleted account
func ( u * User ) IsGhost ( ) bool {
if u == nil {
return false
}
return u . ID == - 1 && u . Name == "Ghost"
}
2016-07-23 16:28:18 +05:30
var (
2021-04-28 18:05:06 +05:30
reservedUsernames = [ ] string {
2020-04-19 02:31:06 +05:30
"." ,
".." ,
".well-known" ,
2018-08-13 10:32:18 +05:30
"admin" ,
"api" ,
"assets" ,
2020-04-19 02:31:06 +05:30
"attachments" ,
2018-08-13 10:32:18 +05:30
"avatars" ,
2021-03-08 22:19:29 +05:30
"captcha" ,
2018-08-13 10:32:18 +05:30
"commits" ,
"debug" ,
"error" ,
"explore" ,
2021-05-13 00:06:53 +05:30
"favicon.ico" ,
2019-02-28 05:34:44 +05:30
"ghost" ,
2018-08-13 10:32:18 +05:30
"help" ,
"install" ,
"issues" ,
"less" ,
2020-04-19 02:31:06 +05:30
"login" ,
2020-02-16 13:26:49 +05:30
"manifest.json" ,
2018-11-05 08:50:00 +05:30
"metrics" ,
2020-02-16 13:26:49 +05:30
"milestones" ,
2018-08-13 10:32:18 +05:30
"new" ,
2019-02-28 05:34:44 +05:30
"notifications" ,
2018-08-13 10:32:18 +05:30
"org" ,
"plugins" ,
"pulls" ,
"raw" ,
"repo" ,
2020-04-19 02:31:06 +05:30
"robots.txt" ,
"search" ,
2021-05-13 00:06:53 +05:30
"serviceworker.js" ,
2018-08-13 10:32:18 +05:30
"stars" ,
"template" ,
"user" ,
2021-04-28 18:05:06 +05:30
}
2020-06-20 18:50:25 +05:30
2021-07-01 20:43:20 +05:30
reservedUserPatterns = [ ] string { "*.keys" , "*.gpg" , "*.rss" , "*.atom" }
2016-07-23 16:28:18 +05:30
)
// isUsableName checks if name is reserved or pattern of name is not allowed
2016-11-12 17:56:45 +05:30
// based on given reserved names and patterns.
2016-07-23 16:28:18 +05:30
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
func isUsableName ( names , patterns [ ] string , name string ) error {
name = strings . TrimSpace ( strings . ToLower ( name ) )
if utf8 . RuneCountInString ( name ) == 0 {
return ErrNameEmpty
}
for i := range names {
if name == names [ i ] {
return ErrNameReserved { name }
}
}
for _ , pat := range patterns {
if pat [ 0 ] == '*' && strings . HasSuffix ( name , pat [ 1 : ] ) ||
( pat [ len ( pat ) - 1 ] == '*' && strings . HasPrefix ( name , pat [ : len ( pat ) - 1 ] ) ) {
return ErrNamePatternNotAllowed { pat }
}
}
return nil
}
2016-11-28 22:17:46 +05:30
// IsUsableUsername returns an error when a username is reserved
2016-07-23 16:28:18 +05:30
func IsUsableUsername ( name string ) error {
2020-02-24 01:22:05 +05:30
// 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 }
}
2016-11-12 17:56:45 +05:30
return isUsableName ( reservedUsernames , reservedUserPatterns , name )
2016-07-23 16:28:18 +05:30
}
2021-06-27 01:23:14 +05:30
// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
type CreateUserOverwriteOptions struct {
Visibility structs . VisibleType
}
2014-06-25 10:14:48 +05:30
// CreateUser creates record of a new user.
2021-06-27 01:23:14 +05:30
func CreateUser ( u * User , overwriteDefault ... * CreateUserOverwriteOptions ) ( err error ) {
2016-07-23 16:28:18 +05:30
if err = IsUsableUsername ( u . Name ) ; err != nil {
2015-03-27 02:41:47 +05:30
return err
2014-06-25 10:14:48 +05:30
}
2021-06-28 00:17:35 +05:30
// set system defaults
u . KeepEmailPrivate = setting . Service . DefaultKeepEmailPrivate
u . Visibility = setting . Service . DefaultUserVisibilityMode
u . AllowCreateOrganization = setting . Service . DefaultAllowCreateOrganization && ! setting . Admin . DisableRegularOrgCreation
u . EmailNotificationsPreference = setting . Admin . DefaultEmailNotification
u . MaxRepoCreation = - 1
u . Theme = setting . UI . DefaultTheme
// overwrite defaults if set
if len ( overwriteDefault ) != 0 && overwriteDefault [ 0 ] != nil {
u . Visibility = overwriteDefault [ 0 ] . Visibility
}
2021-11-11 12:33:30 +05:30
ctx , committer , err := db . TxContext ( )
if err != nil {
2017-10-03 11:59:26 +05:30
return err
}
2021-11-11 12:33:30 +05:30
defer committer . Close ( )
2017-10-03 11:59:26 +05:30
2021-11-11 12:33:30 +05:30
sess := db . GetEngine ( ctx )
2014-06-25 10:14:48 +05:30
2021-11-11 12:33:30 +05:30
// validate data
2021-06-28 00:17:35 +05:30
if err := validateUser ( u ) ; err != nil {
2021-01-24 20:53:05 +05:30
return err
}
2021-06-28 00:17:35 +05:30
isExist , err := isUserExist ( sess , 0 , u . Name )
if err != nil {
2020-11-21 03:15:55 +05:30
return err
2021-06-28 00:17:35 +05:30
} else if isExist {
return ErrUserAlreadyExist { u . Name }
2020-11-14 22:23:43 +05:30
}
2021-11-11 12:33:30 +05:30
isExist , err = user_model . IsEmailUsed ( ctx , u . Email )
2014-06-25 10:14:48 +05:30
if err != nil {
2014-07-26 09:54:27 +05:30
return err
2014-06-25 10:14:48 +05:30
} else if isExist {
2021-11-11 12:33:30 +05:30
return user_model . ErrEmailAlreadyUsed {
Email : u . Email ,
}
2014-06-25 10:14:48 +05:30
}
2021-06-28 00:17:35 +05:30
// prepare for database
2014-06-25 10:14:48 +05:30
u . LowerName = strings . ToLower ( u . Name )
u . AvatarEmail = u . Email
2016-12-20 18:02:02 +05:30
if u . Rands , err = GetUserSalt ( ) ; err != nil {
return err
}
2021-01-10 23:35:18 +05:30
if err = u . SetPassword ( u . Passwd ) ; err != nil {
2016-12-20 18:02:02 +05:30
return err
}
2021-06-27 01:23:14 +05:30
2021-06-28 00:17:35 +05:30
// save changes to database
2021-11-11 12:33:30 +05:30
if err = user_model . DeleteUserRedirect ( ctx , u . Name ) ; err != nil {
2021-06-28 00:17:35 +05:30
return err
2021-06-27 01:23:14 +05:30
}
2014-06-25 10:14:48 +05:30
2021-11-11 12:33:30 +05:30
if err = db . Insert ( ctx , u ) ; err != nil {
2014-07-26 09:54:27 +05:30
return err
2014-06-25 10:14:48 +05:30
}
2014-04-22 22:25:27 +05:30
2021-06-08 09:22:51 +05:30
// insert email address
2021-11-11 12:33:30 +05:30
if err := db . Insert ( ctx , & user_model . EmailAddress {
2021-06-08 09:22:51 +05:30
UID : u . ID ,
Email : u . Email ,
LowerEmail : strings . ToLower ( u . Email ) ,
IsActivated : u . IsActive ,
IsPrimary : true ,
} ) ; err != nil {
return err
}
2021-11-11 12:33:30 +05:30
return committer . Commit ( )
2014-06-25 10:14:48 +05:30
}
2021-09-19 17:19:59 +05:30
func countUsers ( e db . Engine ) int64 {
2016-11-10 20:46:32 +05:30
count , _ := e .
Where ( "type=0" ) .
Count ( new ( User ) )
2015-08-06 21:30:11 +05:30
return count
}
2014-07-07 13:45:08 +05:30
// CountUsers returns number of users.
func CountUsers ( ) int64 {
2021-09-23 21:15:36 +05:30
return countUsers ( db . GetEngine ( db . DefaultContext ) )
2014-07-07 13:45:08 +05:30
}
2017-01-05 06:20:34 +05:30
// get user by verify code
2014-04-10 23:50:58 +05:30
func getVerifyUser ( code string ) ( user * User ) {
if len ( code ) <= base . TimeLimitCodeLength {
return nil
}
// use tail hex username query user
hexStr := code [ base . TimeLimitCodeLength : ]
if b , err := hex . DecodeString ( hexStr ) ; err == nil {
if user , err = GetUserByName ( string ( b ) ) ; user != nil {
return user
}
2019-04-02 13:18:31 +05:30
log . Error ( "user.getVerifyUser: %v" , err )
2014-04-10 23:50:58 +05:30
}
return nil
}
2016-11-28 22:17:46 +05:30
// VerifyUserActiveCode verifies active code when active account
2014-04-10 23:50:58 +05:30
func VerifyUserActiveCode ( code string ) ( user * User ) {
2014-05-26 05:41:25 +05:30
minutes := setting . Service . ActiveCodeLives
2014-04-10 23:50:58 +05:30
if user = getVerifyUser ( code ) ; user != nil {
// time limit code
prefix := code [ : base . TimeLimitCodeLength ]
2020-12-25 15:29:32 +05:30
data := fmt . Sprintf ( "%d%s%s%s%s" , user . ID , user . Email , user . LowerName , user . Passwd , user . Rands )
2014-04-10 23:50:58 +05:30
if base . VerifyTimeLimitCode ( data , minutes , prefix ) {
return user
}
}
return nil
}
// ChangeUserName changes all corresponding setting from old user name to new one.
2014-07-26 09:54:27 +05:30
func ChangeUserName ( u * User , newUserName string ) ( err error ) {
2021-01-24 20:53:05 +05:30
oldUserName := u . Name
2016-07-23 16:28:18 +05:30
if err = IsUsableUsername ( newUserName ) ; err != nil {
2015-03-27 02:41:47 +05:30
return err
}
2021-11-11 12:33:30 +05:30
ctx , committer , err := db . TxContext ( )
if err != nil {
2020-02-08 20:29:40 +05:30
return err
}
2021-11-11 12:33:30 +05:30
defer committer . Close ( )
sess := db . GetEngine ( ctx )
2020-02-08 20:29:40 +05:30
2021-01-10 17:44:02 +05:30
isExist , err := isUserExist ( sess , 0 , newUserName )
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist { newUserName }
}
2021-01-24 20:53:05 +05:30
if _ , err = sess . Exec ( "UPDATE `repository` SET owner_name=? WHERE owner_name=?" , newUserName , oldUserName ) ; err != nil {
2020-02-08 20:29:40 +05:30
return fmt . Errorf ( "Change repo owner name: %v" , err )
}
2016-01-28 03:15:03 +05:30
2018-09-07 06:10:58 +05:30
// Do not fail if directory does not exist
2021-07-15 21:16:07 +05:30
if err = util . Rename ( UserPath ( oldUserName ) , UserPath ( newUserName ) ) ; err != nil && ! os . IsNotExist ( err ) {
2018-09-07 06:10:58 +05:30
return fmt . Errorf ( "Rename user directory: %v" , err )
}
2021-11-11 12:33:30 +05:30
if err = user_model . NewUserRedirect ( ctx , u . ID , oldUserName , newUserName ) ; err != nil {
2021-01-24 20:53:05 +05:30
return err
}
2021-11-11 12:33:30 +05:30
if err = committer . Commit ( ) ; err != nil {
2021-07-15 21:16:07 +05:30
if err2 := util . Rename ( UserPath ( newUserName ) , UserPath ( oldUserName ) ) ; err2 != nil && ! os . IsNotExist ( err2 ) {
2021-01-24 20:53:05 +05:30
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 )
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 )
}
return err
}
return nil
2014-04-10 23:50:58 +05:30
}
2017-02-25 20:23:57 +05:30
// checkDupEmail checks whether there are the same email with the user
2021-09-19 17:19:59 +05:30
func checkDupEmail ( e db . Engine , u * User ) error {
2017-02-25 20:23:57 +05:30
u . Email = strings . ToLower ( u . Email )
has , err := e .
Where ( "id!=?" , u . ID ) .
And ( "type=?" , u . Type ) .
And ( "email=?" , u . Email ) .
Get ( new ( User ) )
if err != nil {
return err
} else if has {
2021-11-11 12:33:30 +05:30
return user_model . ErrEmailAlreadyUsed {
Email : u . Email ,
}
2017-02-25 20:23:57 +05:30
}
return nil
}
2021-09-08 21:17:49 +05:30
// validateUser check if user is valid to insert / update into database
2021-06-28 00:17:35 +05:30
func validateUser ( u * User ) error {
2021-09-08 21:17:49 +05:30
if ! setting . Service . AllowedUserVisibilityModesSlice . IsAllowedVisibility ( u . Visibility ) && ! u . IsOrganization ( ) {
2021-06-28 00:17:35 +05:30
return fmt . Errorf ( "visibility Mode not allowed: %s" , u . Visibility . String ( ) )
}
2020-11-14 22:23:43 +05:30
u . Email = strings . ToLower ( u . Email )
2021-11-11 12:33:30 +05:30
return user_model . ValidateEmail ( u . Email )
2021-06-28 00:17:35 +05:30
}
2021-09-19 17:19:59 +05:30
func updateUser ( e db . Engine , u * User ) error {
2021-06-28 00:17:35 +05:30
if err := validateUser ( u ) ; err != nil {
2020-11-21 03:15:55 +05:30
return err
2020-11-14 22:23:43 +05:30
}
2021-06-28 00:17:35 +05:30
_ , err := e . ID ( u . ID ) . AllCols ( ) . Update ( u )
2014-04-10 23:50:58 +05:30
return err
}
2015-08-29 22:43:24 +05:30
// UpdateUser updates user's information.
func UpdateUser ( u * User ) error {
2021-09-23 21:15:36 +05:30
return updateUser ( db . GetEngine ( db . DefaultContext ) , u )
2017-02-25 20:23:57 +05:30
}
2017-08-12 19:48:44 +05:30
// UpdateUserCols update user according special columns
2021-11-22 20:51:55 +05:30
func UpdateUserCols ( ctx context . Context , u * User , cols ... string ) error {
return updateUserCols ( db . GetEngine ( ctx ) , u , cols ... )
2017-09-25 10:29:27 +05:30
}
2021-09-19 17:19:59 +05:30
func updateUserCols ( e db . Engine , u * User , cols ... string ) error {
2021-06-28 00:17:35 +05:30
if err := validateUser ( u ) ; err != nil {
return err
}
2017-10-05 10:13:04 +05:30
_ , err := e . ID ( u . ID ) . Cols ( cols ... ) . Update ( u )
2017-08-12 19:48:44 +05:30
return err
}
2017-02-25 20:23:57 +05:30
// UpdateUserSetting updates user's settings.
2020-11-21 03:15:55 +05:30
func UpdateUserSetting ( u * User ) ( err error ) {
2021-11-21 21:11:00 +05:30
ctx , committer , err := db . TxContext ( )
if err != nil {
2020-11-21 03:15:55 +05:30
return err
}
2021-11-21 21:11:00 +05:30
defer committer . Close ( )
sess := db . GetEngine ( ctx )
2017-02-25 20:23:57 +05:30
if ! u . IsOrganization ( ) {
2020-11-21 03:15:55 +05:30
if err = checkDupEmail ( sess , u ) ; err != nil {
2017-02-25 20:23:57 +05:30
return err
}
}
2020-11-21 03:15:55 +05:30
if err = updateUser ( sess , u ) ; err != nil {
return err
}
2021-11-21 21:11:00 +05:30
return committer . Commit ( )
2015-08-29 22:43:24 +05:30
}
2015-09-06 18:24:08 +05:30
// deleteBeans deletes all given beans, beans should contain delete conditions.
2021-09-19 17:19:59 +05:30
func deleteBeans ( e db . Engine , beans ... interface { } ) ( err error ) {
2015-03-18 07:21:39 +05:30
for i := range beans {
if _ , err = e . Delete ( beans [ i ] ) ; err != nil {
return err
}
}
return nil
}
2021-11-18 23:12:27 +05:30
// DeleteUser deletes models associated to an user.
func DeleteUser ( ctx context . Context , u * User ) ( err error ) {
2021-11-18 11:28:42 +05:30
e := db . GetEngine ( ctx )
2014-06-27 13:07:01 +05:30
2015-08-17 14:35:37 +05:30
// ***** START: Watch *****
2017-05-20 14:18:22 +05:30
watchedRepoIDs := make ( [ ] int64 , 0 , 10 )
if err = e . Table ( "watch" ) . Cols ( "watch.repo_id" ) .
2019-11-10 14:52:19 +05:30
Where ( "watch.user_id = ?" , u . ID ) . And ( "watch.mode <>?" , RepoWatchModeDont ) . Find ( & watchedRepoIDs ) ; err != nil {
2015-03-18 07:21:39 +05:30
return fmt . Errorf ( "get all watches: %v" , err )
2014-04-10 23:50:58 +05:30
}
2018-07-05 03:17:05 +05:30
if _ , err = e . Decr ( "num_watches" ) . In ( "id" , watchedRepoIDs ) . NoAutoTime ( ) . Update ( new ( Repository ) ) ; err != nil {
2017-05-20 14:18:22 +05:30
return fmt . Errorf ( "decrease repository num_watches: %v" , err )
2014-04-12 07:17:39 +05:30
}
2015-08-17 14:35:37 +05:30
// ***** END: Watch *****
2015-03-18 07:21:39 +05:30
2015-08-17 14:35:37 +05:30
// ***** START: Star *****
2017-05-20 14:18:22 +05:30
starredRepoIDs := make ( [ ] int64 , 0 , 10 )
if err = e . Table ( "star" ) . Cols ( "star.repo_id" ) .
Where ( "star.uid = ?" , u . ID ) . Find ( & starredRepoIDs ) ; err != nil {
2015-08-17 14:35:37 +05:30
return fmt . Errorf ( "get all stars: %v" , err )
2018-07-05 03:17:05 +05:30
} else if _ , err = e . Decr ( "num_stars" ) . In ( "id" , starredRepoIDs ) . NoAutoTime ( ) . Update ( new ( Repository ) ) ; err != nil {
2017-05-20 14:18:22 +05:30
return fmt . Errorf ( "decrease repository num_stars: %v" , err )
2015-08-17 14:35:37 +05:30
}
// ***** END: Star *****
2015-03-18 07:21:39 +05:30
2015-08-17 14:35:37 +05:30
// ***** START: Follow *****
2017-05-20 14:18:22 +05:30
followeeIDs := make ( [ ] int64 , 0 , 10 )
if err = e . Table ( "follow" ) . Cols ( "follow.follow_id" ) .
Where ( "follow.user_id = ?" , u . ID ) . Find ( & followeeIDs ) ; err != nil {
return fmt . Errorf ( "get all followees: %v" , err )
} else if _ , err = e . Decr ( "num_followers" ) . In ( "id" , followeeIDs ) . Update ( new ( User ) ) ; err != nil {
return fmt . Errorf ( "decrease user num_followers: %v" , err )
2015-08-17 14:35:37 +05:30
}
2017-05-20 14:18:22 +05:30
followerIDs := make ( [ ] int64 , 0 , 10 )
if err = e . Table ( "follow" ) . Cols ( "follow.user_id" ) .
Where ( "follow.follow_id = ?" , u . ID ) . Find ( & followerIDs ) ; err != nil {
return fmt . Errorf ( "get all followers: %v" , err )
} else if _ , err = e . Decr ( "num_following" ) . In ( "id" , followerIDs ) . Update ( new ( User ) ) ; err != nil {
return fmt . Errorf ( "decrease user num_following: %v" , err )
2014-04-10 23:50:58 +05:30
}
2015-08-17 14:35:37 +05:30
// ***** END: Follow *****
2015-03-18 07:21:39 +05:30
2015-09-06 18:24:08 +05:30
if err = deleteBeans ( e ,
2016-07-23 22:38:22 +05:30
& AccessToken { UID : u . ID } ,
& Collaboration { UserID : u . ID } ,
& Access { UserID : u . ID } ,
& Watch { UserID : u . ID } ,
& Star { UID : u . ID } ,
2021-11-17 15:28:31 +05:30
& user_model . Follow { UserID : u . ID } ,
& user_model . Follow { FollowID : u . ID } ,
2016-07-23 22:38:22 +05:30
& Action { UserID : u . ID } ,
& IssueUser { UID : u . ID } ,
2021-11-11 12:33:30 +05:30
& user_model . EmailAddress { UID : u . ID } ,
2021-11-17 15:28:31 +05:30
& user_model . UserOpenID { UID : u . ID } ,
2017-12-04 04:44:26 +05:30
& Reaction { UserID : u . ID } ,
2018-12-18 21:56:26 +05:30
& TeamUser { UID : u . ID } ,
& Collaboration { UserID : u . ID } ,
& Stopwatch { UserID : u . ID } ,
2021-11-22 15:17:23 +05:30
& user_model . Setting { UserID : u . ID } ,
2015-03-18 07:21:39 +05:30
) ; err != nil {
2015-12-01 07:15:55 +05:30
return fmt . Errorf ( "deleteBeans: %v" , err )
2014-04-10 23:50:58 +05:30
}
2015-03-18 07:21:39 +05:30
2021-01-22 08:26:19 +05:30
if setting . Service . UserDeleteWithCommentsMaxTime != 0 &&
u . CreatedUnix . AsTime ( ) . Add ( setting . Service . UserDeleteWithCommentsMaxTime ) . After ( time . Now ( ) ) {
// Delete Comments
const batchSize = 50
for start := 0 ; ; start += batchSize {
comments := make ( [ ] * Comment , 0 , batchSize )
if err = e . Where ( "type=? AND poster_id=?" , CommentTypeComment , u . ID ) . Limit ( batchSize , start ) . Find ( & comments ) ; err != nil {
return err
}
if len ( comments ) == 0 {
break
}
for _ , comment := range comments {
if err = deleteComment ( e , comment ) ; err != nil {
return err
}
}
}
// Delete Reactions
if err = deleteReaction ( e , & ReactionOptions { Doer : u } ) ; err != nil {
return err
2021-01-18 02:18:38 +05:30
}
}
2015-08-17 14:35:37 +05:30
// ***** START: PublicKey *****
2018-12-18 21:56:26 +05:30
if _ , err = e . Delete ( & PublicKey { OwnerID : u . ID } ) ; err != nil {
2016-07-26 14:56:48 +05:30
return fmt . Errorf ( "deletePublicKeys: %v" , err )
2014-04-10 23:50:58 +05:30
}
2015-08-17 14:35:37 +05:30
// ***** END: PublicKey *****
2014-04-10 23:50:58 +05:30
2018-12-18 21:56:26 +05:30
// ***** START: GPGPublicKey *****
2021-09-24 17:02:56 +05:30
keys , err := listGPGKeys ( e , u . ID , db . ListOptions { } )
2021-02-04 14:46:21 +05:30
if err != nil {
return fmt . Errorf ( "ListGPGKeys: %v" , err )
}
// Delete GPGKeyImport(s).
for _ , key := range keys {
if _ , err = e . Delete ( & GPGKeyImport { KeyID : key . KeyID } ) ; err != nil {
return fmt . Errorf ( "deleteGPGKeyImports: %v" , err )
}
}
2018-12-18 21:56:26 +05:30
if _ , err = e . Delete ( & GPGKey { OwnerID : u . ID } ) ; err != nil {
return fmt . Errorf ( "deleteGPGKeys: %v" , err )
}
// ***** END: GPGPublicKey *****
2015-08-15 00:18:05 +05:30
// Clear assignee.
2018-05-09 21:59:04 +05:30
if err = clearAssigneeByUserID ( e , u . ID ) ; err != nil {
2015-08-17 14:35:37 +05:30
return fmt . Errorf ( "clear assignee: %v" , err )
2015-08-15 00:18:05 +05:30
}
2017-02-22 12:44:37 +05:30
// ***** START: ExternalLoginUser *****
2017-03-20 19:43:52 +05:30
if err = removeAllAccountLinks ( e , u ) ; err != nil {
2017-02-22 12:44:37 +05:30
return fmt . Errorf ( "ExternalLoginUser: %v" , err )
}
// ***** END: ExternalLoginUser *****
2017-10-05 10:13:04 +05:30
if _ , err = e . ID ( u . ID ) . Delete ( new ( User ) ) ; err != nil {
2015-08-17 14:35:37 +05:30
return fmt . Errorf ( "Delete: %v" , err )
2015-03-18 07:21:39 +05:30
}
2015-08-17 14:35:37 +05:30
return nil
2014-06-21 10:21:41 +05:30
}
2021-11-18 23:12:27 +05:30
// GetInactiveUsers gets all inactive users
func GetInactiveUsers ( ctx context . Context , olderThan time . Duration ) ( [ ] * User , error ) {
var cond builder . Cond = builder . Eq { "is_active" : false }
2015-09-06 18:24:08 +05:30
2020-05-17 05:01:38 +05:30
if olderThan > 0 {
2021-11-18 23:12:27 +05:30
cond = cond . And ( builder . Lt { "created_unix" : time . Now ( ) . Add ( - olderThan ) . Unix ( ) } )
2014-12-17 13:56:19 +05:30
}
2015-08-17 14:35:37 +05:30
2021-11-18 23:12:27 +05:30
users := make ( [ ] * User , 0 , 10 )
return users , db . GetEngine ( ctx ) .
Where ( cond ) .
Find ( & users )
2014-04-10 23:50:58 +05:30
}
// UserPath returns the path absolute path of user repositories.
func UserPath ( userName string ) string {
2014-05-26 05:41:25 +05:30
return filepath . Join ( setting . RepoRootPath , strings . ToLower ( userName ) )
2014-04-10 23:50:58 +05:30
}
2021-09-19 17:19:59 +05:30
func getUserByID ( e db . Engine , id int64 ) ( * User , error ) {
2014-06-06 07:37:35 +05:30
u := new ( User )
2017-10-05 10:13:04 +05:30
has , err := e . ID ( id ) . Get ( u )
2014-04-10 23:50:58 +05:30
if err != nil {
return nil , err
2014-06-06 07:37:35 +05:30
} else if ! has {
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { id , "" , 0 }
2014-04-10 23:50:58 +05:30
}
2014-06-06 07:37:35 +05:30
return u , nil
2014-04-10 23:50:58 +05:30
}
2015-08-08 20:13:14 +05:30
// GetUserByID returns the user object by given ID if exists.
func GetUserByID ( id int64 ) ( * User , error ) {
2021-11-10 08:51:01 +05:30
return GetUserByIDCtx ( db . DefaultContext , id )
}
// GetUserByIDCtx returns the user object by given ID if exists.
func GetUserByIDCtx ( ctx context . Context , id int64 ) ( * User , error ) {
return getUserByID ( db . GetEngine ( ctx ) , id )
2015-02-13 11:28:46 +05:30
}
2014-10-25 04:13:17 +05:30
// GetUserByName returns user by given name.
2014-04-10 23:50:58 +05:30
func GetUserByName ( name string ) ( * User , error ) {
2021-09-23 21:15:36 +05:30
return getUserByName ( db . GetEngine ( db . DefaultContext ) , name )
2017-08-30 10:01:33 +05:30
}
2021-09-19 17:19:59 +05:30
func getUserByName ( e db . Engine , name string ) ( * User , error ) {
2014-04-10 23:50:58 +05:30
if len ( name ) == 0 {
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { 0 , name , 0 }
2014-04-10 23:50:58 +05:30
}
2014-10-25 04:13:17 +05:30
u := & User { LowerName : strings . ToLower ( name ) }
2017-08-30 10:01:33 +05:30
has , err := e . Get ( u )
2014-04-10 23:50:58 +05:30
if err != nil {
return nil , err
} else if ! has {
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { 0 , name , 0 }
2014-04-10 23:50:58 +05:30
}
2014-10-25 04:13:17 +05:30
return u , nil
2014-04-10 23:50:58 +05:30
}
2019-08-29 19:35:42 +05:30
// GetUserEmailsByNames returns a list of e-mails corresponds to names of users
// that have their email notifications set to enabled or onmention.
2014-04-10 23:50:58 +05:30
func GetUserEmailsByNames ( names [ ] string ) [ ] string {
2021-09-23 21:15:36 +05:30
return getUserEmailsByNames ( db . GetEngine ( db . DefaultContext ) , names )
2017-08-30 10:01:33 +05:30
}
2021-09-19 17:19:59 +05:30
func getUserEmailsByNames ( e db . Engine , names [ ] string ) [ ] string {
2014-04-10 23:50:58 +05:30
mails := make ( [ ] string , 0 , len ( names ) )
for _ , name := range names {
2017-08-30 10:01:33 +05:30
u , err := getUserByName ( e , name )
2014-04-10 23:50:58 +05:30
if err != nil {
continue
}
2019-08-29 19:35:42 +05:30
if u . IsMailable ( ) && u . EmailNotifications ( ) != EmailNotificationsDisabled {
2017-02-02 18:03:36 +05:30
mails = append ( mails , u . Email )
}
2014-04-10 23:50:58 +05:30
}
return mails
}
2019-11-18 13:38:20 +05:30
// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails
2020-09-10 00:38:55 +05:30
func GetMaileableUsersByIDs ( ids [ ] int64 , isMention bool ) ( [ ] * User , error ) {
2019-11-18 13:38:20 +05:30
if len ( ids ) == 0 {
return nil , nil
}
ous := make ( [ ] * User , 0 , len ( ids ) )
2020-09-10 00:38:55 +05:30
if isMention {
2021-09-23 21:15:36 +05:30
return ous , db . GetEngine ( db . DefaultContext ) . In ( "id" , ids ) .
2020-09-10 00:38:55 +05:30
Where ( "`type` = ?" , UserTypeIndividual ) .
And ( "`prohibit_login` = ?" , false ) .
And ( "`is_active` = ?" , true ) .
And ( "`email_notifications_preference` IN ( ?, ?)" , EmailNotificationsEnabled , EmailNotificationsOnMention ) .
Find ( & ous )
}
2021-09-23 21:15:36 +05:30
return ous , db . GetEngine ( db . DefaultContext ) . In ( "id" , ids ) .
2019-11-18 13:38:20 +05:30
Where ( "`type` = ?" , UserTypeIndividual ) .
And ( "`prohibit_login` = ?" , false ) .
And ( "`is_active` = ?" , true ) .
And ( "`email_notifications_preference` = ?" , EmailNotificationsEnabled ) .
Find ( & ous )
}
2020-02-13 04:49:35 +05:30
// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids.
func GetUserNamesByIDs ( ids [ ] int64 ) ( [ ] string , error ) {
unames := make ( [ ] string , 0 , len ( ids ) )
2021-09-23 21:15:36 +05:30
err := db . GetEngine ( db . DefaultContext ) . In ( "id" , ids ) .
2020-02-13 04:49:35 +05:30
Table ( "user" ) .
Asc ( "name" ) .
Cols ( "name" ) .
Find ( & unames )
return unames , err
}
2016-11-10 13:40:35 +05:30
// GetUsersByIDs returns all resolved users from a list of Ids.
2020-02-26 12:02:22 +05:30
func GetUsersByIDs ( ids [ ] int64 ) ( UserList , error ) {
2016-11-10 13:40:35 +05:30
ous := make ( [ ] * User , 0 , len ( ids ) )
2017-01-20 08:01:46 +05:30
if len ( ids ) == 0 {
return ous , nil
}
2021-09-23 21:15:36 +05:30
err := db . GetEngine ( db . DefaultContext ) . In ( "id" , ids ) .
2016-11-10 20:46:32 +05:30
Asc ( "name" ) .
Find ( & ous )
2016-11-10 13:40:35 +05:30
return ous , err
}
2016-07-16 07:38:04 +05:30
// GetUserIDsByNames returns a slice of ids corresponds to names.
2019-10-25 20:16:37 +05:30
func GetUserIDsByNames ( names [ ] string , ignoreNonExistent bool ) ( [ ] int64 , error ) {
2014-05-08 02:21:14 +05:30
ids := make ( [ ] int64 , 0 , len ( names ) )
for _ , name := range names {
u , err := GetUserByName ( name )
if err != nil {
2019-10-25 20:16:37 +05:30
if ignoreNonExistent {
continue
} else {
return nil , err
}
2014-05-08 02:21:14 +05:30
}
2016-07-23 22:38:22 +05:30
ids = append ( ids , u . ID )
2014-05-08 02:21:14 +05:30
}
2019-10-25 20:16:37 +05:30
return ids , nil
2014-05-08 02:21:14 +05:30
}
2021-07-24 15:46:34 +05:30
// GetUsersBySource returns a list of Users for a login source
2021-09-24 17:02:56 +05:30
func GetUsersBySource ( s * login . Source ) ( [ ] * User , error ) {
2021-07-24 15:46:34 +05:30
var users [ ] * User
2021-09-23 21:15:36 +05:30
err := db . GetEngine ( db . DefaultContext ) . Where ( "login_type = ? AND login_source = ?" , s . Type , s . ID ) . Find ( & users )
2021-07-24 15:46:34 +05:30
return users , err
}
2014-12-07 06:52:48 +05:30
// UserCommit represents a commit with validation of user.
2014-09-24 01:00:04 +05:30
type UserCommit struct {
2014-11-21 21:28:08 +05:30
User * User
2015-12-10 07:16:05 +05:30
* git . Commit
2014-09-24 01:00:04 +05:30
}
2017-01-05 06:20:34 +05:30
// ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user.
2015-12-10 07:16:05 +05:30
func ValidateCommitWithEmail ( c * git . Commit ) * User {
2017-10-26 13:15:14 +05:30
if c . Author == nil {
return nil
}
2014-09-26 18:25:13 +05:30
u , err := GetUserByEmail ( c . Author . Email )
2014-11-21 21:28:08 +05:30
if err != nil {
return nil
2014-09-26 18:25:13 +05:30
}
2014-11-21 21:28:08 +05:30
return u
2014-09-26 18:25:13 +05:30
}
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
2021-08-09 23:38:51 +05:30
func ValidateCommitsWithEmails ( oldCommits [ ] * git . Commit ) [ ] * UserCommit {
2015-08-05 15:06:22 +05:30
var (
2021-08-09 23:38:51 +05:30
emails = make ( map [ string ] * User )
newCommits = make ( [ ] * UserCommit , 0 , len ( oldCommits ) )
2015-08-05 15:06:22 +05:30
)
2021-08-09 23:38:51 +05:30
for _ , c := range oldCommits {
var u * User
2017-10-26 13:15:14 +05:30
if c . Author != nil {
if v , ok := emails [ c . Author . Email ] ; ! ok {
u , _ = GetUserByEmail ( c . Author . Email )
emails [ c . Author . Email ] = u
} else {
u = v
}
2014-09-24 01:00:04 +05:30
}
2021-08-09 23:38:51 +05:30
newCommits = append ( newCommits , & UserCommit {
2014-11-21 21:28:08 +05:30
User : u ,
Commit : c ,
2014-09-24 01:00:04 +05:30
} )
}
return newCommits
}
2014-04-10 23:50:58 +05:30
// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail ( email string ) ( * User , error ) {
2021-09-23 21:15:36 +05:30
return GetUserByEmailContext ( db . DefaultContext , email )
2020-02-03 14:17:04 +05:30
}
// GetUserByEmailContext returns the user object by given e-mail if exists with db context
2021-09-23 21:15:36 +05:30
func GetUserByEmailContext ( ctx context . Context , email string ) ( * User , error ) {
2014-04-10 23:50:58 +05:30
if len ( email ) == 0 {
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { 0 , email , 0 }
2014-04-10 23:50:58 +05:30
}
2015-08-05 15:06:22 +05:30
email = strings . ToLower ( email )
2014-12-17 13:56:19 +05:30
// First try to find the user by primary email
2015-08-05 15:06:22 +05:30
user := & User { Email : email }
2021-09-23 21:15:36 +05:30
has , err := db . GetEngine ( ctx ) . Get ( user )
2014-04-10 23:50:58 +05:30
if err != nil {
return nil , err
}
2014-12-17 13:56:19 +05:30
if has {
return user , nil
}
// Otherwise, check in alternative list for activated email addresses
2021-11-11 12:33:30 +05:30
emailAddress := & user_model . EmailAddress { Email : email , IsActivated : true }
2021-09-23 21:15:36 +05:30
has , err = db . GetEngine ( ctx ) . Get ( emailAddress )
2014-12-17 13:56:19 +05:30
if err != nil {
return nil , err
}
if has {
2021-11-10 08:51:01 +05:30
return GetUserByIDCtx ( ctx , emailAddress . UID )
2014-12-17 13:56:19 +05:30
}
2019-06-06 11:24:25 +05:30
// Finally, if email address is the protected email address:
if strings . HasSuffix ( email , fmt . Sprintf ( "@%s" , setting . Service . NoReplyAddress ) ) {
username := strings . TrimSuffix ( email , fmt . Sprintf ( "@%s" , setting . Service . NoReplyAddress ) )
2020-06-17 23:20:11 +05:30
user := & User { }
2021-09-23 21:15:36 +05:30
has , err := db . GetEngine ( ctx ) . Where ( "lower_name=?" , username ) . Get ( user )
2019-06-06 11:24:25 +05:30
if err != nil {
return nil , err
}
if has {
return user , nil
}
}
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { 0 , email , 0 }
2014-04-10 23:50:58 +05:30
}
2017-02-22 12:44:37 +05:30
// GetUser checks if a user already exists
func GetUser ( user * User ) ( bool , error ) {
2021-09-23 21:15:36 +05:30
return db . GetEngine ( db . DefaultContext ) . Get ( user )
2017-02-22 12:44:37 +05:30
}
2016-11-28 22:17:46 +05:30
// SearchUserOptions contains the options for searching
2016-03-12 02:03:12 +05:30
type SearchUserOptions struct {
2021-09-24 17:02:56 +05:30
db . ListOptions
2017-11-26 14:10:38 +05:30
Keyword string
Type UserType
2018-10-18 14:14:51 +05:30
UID int64
2018-05-24 06:33:42 +05:30
OrderBy SearchOrderBy
2020-01-12 21:13:44 +05:30
Visible [ ] structs . VisibleType
2020-01-13 23:03:46 +05:30
Actor * User // The user doing the search
2021-10-12 23:41:35 +05:30
SearchByEmail bool // Search by email as well as username/full name
IsActive util . OptionalBool
IsAdmin util . OptionalBool
IsRestricted util . OptionalBool
IsTwoFactorEnabled util . OptionalBool
IsProhibitLogin util . OptionalBool
2016-03-12 02:03:12 +05:30
}
2021-11-21 21:11:00 +05:30
func ( opts * SearchUserOptions ) toSearchQueryBase ( ) * xorm . Session {
2019-08-05 00:03:36 +05:30
var cond builder . Cond = builder . Eq { "type" : opts . Type }
2017-10-24 23:06:19 +05:30
if len ( opts . Keyword ) > 0 {
lowerKeyword := strings . ToLower ( opts . Keyword )
2017-11-26 14:10:38 +05:30
keywordCond := builder . Or (
2017-10-24 23:06:19 +05:30
builder . Like { "lower_name" , lowerKeyword } ,
builder . Like { "LOWER(full_name)" , lowerKeyword } ,
2017-11-26 14:10:38 +05:30
)
if opts . SearchByEmail {
keywordCond = keywordCond . Or ( builder . Like { "LOWER(email)" , lowerKeyword } )
}
2018-10-18 14:14:51 +05:30
2017-11-26 14:10:38 +05:30
cond = cond . And ( keywordCond )
2016-03-12 02:03:12 +05:30
}
2021-06-27 01:23:14 +05:30
// If visibility filtered
2020-01-12 21:13:44 +05:30
if len ( opts . Visible ) > 0 {
cond = cond . And ( builder . In ( "visibility" , opts . Visible ) )
2019-02-18 21:30:27 +05:30
}
2020-01-13 23:03:46 +05:30
if opts . Actor != nil {
2021-08-25 17:53:34 +05:30
var exprCond builder . Cond = builder . Expr ( "org_user.org_id = `user`.id" )
2021-03-15 00:22:12 +05:30
2021-06-27 01:23:14 +05:30
// If Admin - they see all users!
if ! opts . Actor . IsAdmin {
2021-07-08 17:08:13 +05:30
// Force visibility for privacy
2021-06-27 01:23:14 +05:30
var accessCond builder . Cond
if ! opts . Actor . IsRestricted {
accessCond = builder . Or (
builder . In ( "id" , builder . Select ( "org_id" ) . From ( "org_user" ) . LeftJoin ( "`user`" , exprCond ) . Where ( builder . And ( builder . Eq { "uid" : opts . Actor . ID } , builder . Eq { "visibility" : structs . VisibleTypePrivate } ) ) ) ,
builder . In ( "visibility" , structs . VisibleTypePublic , structs . VisibleTypeLimited ) )
} else {
// restricted users only see orgs they are a member of
accessCond = builder . In ( "id" , builder . Select ( "org_id" ) . From ( "org_user" ) . LeftJoin ( "`user`" , exprCond ) . Where ( builder . And ( builder . Eq { "uid" : opts . Actor . ID } ) ) )
}
// Don't forget about self
accessCond = accessCond . Or ( builder . Eq { "id" : opts . Actor . ID } )
cond = cond . And ( accessCond )
2020-01-13 23:03:46 +05:30
}
2021-06-27 01:23:14 +05:30
} else {
2021-07-08 17:08:13 +05:30
// Force visibility for privacy
2021-06-27 01:23:14 +05:30
// Not logged in - only public users
cond = cond . And ( builder . In ( "visibility" , structs . VisibleTypePublic ) )
2019-02-18 21:30:27 +05:30
}
2018-10-18 14:14:51 +05:30
if opts . UID > 0 {
cond = cond . And ( builder . Eq { "id" : opts . UID } )
}
2017-10-24 23:06:19 +05:30
if ! opts . IsActive . IsNone ( ) {
cond = cond . And ( builder . Eq { "is_active" : opts . IsActive . IsTrue ( ) } )
2016-03-12 02:03:12 +05:30
}
2021-10-12 23:41:35 +05:30
if ! opts . IsAdmin . IsNone ( ) {
cond = cond . And ( builder . Eq { "is_admin" : opts . IsAdmin . IsTrue ( ) } )
}
if ! opts . IsRestricted . IsNone ( ) {
cond = cond . And ( builder . Eq { "is_restricted" : opts . IsRestricted . IsTrue ( ) } )
}
if ! opts . IsProhibitLogin . IsNone ( ) {
cond = cond . And ( builder . Eq { "prohibit_login" : opts . IsProhibitLogin . IsTrue ( ) } )
}
2021-11-21 21:11:00 +05:30
e := db . GetEngine ( db . DefaultContext )
if opts . IsTwoFactorEnabled . IsNone ( ) {
return e . Where ( cond )
2021-10-12 23:41:35 +05:30
}
2021-11-21 21:11:00 +05:30
// 2fa filter uses LEFT JOIN to check whether a user has a 2fa record
// TODO: bad performance here, maybe there will be a column "is_2fa_enabled" in the future
if opts . IsTwoFactorEnabled . IsTrue ( ) {
cond = cond . And ( builder . Expr ( "two_factor.uid IS NOT NULL" ) )
} else {
cond = cond . And ( builder . Expr ( "two_factor.uid IS NULL" ) )
}
return e . Join ( "LEFT OUTER" , "two_factor" , "two_factor.uid = `user`.id" ) .
Where ( cond )
2017-10-24 23:06:19 +05:30
}
2016-03-12 02:03:12 +05:30
2017-10-24 23:06:19 +05:30
// SearchUsers takes options i.e. keyword and part of user name to search,
// it returns results in given range and number of total results.
func SearchUsers ( opts * SearchUserOptions ) ( users [ ] * User , _ int64 , _ error ) {
2021-10-12 23:41:35 +05:30
sessCount := opts . toSearchQueryBase ( )
defer sessCount . Close ( )
count , err := sessCount . Count ( new ( User ) )
2016-03-12 02:03:12 +05:30
if err != nil {
return nil , 0 , fmt . Errorf ( "Count: %v" , err )
2014-05-01 09:18:01 +05:30
}
2017-10-24 23:06:19 +05:30
if len ( opts . OrderBy ) == 0 {
2018-10-20 07:55:38 +05:30
opts . OrderBy = SearchOrderByAlphabetically
2017-10-24 23:06:19 +05:30
}
2021-10-12 23:41:35 +05:30
sessQuery := opts . toSearchQueryBase ( ) . OrderBy ( opts . OrderBy . String ( ) )
defer sessQuery . Close ( )
2020-01-25 00:30:29 +05:30
if opts . Page != 0 {
2021-10-12 23:41:35 +05:30
sessQuery = db . SetSessionPagination ( sessQuery , opts )
2019-02-22 02:12:54 +05:30
}
2019-01-24 04:00:19 +05:30
2021-10-12 23:41:35 +05:30
// the sql may contain JOIN, so we must only select User related columns
sessQuery = sessQuery . Select ( "`user`.*" )
2017-10-24 23:06:19 +05:30
users = make ( [ ] * User , 0 , opts . PageSize )
2021-10-12 23:41:35 +05:30
return users , count , sessQuery . Find ( & users )
2014-05-01 09:18:01 +05:30
}
2016-11-15 04:03:58 +05:30
// GetStarredRepos returns the repos starred by a particular user
2021-09-24 17:02:56 +05:30
func GetStarredRepos ( userID int64 , private bool , listOptions db . ListOptions ) ( [ ] * Repository , error ) {
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) . Where ( "star.uid=?" , userID ) .
2016-11-15 04:03:58 +05:30
Join ( "LEFT" , "star" , "`repository`.id=`star`.repo_id" )
if ! private {
sess = sess . And ( "is_private=?" , false )
}
2020-01-25 00:30:29 +05:30
if listOptions . Page != 0 {
2021-09-24 17:02:56 +05:30
sess = db . SetSessionPagination ( sess , & listOptions )
2020-01-25 00:30:29 +05:30
repos := make ( [ ] * Repository , 0 , listOptions . PageSize )
return repos , sess . Find ( & repos )
2016-11-15 04:03:58 +05:30
}
2020-01-25 00:30:29 +05:30
repos := make ( [ ] * Repository , 0 , 10 )
return repos , sess . Find ( & repos )
2016-11-15 04:03:58 +05:30
}
2016-12-24 07:23:11 +05:30
// GetWatchedRepos returns the repos watched by a particular user
2021-09-24 17:02:56 +05:30
func GetWatchedRepos ( userID int64 , private bool , listOptions db . ListOptions ) ( [ ] * Repository , int64 , error ) {
2021-09-23 21:15:36 +05:30
sess := db . GetEngine ( db . DefaultContext ) . Where ( "watch.user_id=?" , userID ) .
2019-11-10 14:52:19 +05:30
And ( "`watch`.mode<>?" , RepoWatchModeDont ) .
2016-12-24 07:23:11 +05:30
Join ( "LEFT" , "watch" , "`repository`.id=`watch`.repo_id" )
if ! private {
sess = sess . And ( "is_private=?" , false )
}
2020-01-25 00:30:29 +05:30
if listOptions . Page != 0 {
2021-09-24 17:02:56 +05:30
sess = db . SetSessionPagination ( sess , & listOptions )
2020-01-25 00:30:29 +05:30
repos := make ( [ ] * Repository , 0 , listOptions . PageSize )
2021-08-12 18:13:08 +05:30
total , err := sess . FindAndCount ( & repos )
return repos , total , err
2016-12-24 07:23:11 +05:30
}
2020-01-25 00:30:29 +05:30
repos := make ( [ ] * Repository , 0 , 10 )
2021-08-12 18:13:08 +05:30
total , err := sess . FindAndCount ( & repos )
return repos , total , err
2016-12-24 07:23:11 +05:30
}
2017-05-10 18:40:18 +05:30
2020-10-14 18:37:51 +05:30
// IterateUser iterate users
func IterateUser ( f func ( user * User ) error ) error {
var start int
2021-03-15 00:22:12 +05:30
batchSize := setting . Database . IterateBufferSize
2020-10-14 18:37:51 +05:30
for {
2021-03-15 00:22:12 +05:30
users := make ( [ ] * User , 0 , batchSize )
2021-09-23 21:15:36 +05:30
if err := db . GetEngine ( db . DefaultContext ) . Limit ( batchSize , start ) . Find ( & users ) ; err != nil {
2020-10-14 18:37:51 +05:30
return err
}
if len ( users ) == 0 {
return nil
}
start += len ( users )
for _ , user := range users {
if err := f ( user ) ; err != nil {
return err
}
}
}
}
2021-11-17 15:28:31 +05:30
// GetUserByOpenID returns the user object by given OpenID if exists.
func GetUserByOpenID ( uri string ) ( * User , error ) {
if len ( uri ) == 0 {
return nil , ErrUserNotExist { 0 , uri , 0 }
}
uri , err := openid . Normalize ( uri )
if err != nil {
return nil , err
}
log . Trace ( "Normalized OpenID URI: " + uri )
// Otherwise, check in openid table
oid := & user_model . UserOpenID { }
has , err := db . GetEngine ( db . DefaultContext ) . Where ( "uri=?" , uri ) . Get ( oid )
if err != nil {
return nil , err
}
if has {
return GetUserByID ( oid . UID )
}
return nil , ErrUserNotExist { 0 , uri , 0 }
}
2021-11-18 11:28:42 +05:30
// GetAdminUser returns the first administrator
func GetAdminUser ( ) ( * User , error ) {
var admin User
has , err := db . GetEngine ( db . DefaultContext ) . Where ( "is_admin=?" , true ) . Get ( & admin )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrUserNotExist { }
}
return & admin , nil
}