User model/migrations and gorm init

pull/247/head
HFO4 5 years ago
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
###

@ -3,11 +3,13 @@ module Cloudreve
go 1.12
require (
github.com/DATA-DOG/go-sqlmock v1.3.3
github.com/gin-gonic/gin v1.4.0
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/jinzhu/gorm v1.9.11
github.com/leodido/go-urn v1.2.0 // indirect
github.com/pkg/errors v0.8.0
github.com/stretchr/testify v1.4.0
gopkg.in/go-playground/validator.v8 v8.18.2
gopkg.in/go-playground/validator.v9 v9.30.0

@ -1,9 +1,14 @@
package main
import (
"Cloudreve/models"
"Cloudreve/routers"
)
func init() {
model.Init()
}
func main() {
api := routers.InitRouter()

@ -13,8 +13,10 @@ import (
var DB *gorm.DB
// Database 在中间件中初始化mysql链接
func Database(connString string) {
db, err := gorm.Open("mysql", connString)
func Init() {
//TODO 从配置文件中读取 包括DEBUG模式
util.Log().Info("初始化数据库连接\n")
db, err := gorm.Open("mysql", "root:root@(localhost)/v3?charset=utf8&parseTime=True&loc=Local")
db.LogMode(true)
// Error
if err != nil {

@ -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)
}

@ -9,6 +9,7 @@ import (
"testing"
)
// 测试 ErrorResponse
func TestErrorResponse(t *testing.T) {
asserts := assert.New(t)

Loading…
Cancel
Save