2014-04-10 23:50:58 +05:30
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2014-11-21 21:28:08 +05:30
"bytes"
2014-09-24 01:00:04 +05:30
"container/list"
2017-02-25 20:28:57 +05:30
"crypto/md5"
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"
2014-11-21 21:28:08 +05:30
"image"
2016-11-28 22:17:46 +05:30
// Needed for jpeg support
2015-08-09 09:16:10 +05:30
_ "image/jpeg"
2015-11-14 03:13:43 +05:30
"image/png"
2014-04-10 23:50:58 +05:30
"os"
"path/filepath"
"strings"
"time"
2016-07-23 16:28:18 +05:30
"unicode/utf8"
2014-04-10 23:50:58 +05:30
2014-07-26 09:54:27 +05:30
"github.com/Unknwon/com"
2017-02-25 19:12:20 +05:30
"github.com/go-xorm/builder"
2015-09-01 21:49:52 +05:30
"github.com/go-xorm/xorm"
2014-11-21 21:28:08 +05:30
"github.com/nfnt/resize"
2016-12-15 06:54:27 +05:30
"golang.org/x/crypto/pbkdf2"
2014-04-10 23:50:58 +05:30
2016-11-10 21:54:48 +05:30
"code.gitea.io/git"
2016-11-11 15:09:44 +05:30
api "code.gitea.io/sdk/gitea"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/base"
2018-02-18 23:44:37 +05:30
"code.gitea.io/gitea/modules/generate"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2017-10-24 23:06:19 +05:30
"code.gitea.io/gitea/modules/util"
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
)
2017-05-10 18:40:18 +05:30
const syncExternalUsers = "sync_external_users"
2014-04-10 23:50:58 +05:30
var (
2016-11-28 22:17:46 +05:30
// ErrUserNotKeyOwner user does not own this key error
ErrUserNotKeyOwner = errors . New ( "User does not own this public key" )
// ErrEmailNotExist e-mail does not exist error
ErrEmailNotExist = errors . New ( "E-mail does not exist" )
// ErrEmailNotActivated e-mail address has not been activated error
ErrEmailNotActivated = errors . New ( "E-mail address has not been activated" )
// 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" )
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)
2017-01-08 08:42:03 +05:30
Email string ` xorm:"NOT NULL" `
KeepEmailPrivate bool
Passwd string ` xorm:"NOT NULL" `
LoginType LoginType
LoginSource int64 ` xorm:"NOT NULL DEFAULT 0" `
LoginName string
Type UserType
OwnedOrgs [ ] * User ` xorm:"-" `
Orgs [ ] * User ` xorm:"-" `
Repos [ ] * Repository ` xorm:"-" `
Location string
Website string
Rands string ` xorm:"VARCHAR(10)" `
Salt string ` xorm:"VARCHAR(10)" `
2018-05-05 05:58:30 +05:30
Language string ` xorm:"VARCHAR(5)" `
2016-03-10 06:23:30 +05:30
2017-12-11 10:07:04 +05:30
CreatedUnix util . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix util . TimeStamp ` xorm:"INDEX updated" `
LastLoginUnix util . 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
2015-12-21 17:54:11 +05:30
// Permissions
2017-01-06 20:44:33 +05:30
IsActive bool ` xorm:"INDEX" ` // Activate primary email
2016-12-31 08:03:30 +05:30
IsAdmin bool
AllowGitHook bool
AllowImportLocal bool // Allow migrate repository by local path
AllowCreateOrganization bool ` xorm:"DEFAULT true" `
2017-09-16 05:48:25 +05:30
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
2014-06-27 13:07:01 +05:30
Description string
NumTeams int
NumMembers int
2014-06-29 01:13:25 +05:30
Teams [ ] * Team ` xorm:"-" `
Members [ ] * User ` xorm:"-" `
2016-11-13 08:24:04 +05:30
// Preferences
DiffViewStyle string ` xorm:"NOT NULL DEFAULT ''" `
2014-04-10 23:50:58 +05:30
}
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
}
2018-02-04 04:07:05 +05:30
if len ( u . AvatarEmail ) > 0 && u . Avatar == "" {
2018-01-13 15:15:16 +05:30
u . Avatar = base . HashEmail ( u . AvatarEmail )
}
}
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
}
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 ( ) {
2017-12-11 10:07:04 +05:30
u . LastLoginUnix = util . TimeStampNow ( )
2016-11-09 16:23:45 +05:30
}
2016-11-28 22:17:46 +05:30
// UpdateDiffViewStyle updates the users diff view style
2016-11-13 08:24:04 +05:30
func ( u * User ) UpdateDiffViewStyle ( style string ) error {
u . DiffViewStyle = style
2017-08-12 19:48:44 +05:30
return UpdateUserCols ( u , "diff_view_style" )
2016-11-13 08:24:04 +05:30
}
2017-01-08 08:42:03 +05:30
// getEmail returns an noreply email, if the user has set to keep his
// email address private, otherwise the primary email address.
func ( u * User ) getEmail ( ) string {
if u . KeepEmailPrivate {
return fmt . Sprintf ( "%s@%s" , u . LowerName , setting . Service . NoReplyAddress )
}
return u . Email
}
2016-11-28 22:17:46 +05:30
// APIFormat converts a User to api.User
2016-08-14 16:02:24 +05:30
func ( u * User ) APIFormat ( ) * api . User {
return & api . User {
ID : u . ID ,
UserName : u . Name ,
FullName : u . FullName ,
2017-01-08 08:42:03 +05:30
Email : u . getEmail ( ) ,
2016-11-29 13:55:47 +05:30
AvatarURL : u . AvatarLink ( ) ,
2018-05-05 05:58:30 +05:30
Language : u . Language ,
2016-08-14 16:02:24 +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 {
2016-11-07 22:00:04 +05:30
return u . LoginType <= LoginPlain
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 {
return u . LoginType == LoginOAuth2
}
2015-11-16 20:44:12 +05:30
// HasForkedRepo checks if user has already forked a repository with given ID.
func ( u * User ) HasForkedRepo ( repoID int64 ) bool {
2016-07-23 22:38:22 +05:30
_ , has := HasForkedRepo ( u . ID , repoID )
2015-11-16 20:44:12 +05:30
return has
}
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
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 {
2017-01-23 06:49:50 +05:30
if ! setting . ImportLocalPaths {
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 ( ) {
2016-11-27 15:44:25 +05:30
return setting . AppSubURL + "/org/" + u . Name + "/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 {
2016-11-27 15:44:25 +05:30
return setting . AppSubURL + "/" + 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 {
return setting . AppURL + u . Name
}
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 (
2016-07-23 22:38:22 +05:30
com . ToStr ( 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
}
// GenerateActivateCode generates an activate code based on user information.
func ( u * User ) GenerateActivateCode ( ) string {
return u . GenerateEmailActivateCode ( u . Email )
}
2014-04-10 23:50:58 +05:30
2015-09-06 19:38:14 +05:30
// CustomAvatarPath returns user custom avatar file path.
func ( u * User ) CustomAvatarPath ( ) string {
2017-02-25 20:28:57 +05:30
return filepath . Join ( setting . AvatarUploadPath , u . Avatar )
2015-09-06 19:38:14 +05:30
}
// GenerateRandomAvatar generates a random avatar for user.
func ( u * User ) GenerateRandomAvatar ( ) error {
2017-03-08 20:35:15 +05:30
return u . generateRandomAvatar ( x )
}
func ( u * User ) generateRandomAvatar ( e Engine ) error {
2015-09-06 19:38:14 +05:30
seed := u . Email
if len ( seed ) == 0 {
seed = u . Name
}
img , err := avatar . RandomImage ( [ ] byte ( seed ) )
if err != nil {
return fmt . Errorf ( "RandomImage: %v" , err )
}
2017-03-06 13:45:40 +05:30
// NOTICE for random avatar, it still uses id as avatar name, but custom avatar use md5
// since random image is not a user's photo, there is no security for enumable
2018-02-08 21:13:08 +05:30
if u . Avatar == "" {
u . Avatar = fmt . Sprintf ( "%d" , u . ID )
}
2016-02-02 20:52:27 +05:30
if err = os . MkdirAll ( filepath . Dir ( u . CustomAvatarPath ( ) ) , os . ModePerm ) ; err != nil {
2015-09-06 19:38:14 +05:30
return fmt . Errorf ( "MkdirAll: %v" , err )
}
fw , err := os . Create ( u . CustomAvatarPath ( ) )
if err != nil {
return fmt . Errorf ( "Create: %v" , err )
}
defer fw . Close ( )
2017-10-05 10:13:04 +05:30
if _ , err := e . ID ( u . ID ) . Cols ( "avatar" ) . Update ( u ) ; err != nil {
2017-03-08 20:35:15 +05:30
return err
}
2016-07-21 13:01:14 +05:30
if err = png . Encode ( fw , img ) ; err != nil {
2015-09-06 19:38:14 +05:30
return fmt . Errorf ( "Encode: %v" , err )
}
2016-07-23 22:38:22 +05:30
log . Info ( "New random avatar created: %d" , u . ID )
2015-09-06 19:38:14 +05:30
return nil
}
2017-12-03 17:25:13 +05:30
// SizedRelAvatarLink returns a relative link to the user's avatar. When
// applicable, the link is for an avatar of the indicated size (in pixels).
func ( u * User ) SizedRelAvatarLink ( size int ) string {
2016-07-23 22:38:22 +05:30
if u . ID == - 1 {
2017-06-29 21:40:33 +05:30
return base . DefaultAvatarLink ( )
2015-08-15 00:18:05 +05:30
}
2014-11-21 21:28:08 +05:30
switch {
case u . UseCustomAvatar :
2017-02-27 06:12:22 +05:30
if ! com . IsFile ( u . CustomAvatarPath ( ) ) {
2017-06-29 21:40:33 +05:30
return base . DefaultAvatarLink ( )
2015-08-09 09:16:10 +05:30
}
2017-02-25 20:28:57 +05:30
return setting . AppSubURL + "/avatars/" + u . Avatar
2015-03-13 06:02:38 +05:30
case setting . DisableGravatar , setting . OfflineMode :
2017-02-27 06:12:22 +05:30
if ! com . IsFile ( u . CustomAvatarPath ( ) ) {
2015-09-06 19:38:14 +05:30
if err := u . GenerateRandomAvatar ( ) ; err != nil {
log . Error ( 3 , "GenerateRandomAvatar: %v" , err )
2015-08-09 09:16:10 +05:30
}
}
2017-02-25 20:28:57 +05:30
return setting . AppSubURL + "/avatars/" + u . Avatar
2014-04-10 23:50:58 +05:30
}
2017-12-03 17:25:13 +05:30
return base . SizedAvatarLink ( u . AvatarEmail , size )
}
// RelAvatarLink returns a relative link to the user's avatar. The link
// may either be a sub-URL to this site, or a full URL to an external avatar
// service.
func ( u * User ) RelAvatarLink ( ) string {
return u . SizedRelAvatarLink ( base . DefaultAvatarSize )
2014-04-10 23:50:58 +05:30
}
2016-08-06 00:42:54 +05:30
// AvatarLink returns user avatar absolute link.
2015-08-28 21:06:13 +05:30
func ( u * User ) AvatarLink ( ) string {
link := u . RelAvatarLink ( )
2015-09-02 14:46:30 +05:30
if link [ 0 ] == '/' && link [ 1 ] != '/' {
2016-11-27 15:44:25 +05:30
return setting . AppURL + strings . TrimPrefix ( link , setting . AppSubURL ) [ 1 : ]
2015-08-28 21:06:13 +05:30
}
return link
}
2016-11-28 22:17:46 +05:30
// GetFollowers returns range of user's followers.
2015-12-21 17:54:11 +05:30
func ( u * User ) GetFollowers ( page int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , ItemsPerPage )
2016-11-10 20:46:32 +05:30
sess := x .
Limit ( ItemsPerPage , ( page - 1 ) * ItemsPerPage ) .
Where ( "follow.follow_id=?" , u . ID )
2015-12-21 17:54:11 +05:30
if setting . UsePostgreSQL {
sess = sess . Join ( "LEFT" , "follow" , ` "user".id=follow.user_id ` )
} else {
sess = sess . Join ( "LEFT" , "follow" , "user.id=follow.user_id" )
}
return users , sess . Find ( & users )
}
2016-11-28 22:17:46 +05:30
// IsFollowing returns true if user is following followID.
2015-12-21 17:54:11 +05:30
func ( u * User ) IsFollowing ( followID int64 ) bool {
2016-07-23 22:38:22 +05:30
return IsFollowing ( u . ID , followID )
2015-12-21 17:54:11 +05:30
}
// GetFollowing returns range of user's following.
func ( u * User ) GetFollowing ( page int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , ItemsPerPage )
2016-11-10 20:46:32 +05:30
sess := x .
Limit ( ItemsPerPage , ( page - 1 ) * ItemsPerPage ) .
Where ( "follow.user_id=?" , u . ID )
2015-12-21 17:54:11 +05:30
if setting . UsePostgreSQL {
sess = sess . Join ( "LEFT" , "follow" , ` "user".id=follow.follow_id ` )
} else {
sess = sess . Join ( "LEFT" , "follow" , "user.id=follow.follow_id" )
}
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 {
2016-08-28 02:07:55 +05:30
Name : u . DisplayName ( ) ,
2017-01-08 08:42:03 +05:30
Email : u . getEmail ( ) ,
2014-04-10 23:50:58 +05:30
When : time . Now ( ) ,
}
}
2018-01-12 03:49:38 +05:30
func hashPassword ( passwd , salt string ) string {
tempPasswd := pbkdf2 . Key ( [ ] byte ( passwd ) , [ ] byte ( salt ) , 10000 , 50 , sha256 . New )
return fmt . Sprintf ( "%x" , tempPasswd )
}
2018-01-09 03:58:18 +05:30
// HashPassword hashes a password using PBKDF.
2018-01-12 03:49:38 +05:30
func ( u * User ) HashPassword ( passwd string ) {
u . Passwd = hashPassword ( passwd , u . Salt )
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 {
2018-01-12 03:49:38 +05:30
tempHash := hashPassword ( passwd , u . Salt )
return subtle . ConstantTimeCompare ( [ ] byte ( u . Passwd ) , [ ] byte ( tempHash ) ) == 1
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 {
return ! u . ValidatePassword ( "" )
}
2014-11-21 21:28:08 +05:30
// UploadAvatar saves custom avatar for user.
2014-12-07 06:52:48 +05:30
// FIXME: split uploads to different subdirs in case we have massive users.
2014-11-21 21:28:08 +05:30
func ( u * User ) UploadAvatar ( data [ ] byte ) error {
2018-07-03 09:26:32 +05:30
imgCfg , _ , err := image . DecodeConfig ( bytes . NewReader ( data ) )
if err != nil {
return fmt . Errorf ( "DecodeConfig: %v" , err )
}
if imgCfg . Width > setting . AvatarMaxWidth {
return fmt . Errorf ( "Image width is to large: %d > %d" , imgCfg . Width , setting . AvatarMaxWidth )
}
if imgCfg . Height > setting . AvatarMaxHeight {
return fmt . Errorf ( "Image height is to large: %d > %d" , imgCfg . Height , setting . AvatarMaxHeight )
}
2014-11-21 21:28:08 +05:30
img , _ , err := image . Decode ( bytes . NewReader ( data ) )
if err != nil {
2015-11-14 03:13:43 +05:30
return fmt . Errorf ( "Decode: %v" , err )
2014-11-21 21:28:08 +05:30
}
2015-09-07 03:49:30 +05:30
2016-11-25 14:07:04 +05:30
m := resize . Resize ( avatar . AvatarSize , avatar . AvatarSize , img , resize . NearestNeighbor )
2014-11-21 21:28:08 +05:30
sess := x . NewSession ( )
2017-06-21 06:27:05 +05:30
defer sess . Close ( )
2014-11-21 21:28:08 +05:30
if err = sess . Begin ( ) ; err != nil {
return err
}
2015-11-14 03:13:43 +05:30
u . UseCustomAvatar = true
2017-02-25 20:28:57 +05:30
u . Avatar = fmt . Sprintf ( "%x" , md5 . Sum ( data ) )
2015-11-14 03:13:43 +05:30
if err = updateUser ( sess , u ) ; err != nil {
return fmt . Errorf ( "updateUser: %v" , err )
2014-11-21 21:28:08 +05:30
}
2016-12-01 05:26:15 +05:30
if err := os . MkdirAll ( setting . AvatarUploadPath , os . ModePerm ) ; err != nil {
2017-01-30 01:43:57 +05:30
return fmt . Errorf ( "Failed to create dir %s: %v" , setting . AvatarUploadPath , err )
2016-12-01 05:26:15 +05:30
}
2014-11-22 20:52:53 +05:30
fw , err := os . Create ( u . CustomAvatarPath ( ) )
2014-11-21 21:28:08 +05:30
if err != nil {
2015-11-14 03:13:43 +05:30
return fmt . Errorf ( "Create: %v" , err )
2014-11-21 21:28:08 +05:30
}
defer fw . Close ( )
2015-09-07 03:49:30 +05:30
2015-11-14 03:13:43 +05:30
if err = png . Encode ( fw , m ) ; err != nil {
return fmt . Errorf ( "Encode: %v" , err )
2014-11-21 21:28:08 +05:30
}
return sess . Commit ( )
}
2016-03-06 22:06:30 +05:30
// DeleteAvatar deletes the user's custom avatar.
func ( u * User ) DeleteAvatar ( ) error {
2016-07-23 22:38:22 +05:30
log . Trace ( "DeleteAvatar[%d]: %s" , u . ID , u . CustomAvatarPath ( ) )
2017-03-06 13:45:40 +05:30
if len ( u . Avatar ) > 0 {
if err := os . Remove ( u . CustomAvatarPath ( ) ) ; err != nil {
return fmt . Errorf ( "Failed to remove %s: %v" , u . CustomAvatarPath ( ) , err )
}
2016-12-01 05:26:15 +05:30
}
2016-03-06 22:06:30 +05:30
u . UseCustomAvatar = false
2017-03-06 13:45:40 +05:30
u . Avatar = ""
2017-10-05 10:13:04 +05:30
if _ , err := x . ID ( u . ID ) . Cols ( "avatar, use_custom_avatar" ) . Update ( u ) ; err != nil {
2016-03-06 23:54:42 +05:30
return fmt . Errorf ( "UpdateUser: %v" , err )
2016-03-06 22:06:30 +05:30
}
return nil
}
2015-08-14 00:13:40 +05:30
// IsAdminOfRepo returns true if user has admin or higher access of repository.
func ( u * User ) IsAdminOfRepo ( repo * Repository ) bool {
2017-03-15 06:21:46 +05:30
has , err := HasAccess ( u . ID , repo , AccessModeAdmin )
2016-03-06 07:15:23 +05:30
if err != nil {
log . Error ( 3 , "HasAccess: %v" , err )
2015-08-14 00:13:40 +05:30
}
2016-03-06 07:15:23 +05:30
return has
2015-08-14 00:13:40 +05:30
}
2016-03-06 07:15:23 +05:30
// IsWriterOfRepo returns true if user has write access to given repository.
func ( u * User ) IsWriterOfRepo ( repo * Repository ) bool {
2017-03-15 06:21:46 +05:30
has , err := HasAccess ( u . ID , repo , AccessModeWrite )
2016-03-05 02:13:01 +05:30
if err != nil {
log . Error ( 3 , "HasAccess: %v" , err )
}
return has
}
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
}
2014-08-15 15:59:41 +05:30
// IsUserOrgOwner returns true if user is in the owner team of given organization.
2016-11-28 22:17:46 +05:30
func ( u * User ) IsUserOrgOwner ( orgID int64 ) bool {
2017-12-21 13:13:26 +05:30
isOwner , err := IsOrganizationOwner ( orgID , u . ID )
if err != nil {
log . Error ( 4 , "IsOrganizationOwner: %v" , err )
return false
}
return isOwner
2014-08-15 15:59:41 +05:30
}
2017-01-20 10:46:10 +05:30
// IsPublicMember returns true if user public his/her membership in given organization.
2016-11-28 22:17:46 +05:30
func ( u * User ) IsPublicMember ( orgID int64 ) bool {
2017-12-21 13:13:26 +05:30
isMember , err := IsPublicMembership ( orgID , u . ID )
if err != nil {
log . Error ( 4 , "IsPublicMembership: %v" , err )
return false
}
return isMember
2014-08-15 15:59:41 +05:30
}
2015-09-06 18:24:08 +05:30
func ( u * User ) getOrganizationCount ( e Engine ) ( int64 , error ) {
2016-11-10 20:46:32 +05:30
return e .
Where ( "uid=?" , u . ID ) .
Count ( new ( OrgUser ) )
2015-09-06 18:24:08 +05:30
}
2014-06-29 01:13:25 +05:30
// GetOrganizationCount returns count of membership of organization of user.
func ( u * User ) GetOrganizationCount ( ) ( int64 , error ) {
2015-09-06 18:24:08 +05:30
return u . getOrganizationCount ( x )
2014-06-29 01:13:25 +05:30
}
2016-07-24 12:02:46 +05:30
// GetRepositories returns repositories that user owns, including private repositories.
func ( u * User ) GetRepositories ( page , pageSize int ) ( err error ) {
2017-02-04 17:50:20 +05:30
u . Repos , err = GetUserRepositories ( u . ID , true , page , pageSize , "" )
2014-08-24 18:39:05 +05:30
return err
}
2018-06-21 21:30:13 +05:30
// GetRepositoryIDs returns repositories IDs where user owned and has unittypes
func ( u * User ) GetRepositoryIDs ( units ... UnitType ) ( [ ] int64 , error ) {
2017-02-17 06:28:19 +05:30
var ids [ ] int64
2018-06-21 21:30:13 +05:30
sess := x . Table ( "repository" ) . Cols ( "repository.id" )
if len ( units ) > 0 {
sess = sess . Join ( "INNER" , "repo_unit" , "repository.id = repo_unit.repo_id" )
sess = sess . In ( "repo_unit.type" , units )
}
return ids , sess . Where ( "owner_id = ?" , u . ID ) . Find ( & ids )
2017-02-17 06:28:19 +05:30
}
2018-06-21 21:30:13 +05:30
// GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes
func ( u * User ) GetOrgRepositoryIDs ( units ... UnitType ) ( [ ] int64 , error ) {
2017-02-17 06:28:19 +05:30
var ids [ ] int64
2018-06-21 21:30:13 +05:30
sess := x . Table ( "repository" ) .
2017-02-17 06:28:19 +05:30
Cols ( "repository.id" ) .
2018-06-21 21:30:13 +05:30
Join ( "INNER" , "team_user" , "repository.owner_id = team_user.org_id" ) .
Join ( "INNER" , "team_repo" , "repository.is_private != ? OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)" , true )
if len ( units ) > 0 {
sess = sess . Join ( "INNER" , "team_unit" , "team_unit.team_id = team_user.team_id" )
sess = sess . In ( "team_unit.type" , units )
}
return ids , sess .
Where ( "team_user.uid = ?" , u . ID ) .
2017-02-17 06:28:19 +05:30
GroupBy ( "repository.id" ) . Find ( & ids )
}
2017-03-15 06:22:01 +05:30
// GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
2018-06-21 21:30:13 +05:30
func ( u * User ) GetAccessRepoIDs ( units ... UnitType ) ( [ ] int64 , error ) {
ids , err := u . GetRepositoryIDs ( units ... )
2017-02-17 06:28:19 +05:30
if err != nil {
return nil , err
}
2018-06-21 21:30:13 +05:30
ids2 , err := u . GetOrgRepositoryIDs ( units ... )
2017-02-17 06:28:19 +05:30
if err != nil {
return nil , err
}
return append ( ids , ids2 ... ) , nil
}
2016-11-28 22:17:46 +05:30
// GetMirrorRepositories returns mirror repositories that user owns, including private repositories.
2016-07-24 12:02:46 +05:30
func ( u * User ) GetMirrorRepositories ( ) ( [ ] * Repository , error ) {
return GetUserMirrorRepositories ( u . ID )
}
2015-09-07 23:28:23 +05:30
// GetOwnedOrganizations returns all organizations that user owns.
func ( u * User ) GetOwnedOrganizations ( ) ( err error ) {
2016-07-23 22:38:22 +05:30
u . OwnedOrgs , err = GetOwnedOrgsByUserID ( u . ID )
2015-09-07 23:28:23 +05:30
return err
}
2014-06-28 10:10:07 +05:30
// GetOrganizations returns all organizations that user belongs to.
2015-12-17 12:58:47 +05:30
func ( u * User ) GetOrganizations ( all bool ) error {
2016-07-23 22:38:22 +05:30
ous , err := GetOrgUsersByUserID ( u . ID , all )
2014-06-25 10:14:48 +05:30
if err != nil {
return err
}
u . Orgs = make ( [ ] * User , len ( ous ) )
for i , ou := range ous {
2015-08-08 20:13:14 +05:30
u . Orgs [ i ] , err = GetUserByID ( ou . OrgID )
2014-06-25 10:14:48 +05:30
if err != nil {
return err
}
}
return nil
2014-04-10 23:50:58 +05:30
}
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 {
if len ( u . FullName ) > 0 {
return u . FullName
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
}
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
}
2017-10-03 11:59:26 +05:30
func isUserExist ( e 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 ) {
return isUserExist ( x , uid , name )
}
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 ) {
2018-02-18 23:44:37 +05:30
return generate . GetRandomString ( 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
}
}
2016-07-23 16:28:18 +05:30
var (
2018-05-02 13:36:31 +05:30
reservedUsernames = [ ] string { "assets" , "css" , "explore" , "img" , "js" , "less" , "plugins" , "debug" , "raw" , "install" , "api" , "avatars" , "user" , "org" , "help" , "stars" , "issues" , "pulls" , "commits" , "repo" , "template" , "admin" , "error" , "new" , "." , ".." }
2016-11-12 17:56:45 +05:30
reservedUserPatterns = [ ] string { "*.keys" }
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 {
2016-11-12 17:56:45 +05:30
return isUsableName ( reservedUsernames , reservedUserPatterns , name )
2016-07-23 16:28:18 +05:30
}
2014-06-25 10:14:48 +05:30
// CreateUser creates record of a new user.
2015-03-27 02:41:47 +05:30
func CreateUser ( u * User ) ( 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
}
2017-10-03 11:59:26 +05:30
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
isExist , err := isUserExist ( sess , 0 , u . Name )
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 {
2015-03-27 02:41:47 +05:30
return ErrUserAlreadyExist { u . Name }
2014-06-25 10:14:48 +05:30
}
2015-11-24 07:13:04 +05:30
u . Email = strings . ToLower ( u . Email )
2017-10-03 11:59:26 +05:30
isExist , err = sess .
2017-01-05 06:22:20 +05:30
Where ( "email=?" , u . Email ) .
Get ( new ( User ) )
if err != nil {
return err
2017-10-03 11:59:26 +05:30
} else if isExist {
2017-01-05 06:22:20 +05:30
return ErrEmailAlreadyUsed { u . Email }
}
2017-10-03 11:59:26 +05:30
isExist , err = isEmailUsed ( sess , 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 {
2015-03-27 02:41:47 +05:30
return ErrEmailAlreadyUsed { u . Email }
2014-06-25 10:14:48 +05:30
}
2017-01-08 08:42:03 +05:30
u . KeepEmailPrivate = setting . Service . DefaultKeepEmailPrivate
2014-06-25 10:14:48 +05:30
u . LowerName = strings . ToLower ( u . Name )
u . AvatarEmail = u . Email
2016-02-15 09:44:55 +05:30
u . Avatar = base . HashEmail ( u . AvatarEmail )
2016-12-20 18:02:02 +05:30
if u . Rands , err = GetUserSalt ( ) ; err != nil {
return err
}
if u . Salt , err = GetUserSalt ( ) ; err != nil {
return err
}
2018-01-12 03:49:38 +05:30
u . HashPassword ( u . Passwd )
2017-05-09 01:21:53 +05:30
u . AllowCreateOrganization = setting . Service . DefaultAllowCreateOrganization
2015-12-12 01:41:13 +05:30
u . MaxRepoCreation = - 1
2014-06-25 10:14:48 +05:30
if _ , err = sess . Insert ( u ) ; err != nil {
2014-07-26 09:54:27 +05:30
return err
} else if err = os . MkdirAll ( UserPath ( u . Name ) , os . ModePerm ) ; err != nil {
return err
2014-06-25 10:14:48 +05:30
}
2014-04-22 22:25:27 +05:30
2015-08-19 02:28:45 +05:30
return sess . Commit ( )
2014-06-25 10:14:48 +05:30
}
2015-08-06 21:30:11 +05:30
func countUsers ( e 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 {
2015-08-06 21:30:11 +05:30
return countUsers ( x )
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
}
2014-07-26 09:54:27 +05:30
log . Error ( 4 , "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 ]
2016-07-23 22:38:22 +05:30
data := com . ToStr ( 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
}
2016-11-28 22:17:46 +05:30
// VerifyActiveEmailCode verifies active email code when active account
2014-12-17 21:10:10 +05:30
func VerifyActiveEmailCode ( code , email string ) * EmailAddress {
minutes := setting . Service . ActiveCodeLives
if user := getVerifyUser ( code ) ; user != nil {
// time limit code
prefix := code [ : base . TimeLimitCodeLength ]
2016-07-23 22:38:22 +05:30
data := com . ToStr ( user . ID ) + email + user . LowerName + user . Passwd + user . Rands
2014-12-17 21:10:10 +05:30
if base . VerifyTimeLimitCode ( data , minutes , prefix ) {
emailAddress := & EmailAddress { Email : email }
if has , _ := x . Get ( emailAddress ) ; has {
return emailAddress
}
}
}
return nil
}
2014-04-10 23:50:58 +05:30
// 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 ) {
2016-07-23 16:28:18 +05:30
if err = IsUsableUsername ( newUserName ) ; err != nil {
2015-03-27 02:41:47 +05:30
return err
}
isExist , err := IsUserExist ( 0 , newUserName )
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist { newUserName }
2014-07-26 09:54:27 +05:30
}
2016-02-06 00:41:53 +05:30
if err = ChangeUsernameInPullRequests ( u . Name , newUserName ) ; err != nil {
2016-01-28 16:37:16 +05:30
return fmt . Errorf ( "ChangeUsernameInPullRequests: %v" , err )
2016-01-28 03:15:03 +05:30
}
2016-02-06 00:41:53 +05:30
// Delete all local copies of repository wiki that user owns.
2017-10-21 11:56:23 +05:30
if err = x . BufferSize ( setting . IterateBufferSize ) .
2016-11-10 20:46:32 +05:30
Where ( "owner_id=?" , u . ID ) .
Iterate ( new ( Repository ) , func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
RemoveAllWithNotice ( "Delete repository wiki local copy" , repo . LocalWikiPath ( ) )
return nil
} ) ; err != nil {
2016-02-06 00:41:53 +05:30
return fmt . Errorf ( "Delete repository wiki local copy: %v" , err )
}
2016-01-28 16:37:16 +05:30
return os . Rename ( UserPath ( u . Name ) , UserPath ( newUserName ) )
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
func checkDupEmail ( e Engine , u * User ) error {
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 {
return ErrEmailAlreadyUsed { u . Email }
}
return nil
}
2015-08-29 22:43:24 +05:30
func updateUser ( e Engine , u * User ) error {
2017-10-05 10:13:04 +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 {
return updateUser ( x , u )
2017-02-25 20:23:57 +05:30
}
2017-08-12 19:48:44 +05:30
// UpdateUserCols update user according special columns
func UpdateUserCols ( u * User , cols ... string ) error {
2017-09-25 10:29:27 +05:30
return updateUserCols ( x , u , cols ... )
}
func updateUserCols ( e Engine , u * User , cols ... string ) error {
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.
func UpdateUserSetting ( u * User ) error {
if ! u . IsOrganization ( ) {
if err := checkDupEmail ( x , u ) ; err != nil {
return err
}
}
return updateUser ( x , u )
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.
func deleteBeans ( e 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
}
2014-11-13 15:57:01 +05:30
// FIXME: need some kind of mechanism to record failure. HINT: system notice
2015-09-06 18:24:08 +05:30
func deleteUser ( e * xorm . Session , u * User ) error {
2015-08-17 14:35:37 +05:30
// Note: A user owns any repository or belongs to any organization
// cannot perform delete operation.
2014-04-10 23:50:58 +05:30
// Check ownership of repository.
2015-09-06 18:24:08 +05:30
count , err := getRepositoryCount ( e , u )
2014-04-10 23:50:58 +05:30
if err != nil {
2015-03-18 07:21:39 +05:30
return fmt . Errorf ( "GetRepositoryCount: %v" , err )
2014-04-10 23:50:58 +05:30
} else if count > 0 {
2016-07-23 22:38:22 +05:30
return ErrUserOwnRepos { UID : u . ID }
2014-04-10 23:50:58 +05:30
}
2014-06-27 13:07:01 +05:30
// Check membership of organization.
2015-09-06 18:24:08 +05:30
count , err = u . getOrganizationCount ( e )
2014-06-27 13:07:01 +05:30
if err != nil {
2015-03-18 07:21:39 +05:30
return fmt . Errorf ( "GetOrganizationCount: %v" , err )
2014-06-27 13:07:01 +05:30
} else if count > 0 {
2016-07-23 22:38:22 +05:30
return ErrUserHasOrgs { UID : u . ID }
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" ) .
Where ( "watch.user_id = ?" , u . ID ) . 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
}
2017-05-20 14:18:22 +05:30
if _ , err = e . Decr ( "num_watches" ) . In ( "id" , watchedRepoIDs ) . Update ( new ( Repository ) ) ; err != nil {
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-04-10 18:09:01 +05:30
} else if _ , err = e . Decr ( "num_stars" ) . In ( "id" , starredRepoIDs ) . 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 } ,
2017-05-20 14:18:22 +05:30
& Follow { UserID : u . ID } ,
2016-07-23 22:38:22 +05:30
& Follow { FollowID : u . ID } ,
& Action { UserID : u . ID } ,
& IssueUser { UID : u . ID } ,
& EmailAddress { UID : u . ID } ,
2017-03-17 19:46:08 +05:30
& UserOpenID { UID : u . ID } ,
2017-12-04 04:44:26 +05:30
& Reaction { 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
2015-08-17 14:35:37 +05:30
// ***** START: PublicKey *****
2014-05-07 01:58:52 +05:30
keys := make ( [ ] * PublicKey , 0 , 10 )
2016-07-23 22:38:22 +05:30
if err = e . Find ( & keys , & PublicKey { OwnerID : u . ID } ) ; err != nil {
2015-08-17 14:35:37 +05:30
return fmt . Errorf ( "get all public keys: %v" , err )
2014-04-10 23:50:58 +05:30
}
2016-07-26 14:56:48 +05:30
keyIDs := make ( [ ] int64 , len ( keys ) )
for i := range keys {
keyIDs [ i ] = keys [ i ] . ID
}
if err = deletePublicKeys ( e , keyIDs ... ) ; err != nil {
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
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
// FIXME: system notice
// Note: There are something just cannot be roll back,
// so just keep error logs of those operations.
2016-12-01 05:26:15 +05:30
path := UserPath ( u . Name )
2015-08-17 14:35:37 +05:30
2016-12-01 05:26:15 +05:30
if err := os . RemoveAll ( path ) ; err != nil {
2017-01-30 01:43:57 +05:30
return fmt . Errorf ( "Failed to RemoveAll %s: %v" , path , err )
2016-12-01 05:26:15 +05:30
}
2017-03-06 13:45:40 +05:30
if len ( u . Avatar ) > 0 {
avatarPath := u . CustomAvatarPath ( )
if com . IsExist ( avatarPath ) {
if err := os . Remove ( avatarPath ) ; err != nil {
return fmt . Errorf ( "Failed to remove %s: %v" , avatarPath , err )
}
2016-12-27 07:32:14 +05:30
}
2016-12-01 05:26:15 +05:30
}
2014-04-10 23:50:58 +05:30
2015-08-17 14:35:37 +05:30
return nil
2014-06-21 10:21:41 +05:30
}
2015-09-06 18:24:08 +05:30
// DeleteUser completely and permanently deletes everything of a user,
// but issues/comments/pulls will be kept and shown as someone has been deleted.
func DeleteUser ( u * User ) ( err error ) {
sess := x . NewSession ( )
2017-06-21 06:27:05 +05:30
defer sess . Close ( )
2015-09-06 18:24:08 +05:30
if err = sess . Begin ( ) ; err != nil {
return err
}
if err = deleteUser ( sess , u ) ; err != nil {
2015-09-13 22:56:20 +05:30
// Note: don't wrapper error here.
return err
2015-09-06 18:24:08 +05:30
}
2016-07-26 14:56:48 +05:30
if err = sess . Commit ( ) ; err != nil {
return err
}
return RewriteAllPublicKeys ( )
2015-09-06 18:24:08 +05:30
}
2014-12-17 13:56:19 +05:30
// DeleteInactivateUsers deletes all inactivate users and email addresses.
2015-08-17 14:35:37 +05:30
func DeleteInactivateUsers ( ) ( err error ) {
users := make ( [ ] * User , 0 , 10 )
2016-11-10 20:46:32 +05:30
if err = x .
Where ( "is_active = ?" , false ) .
Find ( & users ) ; err != nil {
2015-08-17 14:35:37 +05:30
return fmt . Errorf ( "get all inactive users: %v" , err )
}
2016-07-26 14:56:48 +05:30
// FIXME: should only update authorized_keys file once after all deletions.
2015-08-17 14:35:37 +05:30
for _ , u := range users {
if err = DeleteUser ( u ) ; err != nil {
// Ignore users that were set inactive by admin.
if IsErrUserOwnRepos ( err ) || IsErrUserHasOrgs ( err ) {
continue
}
return err
}
2014-12-17 13:56:19 +05:30
}
2015-08-17 14:35:37 +05:30
2016-11-10 20:46:32 +05:30
_ , err = x .
Where ( "is_activated = ?" , false ) .
Delete ( new ( EmailAddress ) )
2014-04-10 23:50:58 +05:30
return err
}
// 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
}
2016-11-12 04:31:09 +05:30
// GetUserByKeyID get user information by user's public key id
2015-11-05 08:27:10 +05:30
func GetUserByKeyID ( keyID int64 ) ( * User , error ) {
2016-11-12 04:31:09 +05:30
var user User
has , err := x . Join ( "INNER" , "public_key" , "`public_key`.owner_id = `user`.id" ) .
2016-11-10 20:46:32 +05:30
Where ( "`public_key`.id=?" , keyID ) .
2016-11-12 14:00:46 +05:30
Get ( & user )
2016-11-12 04:31:09 +05:30
if err != nil {
return nil , err
}
if ! has {
return nil , ErrUserNotExist { 0 , "" , keyID }
}
return & user , nil
2014-04-10 23:50:58 +05:30
}
2015-08-08 20:13:14 +05:30
func getUserByID ( e 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 ) {
return getUserByID ( x , id )
2015-02-13 11:28:46 +05:30
}
2018-05-09 21:59:04 +05:30
// GetUserIfHasWriteAccess returns the user with write access of repository by given ID.
func GetUserIfHasWriteAccess ( repo * Repository , userID int64 ) ( * User , error ) {
2017-03-15 06:21:46 +05:30
has , err := HasAccess ( userID , repo , AccessModeWrite )
2015-08-10 19:17:23 +05:30
if err != nil {
return nil , err
} else if ! has {
2016-11-12 04:31:09 +05:30
return nil , ErrUserNotExist { userID , "" , 0 }
2015-08-10 19:17:23 +05:30
}
return GetUserByID ( userID )
}
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 ) {
2017-08-30 10:01:33 +05:30
return getUserByName ( x , name )
}
func getUserByName ( e 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
}
2014-10-12 03:32:48 +05:30
// GetUserEmailsByNames returns a list of e-mails corresponds to names.
2014-04-10 23:50:58 +05:30
func GetUserEmailsByNames ( names [ ] string ) [ ] string {
2017-08-30 10:01:33 +05:30
return getUserEmailsByNames ( x , names )
}
func getUserEmailsByNames ( e 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
}
2017-02-02 18:03:36 +05:30
if u . IsMailable ( ) {
mails = append ( mails , u . Email )
}
2014-04-10 23:50:58 +05:30
}
return mails
}
2016-11-10 13:40:35 +05:30
// GetUsersByIDs returns all resolved users from a list of Ids.
func GetUsersByIDs ( ids [ ] int64 ) ( [ ] * User , error ) {
ous := make ( [ ] * User , 0 , len ( ids ) )
2017-01-20 08:01:46 +05:30
if len ( ids ) == 0 {
return ous , nil
}
err := x . 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.
func GetUserIDsByNames ( names [ ] string ) [ ] int64 {
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 {
continue
}
2016-07-23 22:38:22 +05:30
ids = append ( ids , u . ID )
2014-05-08 02:21:14 +05:30
}
return ids
}
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.
func ValidateCommitsWithEmails ( oldCommits * list . List ) * list . List {
2015-08-05 15:06:22 +05:30
var (
u * User
emails = map [ string ] * User { }
newCommits = list . New ( )
e = oldCommits . Front ( )
)
2014-09-24 01:00:04 +05:30
for e != nil {
2015-12-10 07:16:05 +05:30
c := e . Value . ( * git . Commit )
2014-09-24 01:00:04 +05:30
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 08:48:14 +05:30
} else {
2017-10-26 13:15:14 +05:30
u = nil
2014-09-24 01:00:04 +05:30
}
newCommits . PushBack ( UserCommit {
2014-11-21 21:28:08 +05:30
User : u ,
Commit : c ,
2014-09-24 01:00:04 +05:30
} )
e = e . Next ( )
}
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 ) {
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 }
2014-06-21 10:21:41 +05:30
has , err := x . 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
2015-08-05 15:06:22 +05:30
emailAddress := & EmailAddress { Email : email , IsActivated : true }
2014-12-17 13:56:19 +05:30
has , err = x . Get ( emailAddress )
if err != nil {
return nil , err
}
if has {
2015-09-10 21:10:34 +05:30
return GetUserByID ( emailAddress . UID )
2014-12-17 13:56:19 +05:30
}
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 ) {
return x . Get ( user )
}
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 {
2017-11-26 14:10:38 +05:30
Keyword string
Type UserType
2018-05-24 06:33:42 +05:30
OrderBy SearchOrderBy
2017-11-26 14:10:38 +05:30
Page int
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
IsActive util . OptionalBool
SearchByEmail bool // Search by email as well as username/full name
2016-03-12 02:03:12 +05:30
}
2017-10-24 23:06:19 +05:30
func ( opts * SearchUserOptions ) toConds ( ) builder . Cond {
var cond builder . Cond = builder . Eq { "type" : opts . Type }
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 } )
}
cond = cond . And ( keywordCond )
2016-03-12 02:03:12 +05:30
}
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
}
2017-10-24 23:06:19 +05:30
return cond
}
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 ) {
cond := opts . toConds ( )
2017-02-25 19:12:20 +05:30
count , err := x . Where ( cond ) . 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 opts . PageSize <= 0 || opts . PageSize > setting . UI . ExplorePagingNum {
opts . PageSize = setting . UI . ExplorePagingNum
}
if opts . Page <= 0 {
opts . Page = 1
2016-03-12 02:41:33 +05:30
}
2017-10-24 23:06:19 +05:30
if len ( opts . OrderBy ) == 0 {
opts . OrderBy = "name ASC"
}
users = make ( [ ] * User , 0 , opts . PageSize )
return users , count , x . Where ( cond ) .
Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize ) .
2018-05-24 06:33:42 +05:30
OrderBy ( opts . OrderBy . String ( ) ) .
2017-10-24 23:06:19 +05:30
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
func GetStarredRepos ( userID int64 , private bool ) ( [ ] * Repository , error ) {
sess := x . Where ( "star.uid=?" , userID ) .
Join ( "LEFT" , "star" , "`repository`.id=`star`.repo_id" )
if ! private {
sess = sess . And ( "is_private=?" , false )
}
repos := make ( [ ] * Repository , 0 , 10 )
err := sess . Find ( & repos )
if err != nil {
return nil , err
}
return repos , nil
}
2016-12-24 07:23:11 +05:30
// GetWatchedRepos returns the repos watched by a particular user
func GetWatchedRepos ( userID int64 , private bool ) ( [ ] * Repository , error ) {
sess := x . Where ( "watch.user_id=?" , userID ) .
Join ( "LEFT" , "watch" , "`repository`.id=`watch`.repo_id" )
if ! private {
sess = sess . And ( "is_private=?" , false )
}
repos := make ( [ ] * Repository , 0 , 10 )
err := sess . Find ( & repos )
if err != nil {
return nil , err
}
return repos , nil
}
2017-05-10 18:40:18 +05:30
2018-05-24 10:29:02 +05:30
// deleteKeysMarkedForDeletion returns true if ssh keys needs update
func deleteKeysMarkedForDeletion ( keys [ ] string ) ( bool , error ) {
// Start session
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
return false , err
}
// Delete keys marked for deletion
var sshKeysNeedUpdate bool
for _ , KeyToDelete := range keys {
key , err := SearchPublicKeyByContent ( KeyToDelete )
if err != nil {
log . Error ( 4 , "SearchPublicKeyByContent: %v" , err )
continue
}
if err = deletePublicKeys ( sess , key . ID ) ; err != nil {
log . Error ( 4 , "deletePublicKeys: %v" , err )
continue
}
sshKeysNeedUpdate = true
}
if err := sess . Commit ( ) ; err != nil {
return false , err
}
return sshKeysNeedUpdate , nil
}
func addLdapSSHPublicKeys ( s * LoginSource , usr * User , SSHPublicKeys [ ] string ) bool {
var sshKeysNeedUpdate bool
for _ , sshKey := range SSHPublicKeys {
if strings . HasPrefix ( strings . ToLower ( sshKey ) , "ssh" ) {
sshKeyName := fmt . Sprintf ( "%s-%s" , s . Name , sshKey [ 0 : 40 ] )
if _ , err := AddPublicKey ( usr . ID , sshKeyName , sshKey , s . ID ) ; err != nil {
log . Error ( 4 , "addLdapSSHPublicKeys[%s]: Error adding LDAP Public SSH Key for user %s: %v" , s . Name , usr . Name , err )
} else {
log . Trace ( "addLdapSSHPublicKeys[%s]: Added LDAP Public SSH Key for user %s" , s . Name , usr . Name )
sshKeysNeedUpdate = true
}
} else {
log . Warn ( "addLdapSSHPublicKeys[%s]: Skipping invalid LDAP Public SSH Key for user %s: %v" , s . Name , usr . Name , sshKey )
}
}
return sshKeysNeedUpdate
}
func synchronizeLdapSSHPublicKeys ( s * LoginSource , SSHPublicKeys [ ] string , usr * User ) bool {
var sshKeysNeedUpdate bool
log . Trace ( "synchronizeLdapSSHPublicKeys[%s]: Handling LDAP Public SSH Key synchronization for user %s" , s . Name , usr . Name )
// Get Public Keys from DB with current LDAP source
var giteaKeys [ ] string
keys , err := ListPublicLdapSSHKeys ( usr . ID , s . ID )
if err != nil {
log . Error ( 4 , "synchronizeLdapSSHPublicKeys[%s]: Error listing LDAP Public SSH Keys for user %s: %v" , s . Name , usr . Name , err )
}
for _ , v := range keys {
giteaKeys = append ( giteaKeys , v . OmitEmail ( ) )
}
// Get Public Keys from LDAP and skip duplicate keys
var ldapKeys [ ] string
for _ , v := range SSHPublicKeys {
ldapKey := strings . Join ( strings . Split ( v , " " ) [ : 2 ] , " " )
if ! util . ExistsInSlice ( ldapKey , ldapKeys ) {
ldapKeys = append ( ldapKeys , ldapKey )
}
}
// Check if Public Key sync is needed
if util . IsEqualSlice ( giteaKeys , ldapKeys ) {
log . Trace ( "synchronizeLdapSSHPublicKeys[%s]: LDAP Public Keys are already in sync for %s (LDAP:%v/DB:%v)" , s . Name , usr . Name , len ( ldapKeys ) , len ( giteaKeys ) )
return false
}
log . Trace ( "synchronizeLdapSSHPublicKeys[%s]: LDAP Public Key needs update for user %s (LDAP:%v/DB:%v)" , s . Name , usr . Name , len ( ldapKeys ) , len ( giteaKeys ) )
// Add LDAP Public SSH Keys that doesn't already exist in DB
var newLdapSSHKeys [ ] string
for _ , LDAPPublicSSHKey := range ldapKeys {
if ! util . ExistsInSlice ( LDAPPublicSSHKey , giteaKeys ) {
newLdapSSHKeys = append ( newLdapSSHKeys , LDAPPublicSSHKey )
}
}
if addLdapSSHPublicKeys ( s , usr , newLdapSSHKeys ) {
sshKeysNeedUpdate = true
}
// Mark LDAP keys from DB that doesn't exist in LDAP for deletion
var giteaKeysToDelete [ ] string
for _ , giteaKey := range giteaKeys {
if ! util . ExistsInSlice ( giteaKey , ldapKeys ) {
log . Trace ( "synchronizeLdapSSHPublicKeys[%s]: Marking LDAP Public SSH Key for deletion for user %s: %v" , s . Name , usr . Name , giteaKey )
giteaKeysToDelete = append ( giteaKeysToDelete , giteaKey )
}
}
// Delete LDAP keys from DB that doesn't exist in LDAP
needUpd , err := deleteKeysMarkedForDeletion ( giteaKeysToDelete )
if err != nil {
log . Error ( 4 , "synchronizeLdapSSHPublicKeys[%s]: Error deleting LDAP Public SSH Keys marked for deletion for user %s: %v" , s . Name , usr . Name , err )
}
if needUpd {
sshKeysNeedUpdate = true
}
return sshKeysNeedUpdate
}
2017-05-10 18:40:18 +05:30
// SyncExternalUsers is used to synchronize users with external authorization source
func SyncExternalUsers ( ) {
2017-05-31 14:27:17 +05:30
if ! taskStatusTable . StartIfNotRunning ( syncExternalUsers ) {
2017-05-10 18:40:18 +05:30
return
}
defer taskStatusTable . Stop ( syncExternalUsers )
log . Trace ( "Doing: SyncExternalUsers" )
ls , err := LoginSources ( )
if err != nil {
log . Error ( 4 , "SyncExternalUsers: %v" , err )
return
}
updateExisting := setting . Cron . SyncExternalUsers . UpdateExisting
for _ , s := range ls {
if ! s . IsActived || ! s . IsSyncEnabled {
continue
}
2018-05-24 10:29:02 +05:30
2017-05-10 18:40:18 +05:30
if s . IsLDAP ( ) {
log . Trace ( "Doing: SyncExternalUsers[%s]" , s . Name )
var existingUsers [ ] int64
2018-05-24 10:29:02 +05:30
var isAttributeSSHPublicKeySet = len ( strings . TrimSpace ( s . LDAP ( ) . AttributeSSHPublicKey ) ) > 0
var sshKeysNeedUpdate bool
2017-05-10 18:40:18 +05:30
// Find all users with this login type
var users [ ] User
x . Where ( "login_type = ?" , LoginLDAP ) .
And ( "login_source = ?" , s . ID ) .
Find ( & users )
sr := s . LDAP ( ) . SearchEntries ( )
for _ , su := range sr {
if len ( su . Username ) == 0 {
continue
}
if len ( su . Mail ) == 0 {
su . Mail = fmt . Sprintf ( "%s@localhost" , su . Username )
}
var usr * User
// Search for existing user
for _ , du := range users {
if du . LowerName == strings . ToLower ( su . Username ) {
usr = & du
break
}
}
fullName := composeFullName ( su . Name , su . Surname , su . Username )
// If no existing user found, create one
if usr == nil {
log . Trace ( "SyncExternalUsers[%s]: Creating user %s" , s . Name , su . Username )
usr = & User {
LowerName : strings . ToLower ( su . Username ) ,
Name : su . Username ,
FullName : fullName ,
LoginType : s . Type ,
LoginSource : s . ID ,
LoginName : su . Username ,
Email : su . Mail ,
IsAdmin : su . IsAdmin ,
IsActive : true ,
}
err = CreateUser ( usr )
2018-05-24 10:29:02 +05:30
2017-05-10 18:40:18 +05:30
if err != nil {
log . Error ( 4 , "SyncExternalUsers[%s]: Error creating user %s: %v" , s . Name , su . Username , err )
2018-05-24 10:29:02 +05:30
} else if isAttributeSSHPublicKeySet {
log . Trace ( "SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s" , s . Name , usr . Name )
if addLdapSSHPublicKeys ( s , usr , su . SSHPublicKey ) {
sshKeysNeedUpdate = true
}
2017-05-10 18:40:18 +05:30
}
} else if updateExisting {
existingUsers = append ( existingUsers , usr . ID )
2018-05-24 10:29:02 +05:30
// Synchronize SSH Public Key if that attribute is set
if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys ( s , su . SSHPublicKey , usr ) {
sshKeysNeedUpdate = true
}
2017-05-10 18:40:18 +05:30
// Check if user data has changed
if ( len ( s . LDAP ( ) . AdminFilter ) > 0 && usr . IsAdmin != su . IsAdmin ) ||
strings . ToLower ( usr . Email ) != strings . ToLower ( su . Mail ) ||
usr . FullName != fullName ||
! usr . IsActive {
log . Trace ( "SyncExternalUsers[%s]: Updating user %s" , s . Name , usr . Name )
usr . FullName = fullName
usr . Email = su . Mail
// Change existing admin flag only if AdminFilter option is set
if len ( s . LDAP ( ) . AdminFilter ) > 0 {
usr . IsAdmin = su . IsAdmin
}
usr . IsActive = true
2017-08-12 19:48:44 +05:30
err = UpdateUserCols ( usr , "full_name" , "email" , "is_admin" , "is_active" )
2017-05-10 18:40:18 +05:30
if err != nil {
log . Error ( 4 , "SyncExternalUsers[%s]: Error updating user %s: %v" , s . Name , usr . Name , err )
}
}
}
}
2018-05-24 10:29:02 +05:30
// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
if sshKeysNeedUpdate {
RewriteAllPublicKeys ( )
}
2017-05-10 18:40:18 +05:30
// Deactivate users not present in LDAP
if updateExisting {
for _ , usr := range users {
found := false
for _ , uid := range existingUsers {
if usr . ID == uid {
found = true
break
}
}
if ! found {
log . Trace ( "SyncExternalUsers[%s]: Deactivating user %s" , s . Name , usr . Name )
usr . IsActive = false
2017-08-12 19:48:44 +05:30
err = UpdateUserCols ( & usr , "is_active" )
2017-05-10 18:40:18 +05:30
if err != nil {
log . Error ( 4 , "SyncExternalUsers[%s]: Error deactivating user %s: %v" , s . Name , usr . Name , err )
}
}
}
}
}
}
}