2014-02-15 03:16:54 +04:00
// 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.
2014-02-14 18:20:57 +04:00
package models
import (
2014-03-19 20:50:44 +04:00
"encoding/hex"
2014-02-14 18:20:57 +04:00
"errors"
"fmt"
2014-02-20 10:53:56 +04:00
"os"
"path/filepath"
2014-02-14 18:20:57 +04:00
"strings"
"time"
"github.com/dchest/scrypt"
2014-03-17 19:56:50 +04:00
"github.com/gogits/git"
2014-03-08 02:22:15 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-19 20:50:44 +04:00
"github.com/gogits/gogs/modules/log"
2014-02-15 03:16:54 +04:00
)
// User types.
2014-02-14 18:20:57 +04:00
const (
2014-02-15 03:16:54 +04:00
UT_INDIVIDUAL = iota + 1
UT_ORGANIZATION
2014-02-14 18:20:57 +04:00
)
2014-02-15 03:16:54 +04:00
// Login types.
2014-02-14 18:20:57 +04:00
const (
2014-02-15 03:16:54 +04:00
LT_PLAIN = iota + 1
LT_LDAP
2014-02-14 18:20:57 +04:00
)
2014-03-21 00:04:56 +04:00
var (
ErrUserOwnRepos = errors . New ( "User still have ownership of repositories" )
ErrUserAlreadyExist = errors . New ( "User already exist" )
ErrUserNotExist = errors . New ( "User does not exist" )
ErrEmailAlreadyUsed = errors . New ( "E-mail already used" )
ErrUserNameIllegal = errors . New ( "User name contains illegal characters" )
)
2014-03-17 22:03:58 +04:00
// User represents the object of individual and member of organization.
2014-02-14 18:20:57 +04:00
type User struct {
Id int64
LowerName string ` xorm:"unique not null" `
2014-03-06 20:10:35 +04:00
Name string ` xorm:"unique not null" `
Email string ` xorm:"unique not null" `
Passwd string ` xorm:"not null" `
2014-02-14 18:20:57 +04:00
LoginType int
Type int
NumFollowers int
NumFollowings int
NumStars int
NumRepos int
2014-03-13 11:39:18 +04:00
Avatar string ` xorm:"varchar(2048) not null" `
2014-03-14 09:12:07 +04:00
AvatarEmail string ` xorm:"not null" `
2014-03-13 11:39:18 +04:00
Location string
Website string
2014-03-19 15:21:23 +04:00
IsActive bool
2014-03-20 15:50:26 +04:00
IsAdmin bool
2014-03-20 05:05:48 +04:00
Rands string ` xorm:"VARCHAR(10)" `
2014-02-14 18:20:57 +04:00
Created time . Time ` xorm:"created" `
Updated time . Time ` xorm:"updated" `
}
2014-03-17 22:03:58 +04:00
// HomeLink returns the user home page link.
2014-03-15 11:28:06 +04:00
func ( user * User ) HomeLink ( ) string {
return "/user/" + user . LowerName
}
2014-03-17 22:03:58 +04:00
// AvatarLink returns the user gravatar link.
2014-03-15 11:28:06 +04:00
func ( user * User ) AvatarLink ( ) string {
return "http://1.gravatar.com/avatar/" + user . Avatar
}
2014-03-21 00:04:56 +04:00
// NewGitSig generates and returns the signature of given user.
func ( user * User ) NewGitSig ( ) * git . Signature {
return & git . Signature {
Name : user . Name ,
Email : user . Email ,
When : time . Now ( ) ,
}
2014-02-14 18:20:57 +04:00
}
2014-03-21 00:04:56 +04:00
// EncodePasswd encodes password to safe format.
func ( user * User ) EncodePasswd ( ) error {
newPasswd , err := scrypt . Key ( [ ] byte ( user . Passwd ) , [ ] byte ( base . SecretKey ) , 16384 , 8 , 1 , 64 )
user . Passwd = fmt . Sprintf ( "%x" , newPasswd )
return err
}
// Member represents user is member of organization.
type Member struct {
Id int64
OrgId int64 ` xorm:"unique(member) index" `
UserId int64 ` xorm:"unique(member)" `
}
2014-02-14 18:20:57 +04:00
2014-02-15 03:16:54 +04:00
// IsUserExist checks if given user name exist,
// the user name should be noncased unique.
2014-02-14 18:20:57 +04:00
func IsUserExist ( name string ) ( bool , error ) {
return orm . Get ( & User { LowerName : strings . ToLower ( name ) } )
}
2014-03-17 22:03:58 +04:00
// IsEmailUsed returns true if the e-mail has been used.
2014-03-06 20:10:35 +04:00
func IsEmailUsed ( email string ) ( bool , error ) {
return orm . Get ( & User { Email : email } )
}
2014-03-19 15:21:23 +04:00
// return a user salt token
func GetUserSalt ( ) string {
return base . GetRandomString ( 10 )
}
2014-02-19 02:31:16 +04:00
// RegisterUser creates record of a new user.
2014-03-19 16:27:27 +04:00
func RegisterUser ( user * User ) ( * User , error ) {
2014-03-20 19:41:24 +04:00
if ! IsLegalName ( user . Name ) {
return nil , ErrUserNameIllegal
}
2014-02-19 02:31:16 +04:00
isExist , err := IsUserExist ( user . Name )
2014-02-15 03:16:54 +04:00
if err != nil {
2014-03-19 16:27:27 +04:00
return nil , err
2014-02-15 03:16:54 +04:00
} else if isExist {
2014-03-19 16:27:27 +04:00
return nil , ErrUserAlreadyExist
2014-02-15 03:16:54 +04:00
}
2014-03-06 20:10:35 +04:00
isExist , err = IsEmailUsed ( user . Email )
if err != nil {
2014-03-19 16:27:27 +04:00
return nil , err
2014-03-06 20:10:35 +04:00
} else if isExist {
2014-03-19 16:27:27 +04:00
return nil , ErrEmailAlreadyUsed
2014-03-06 20:10:35 +04:00
}
2014-02-18 03:38:50 +04:00
user . LowerName = strings . ToLower ( user . Name )
2014-03-08 02:22:15 +04:00
user . Avatar = base . EncodeMd5 ( user . Email )
2014-03-14 09:12:07 +04:00
user . AvatarEmail = user . Email
2014-03-19 15:21:23 +04:00
user . Rands = GetUserSalt ( )
2014-03-06 20:10:35 +04:00
if err = user . EncodePasswd ( ) ; err != nil {
2014-03-19 16:27:27 +04:00
return nil , err
2014-03-17 22:03:58 +04:00
} else if _ , err = orm . Insert ( user ) ; err != nil {
2014-03-19 16:27:27 +04:00
return nil , err
2014-03-17 22:03:58 +04:00
} else if err = os . MkdirAll ( UserPath ( user . Name ) , os . ModePerm ) ; err != nil {
2014-03-06 11:21:44 +04:00
if _ , err := orm . Id ( user . Id ) . Delete ( & User { } ) ; err != nil {
2014-03-19 16:27:27 +04:00
return nil , errors . New ( fmt . Sprintf (
2014-03-17 22:03:58 +04:00
"both create userpath %s and delete table record faild: %v" , user . Name , err ) )
2014-02-20 10:53:56 +04:00
}
2014-03-19 16:27:27 +04:00
return nil , err
2014-03-19 15:21:23 +04:00
}
2014-03-20 16:02:14 +04:00
if user . Id == 1 {
user . IsAdmin = true
user . IsActive = true
_ , err = orm . Id ( user . Id ) . UseBool ( ) . Update ( user )
}
return user , err
2014-02-14 18:20:57 +04:00
}
2014-03-21 00:04:56 +04:00
// GetUsers returns given number of user objects with offset.
func GetUsers ( num , offset int ) ( [ ] User , error ) {
users := make ( [ ] User , 0 , num )
err := orm . Limit ( num , offset ) . Asc ( "id" ) . Find ( & users )
return users , err
}
2014-03-19 20:50:44 +04:00
// get user by erify code
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
}
log . Error ( "user.getVerifyUser: %v" , err )
}
return nil
}
// verify active code when active account
func VerifyUserActiveCode ( code string ) ( user * User ) {
minutes := base . Service . ActiveCodeLives
if user = getVerifyUser ( code ) ; user != nil {
// time limit code
prefix := code [ : base . TimeLimitCodeLength ]
data := base . ToStr ( user . Id ) + user . Email + user . LowerName + user . Passwd + user . Rands
if base . VerifyTimeLimitCode ( data , minutes , prefix ) {
return user
}
}
return nil
}
2014-02-15 03:16:54 +04:00
// UpdateUser updates user's information.
func UpdateUser ( user * User ) ( err error ) {
2014-03-19 20:50:44 +04:00
_ , err = orm . Id ( user . Id ) . UseBool ( ) . Update ( user )
2014-02-14 18:20:57 +04:00
return err
}
2014-02-15 03:16:54 +04:00
// DeleteUser completely deletes everything of the user.
func DeleteUser ( user * User ) error {
2014-03-16 13:24:13 +04:00
// Check ownership of repository.
2014-03-11 19:40:47 +04:00
count , err := GetRepositoryCount ( user )
2014-02-19 22:04:31 +04:00
if err != nil {
return errors . New ( "modesl.GetRepositories: " + err . Error ( ) )
2014-03-11 19:40:47 +04:00
} else if count > 0 {
2014-02-19 22:04:31 +04:00
return ErrUserOwnRepos
}
2014-03-11 10:17:05 +04:00
// TODO: check issues, other repos' commits
2014-03-16 14:38:39 +04:00
// Delete all feeds.
if _ , err = orm . Delete ( & Action { UserId : user . Id } ) ; err != nil {
return err
}
// Delete all SSH keys.
2014-03-16 13:24:13 +04:00
keys := make ( [ ] PublicKey , 0 , 10 )
if err = orm . Find ( & keys , & PublicKey { OwnerId : user . Id } ) ; err != nil {
return err
}
for _ , key := range keys {
if err = DeletePublicKey ( & key ) ; err != nil {
return err
}
}
2014-03-16 18:35:25 +04:00
// Delete user directory.
if err = os . RemoveAll ( UserPath ( user . Name ) ) ; err != nil {
return err
}
2014-02-19 22:04:31 +04:00
_ , err = orm . Delete ( user )
2014-02-15 03:16:54 +04:00
// TODO: delete and update follower information.
return err
}
2014-03-17 22:03:58 +04:00
// UserPath returns the path absolute path of user repositories.
2014-02-20 10:53:56 +04:00
func UserPath ( userName string ) string {
2014-03-21 00:04:56 +04:00
return filepath . Join ( base . RepoRootPath , strings . ToLower ( userName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-19 13:50:53 +04:00
func GetUserByKeyId ( keyId int64 ) ( * User , error ) {
user := new ( User )
2014-03-21 00:04:56 +04:00
rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?"
2014-03-17 22:03:58 +04:00
has , err := orm . Sql ( rawSql , keyId ) . Get ( user )
2014-02-19 13:50:53 +04:00
if err != nil {
return nil , err
2014-03-17 22:03:58 +04:00
} else if ! has {
2014-02-19 13:50:53 +04:00
err = errors . New ( "not exist key owner" )
return nil , err
}
return user , nil
}
2014-03-17 22:03:58 +04:00
// GetUserById returns the user object by given id if exists.
2014-02-25 11:11:54 +04:00
func GetUserById ( id int64 ) ( * User , error ) {
user := new ( User )
has , err := orm . Id ( id ) . Get ( user )
if err != nil {
return nil , err
}
if ! has {
return nil , ErrUserNotExist
}
return user , nil
}
2014-03-17 22:03:58 +04:00
// GetUserByName returns the user object by given name if exists.
2014-03-08 02:08:21 +04:00
func GetUserByName ( name string ) ( * User , error ) {
if len ( name ) == 0 {
return nil , ErrUserNotExist
}
user := & User {
LowerName : strings . ToLower ( name ) ,
}
has , err := orm . Get ( user )
if err != nil {
return nil , err
2014-03-17 22:03:58 +04:00
} else if ! has {
2014-03-08 02:08:21 +04:00
return nil , ErrUserNotExist
}
return user , nil
}
2014-02-15 03:16:54 +04:00
// LoginUserPlain validates user by raw user name and password.
2014-02-14 18:20:57 +04:00
func LoginUserPlain ( name , passwd string ) ( * User , error ) {
2014-03-06 20:42:14 +04:00
user := User { LowerName : strings . ToLower ( name ) , Passwd : passwd }
2014-02-19 02:48:02 +04:00
if err := user . EncodePasswd ( ) ; err != nil {
2014-02-14 18:20:57 +04:00
return nil , err
}
has , err := orm . Get ( & user )
if err != nil {
return nil , err
2014-03-17 22:03:58 +04:00
} else if ! has {
err = ErrUserNotExist
2014-02-14 18:20:57 +04:00
}
2014-03-18 09:33:53 +04:00
return & user , err
2014-02-14 18:20:57 +04:00
}
2014-03-21 00:04:56 +04:00
// Follow is connection request for receiving user notifycation.
type Follow struct {
Id int64
UserId int64 ` xorm:"unique(follow)" `
FollowId int64 ` xorm:"unique(follow)" `
}
2014-02-15 03:16:54 +04:00
// FollowUser marks someone be another's follower.
2014-03-17 22:03:58 +04:00
func FollowUser ( userId int64 , followId int64 ) ( err error ) {
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
defer session . Close ( )
session . Begin ( )
2014-03-17 22:03:58 +04:00
if _ , err = session . Insert ( & Follow { UserId : userId , FollowId : followId } ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-17 22:03:58 +04:00
2014-03-21 00:04:56 +04:00
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
2014-03-17 22:03:58 +04:00
if _ , err = session . Exec ( rawSql , followId ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-17 22:03:58 +04:00
2014-03-21 00:04:56 +04:00
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
2014-03-17 22:03:58 +04:00
if _ , err = session . Exec ( rawSql , userId ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
return session . Commit ( )
}
2014-02-15 03:16:54 +04:00
// UnFollowUser unmarks someone be another's follower.
2014-03-17 22:03:58 +04:00
func UnFollowUser ( userId int64 , unFollowId int64 ) ( err error ) {
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
defer session . Close ( )
session . Begin ( )
2014-03-17 22:03:58 +04:00
if _ , err = session . Delete ( & Follow { UserId : userId , FollowId : unFollowId } ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-17 22:03:58 +04:00
2014-03-21 00:04:56 +04:00
rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?"
2014-03-17 22:03:58 +04:00
if _ , err = session . Exec ( rawSql , unFollowId ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-17 22:03:58 +04:00
2014-03-21 00:04:56 +04:00
rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?"
2014-03-17 22:03:58 +04:00
if _ , err = session . Exec ( rawSql , userId ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
return session . Commit ( )
}