Auth: Add user entity functions and tests #98
This commit is contained in:
parent
35869c6620
commit
19e9c7560e
2 changed files with 190 additions and 5 deletions
|
@ -1,6 +1,7 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
@ -137,12 +138,12 @@ func (m *User) Save() error {
|
|||
}
|
||||
|
||||
// BeforeCreate creates a random UID if needed before inserting a new row to the database.
|
||||
func (m *User) BeforeCreate(scope *gorm.Scope) error {
|
||||
func (m *User) BeforeCreate(tx *gorm.DB) error {
|
||||
if rnd.IsUID(m.UserUID, 'u') {
|
||||
return nil
|
||||
}
|
||||
|
||||
return scope.SetColumn("UserUID", rnd.PPID('u'))
|
||||
m.UserUID = rnd.PPID('u')
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstOrCreateUser returns an existing row, inserts a new row or nil in case of errors.
|
||||
|
@ -191,6 +192,21 @@ func FindUserByUID(uid string) *User {
|
|||
}
|
||||
}
|
||||
|
||||
// DeleteUserByName deletes an existing user or returns error if not found.
|
||||
func DeleteUserByName(userName string) error {
|
||||
if userName == "" {
|
||||
return fmt.Errorf("can not delete user from db: empty username")
|
||||
}
|
||||
user := FindUserByName(userName)
|
||||
if err := Db().Where("user_name = ?", userName).Delete(&User{}).Error; user == nil || err != nil {
|
||||
return fmt.Errorf("user %s not found", txt.Quote(userName))
|
||||
}
|
||||
if err := Db().Where("uid = ?", user.UserUID).Delete(&Password{}).Error; err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns an identifier that can be used in logs.
|
||||
func (m *User) String() string {
|
||||
if m.UserName != "" {
|
||||
|
@ -321,3 +337,58 @@ func (m *User) Role() acl.Role {
|
|||
|
||||
return acl.RoleDefault
|
||||
}
|
||||
|
||||
// Validate Makes sure username and email are unique and meet requirements. Returns error if any property is invalid
|
||||
func (m *User) Validate() error {
|
||||
if m.UserName == "" {
|
||||
return errors.New("username must not be empty")
|
||||
}
|
||||
if len(m.UserName) < 4 {
|
||||
return errors.New("username must be at least 4 characters")
|
||||
}
|
||||
var result = &User{}
|
||||
var err error
|
||||
if err = Db().Unscoped().Where("user_name = ?", m.UserName).First(result).Error; err == nil {
|
||||
return errors.New("username already exists")
|
||||
} else if err != gorm.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
// stop here if no email is provided
|
||||
if m.PrimaryEmail == "" {
|
||||
return nil
|
||||
}
|
||||
if err = Db().Unscoped().Where("primary_email = ?", m.PrimaryEmail).First(result).Error; err == nil {
|
||||
return errors.New("email already exists")
|
||||
} else if err != gorm.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateWithPassword Creates User with Password in db transaction.
|
||||
func (m *User) CreateWithPassword(password string) error {
|
||||
if len(password) < 4 {
|
||||
return fmt.Errorf("new password for %s must be at least 4 characters", txt.Quote(m.UserName))
|
||||
}
|
||||
return Db().Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(m).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
pw := NewPassword(m.UserUID, password)
|
||||
if err := tx.Create(&pw).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("created user %v with uid %v", txt.Quote(m.UserName), txt.Quote(m.UserUID))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// AllUsers Returns a list of all registered Users.
|
||||
func AllUsers() []User {
|
||||
var users []User
|
||||
if err := Db().Find(&users).Error; err != nil {
|
||||
log.Error(err)
|
||||
return []User{}
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package entity
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -376,3 +375,118 @@ func TestUser_Role(t *testing.T) {
|
|||
assert.Equal(t, acl.Role("*"), p.Role())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteUserByName(t *testing.T) {
|
||||
u := FirstOrCreateUser(&User{
|
||||
ID: 877,
|
||||
AddressID: 1,
|
||||
UserName: "delete",
|
||||
FullName: "Delete",
|
||||
PrimaryEmail: "delete@example.com",
|
||||
})
|
||||
|
||||
t.Run("delete empty username", func(t *testing.T) {
|
||||
err := DeleteUserByName("")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("delete fail", func(t *testing.T) {
|
||||
err := DeleteUserByName("notmatching")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("delete success", func(t *testing.T) {
|
||||
err := DeleteUserByName(u.UserName)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUser_Validate(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
u := &User{
|
||||
ID: 878,
|
||||
AddressID: 1,
|
||||
UserName: "validate",
|
||||
FullName: "Validate",
|
||||
PrimaryEmail: "validate@example.com",
|
||||
}
|
||||
err := u.Validate()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("username empty", func(t *testing.T) {
|
||||
u := &User{
|
||||
ID: 878,
|
||||
AddressID: 1,
|
||||
UserName: "",
|
||||
FullName: "Validate",
|
||||
PrimaryEmail: "validate@example.com",
|
||||
}
|
||||
err := u.Validate()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("username too short", func(t *testing.T) {
|
||||
u := &User{
|
||||
ID: 878,
|
||||
AddressID: 1,
|
||||
UserName: "val",
|
||||
FullName: "Validate",
|
||||
PrimaryEmail: "validate@example.com",
|
||||
}
|
||||
err := u.Validate()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("username not unique", func(t *testing.T) {
|
||||
u := FirstOrCreateUser(&User{
|
||||
ID: 879,
|
||||
AddressID: 1,
|
||||
UserName: "notunique",
|
||||
})
|
||||
err := u.Validate()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("email not unique", func(t *testing.T) {
|
||||
u := FirstOrCreateUser(&User{
|
||||
ID: 880,
|
||||
AddressID: 1,
|
||||
PrimaryEmail: "notunique@example.com",
|
||||
})
|
||||
err := u.Validate()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCreateWithPassword(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
u := &User{
|
||||
ID: 881,
|
||||
AddressID: 1,
|
||||
UserName: "thomas",
|
||||
FullName: "Thomas",
|
||||
PrimaryEmail: "thomas@example.com",
|
||||
}
|
||||
err := u.CreateWithPassword("helloworld")
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
t.Run("password too short", func(t *testing.T) {
|
||||
u := &User{
|
||||
ID: 882,
|
||||
AddressID: 1,
|
||||
UserName: "thomas",
|
||||
FullName: "Thomas",
|
||||
PrimaryEmail: "thomas@example.com",
|
||||
}
|
||||
err := u.CreateWithPassword("hel")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAllUsers(t *testing.T) {
|
||||
t.Run("list all", func(t *testing.T) {
|
||||
users := AllUsers()
|
||||
for _, user := range users {
|
||||
log.Infof("user: %v, %s, %s, %s", user.ID, user.UserUID, user.UserName, user.FullName)
|
||||
}
|
||||
log.Infof("user count: %v", len(users))
|
||||
assert.Greater(t, len(users), 3)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue