parent
ece1bca11a
commit
aca8bde521
@ -1,18 +0,0 @@
|
||||
POST http://127.0.0.1:5000/Api/V3/User/Login
|
||||
Accept: */*
|
||||
Cache-Control: no-cache
|
||||
|
||||
{}
|
||||
|
||||
<> 2019-11-05T074354.200.json
|
||||
|
||||
###
|
||||
|
||||
POST http://127.0.0.1:5000/Api/V3/User/Login
|
||||
Accept: */*
|
||||
Cache-Control: no-cache
|
||||
|
||||
<> 2019-11-05T074329.200.json
|
||||
|
||||
###
|
||||
|
@ -1,8 +1,32 @@
|
||||
package model
|
||||
|
||||
//执行数据迁移
|
||||
import (
|
||||
"Cloudreve/pkg/util"
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
//执行数据迁移
|
||||
func migration() {
|
||||
// 自动迁移模式
|
||||
DB.AutoMigrate()
|
||||
DB.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
|
||||
|
||||
// 添加初始用户
|
||||
_, err := GetUser(1)
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
defaultUser := NewUser()
|
||||
//TODO 动态生成密码
|
||||
defaultUser.Email = "admin@cloudreve.org"
|
||||
defaultUser.Nick = "admin"
|
||||
defaultUser.Status = Active
|
||||
defaultUser.Group = 1
|
||||
defaultUser.PrimaryGroup = 1
|
||||
err := defaultUser.SetPassword("admin")
|
||||
if err != nil {
|
||||
util.Log().Panic("无法创建密码, ", err)
|
||||
}
|
||||
if err := DB.Create(&defaultUser).Error; err != nil {
|
||||
util.Log().Panic("无法创建初始用户, ", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,100 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"Cloudreve/pkg/serializer"
|
||||
"Cloudreve/pkg/util"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/pkg/errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Active 账户正常状态
|
||||
Active = iota
|
||||
// NotActivicated 未激活
|
||||
NotActivicated
|
||||
// Baned 被封禁
|
||||
Baned
|
||||
)
|
||||
|
||||
// User 用户模型
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Email string `gorm:"type:varchar(100);unique_index"`
|
||||
Nick string `gorm:"size:50"`
|
||||
Password string
|
||||
Status int
|
||||
Group int
|
||||
PrimaryGroup int
|
||||
ActivationKey string
|
||||
Storage int64
|
||||
LastNotify *time.Time
|
||||
OpenID string
|
||||
TwoFactor string
|
||||
Delay int
|
||||
Avatar string
|
||||
Options string `gorm:"size:4096"`
|
||||
}
|
||||
|
||||
// GetUser 用ID获取用户
|
||||
func GetUser(ID interface{}) (User, error) {
|
||||
var user User
|
||||
result := DB.First(&user, ID)
|
||||
return user, result.Error
|
||||
}
|
||||
|
||||
// NewUser 返回一个新的空 User
|
||||
func NewUser() User {
|
||||
options := serializer.UserOption{
|
||||
ProfileOn: 1,
|
||||
}
|
||||
optionsValue, _ := json.Marshal(&options)
|
||||
return User{
|
||||
Avatar: "default",
|
||||
Options: string(optionsValue),
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPassword 根据明文校验密码
|
||||
func (user *User) CheckPassword(password string) (bool, error) {
|
||||
|
||||
// 根据存储密码拆分为 Salt 和 Digest
|
||||
passwordStore := strings.Split(user.Password, ":")
|
||||
if len(passwordStore) != 2 {
|
||||
return false, errors.New("Unknown password type")
|
||||
}
|
||||
|
||||
// todo 兼容V2/V1密码
|
||||
//计算 Salt 和密码组合的SHA1摘要
|
||||
hash := sha1.New()
|
||||
_, err := hash.Write([]byte(password + passwordStore[0]))
|
||||
bs := hex.EncodeToString(hash.Sum(nil))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return bs == passwordStore[1], nil
|
||||
}
|
||||
|
||||
// SetPassword 根据给定明文设定 User 的 Password 字段
|
||||
func (user *User) SetPassword(password string) error {
|
||||
//生成16位 Salt
|
||||
salt := util.RandStringRunes(16)
|
||||
|
||||
//计算 Salt 和密码组合的SHA1摘要
|
||||
hash := sha1.New()
|
||||
_, err := hash.Write([]byte(password + salt))
|
||||
bs := hex.EncodeToString(hash.Sum(nil))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//存储 Salt 值和摘要, ":"分割
|
||||
user.Password = salt + ":" + string(bs)
|
||||
return nil
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
// 准备数据库 Mock
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
DB, _ = gorm.Open("mysql", db)
|
||||
defer db.Close()
|
||||
|
||||
//找到用户时
|
||||
rows := sqlmock.NewRows([]string{"id", "deleted_at", "email"}).
|
||||
AddRow(1, nil, "admin@cloudreve.org")
|
||||
|
||||
mock.ExpectQuery("^SELECT (.+)").WillReturnRows(rows)
|
||||
|
||||
user, err := GetUser(1)
|
||||
asserts.NoError(err)
|
||||
asserts.Equal(User{
|
||||
Model: gorm.Model{
|
||||
ID: 1,
|
||||
DeletedAt: nil,
|
||||
},
|
||||
Email: "admin@cloudreve.org",
|
||||
}, user)
|
||||
|
||||
//未找到用户时
|
||||
mock.ExpectQuery("^SELECT (.+)").WillReturnError(errors.New("not found"))
|
||||
user, err = GetUser(1)
|
||||
asserts.Error(err)
|
||||
asserts.Equal(User{}, user)
|
||||
}
|
||||
|
||||
func TestUser_SetPassword(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
user := User{}
|
||||
err := user.SetPassword("Cause Sega does what nintendon't")
|
||||
asserts.NoError(err)
|
||||
asserts.NotEmpty(user.Password)
|
||||
}
|
||||
|
||||
func TestUser_CheckPassword(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
user := User{}
|
||||
err := user.SetPassword("Cause Sega does what nintendon't")
|
||||
asserts.NoError(err)
|
||||
|
||||
//密码正确
|
||||
res, err := user.CheckPassword("Cause Sega does what nintendon't")
|
||||
asserts.NoError(err)
|
||||
asserts.True(res)
|
||||
|
||||
//密码错误
|
||||
res, err = user.CheckPassword("Cause Sega does what Nintendon't")
|
||||
asserts.NoError(err)
|
||||
asserts.False(res)
|
||||
|
||||
//密码字段为空
|
||||
user = User{}
|
||||
res, err = user.CheckPassword("Cause Sega does what nintendon't")
|
||||
asserts.Error(err)
|
||||
asserts.False(res)
|
||||
|
||||
}
|
||||
|
||||
func TestNewUser(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
newUser := NewUser()
|
||||
asserts.IsType(User{}, newUser)
|
||||
asserts.NotEmpty(newUser.Avatar)
|
||||
asserts.NotEmpty(newUser.Options)
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package serializer
|
||||
|
||||
// UserOption 用户个性化配置字段
|
||||
type UserOption struct {
|
||||
ProfileOn int `json:"profile_on"`
|
||||
WebDAVKey string `json:"webdav_key"`
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RandStringRunes 返回随机字符串
|
||||
func RandStringRunes(n int) string {
|
||||
var letterRunes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
Loading…
Reference in new issue