1
Fork 0
photoview/api/graphql/models/user.go

184 lines
4.2 KiB
Go
Raw Normal View History

2020-01-31 17:36:48 +01:00
package models
import (
2020-01-31 18:51:24 +01:00
"crypto/rand"
"fmt"
2020-01-31 23:30:34 +01:00
"log"
2020-02-16 12:22:00 +01:00
"os"
2020-01-31 18:51:24 +01:00
"time"
2020-01-31 17:36:48 +01:00
2020-07-10 18:35:37 +02:00
"github.com/pkg/errors"
2020-01-31 17:36:48 +01:00
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
2020-01-31 17:36:48 +01:00
)
type User struct {
Model
Username string `gorm:"unique;size:128"`
2021-01-19 18:35:19 +01:00
Password *string `gorm:"size:256"`
// RootPath string `gorm:"size:512`
Albums []Album `gorm:"many2many:user_albums"`
Admin bool `gorm:"default:false"`
2020-01-31 18:51:24 +01:00
}
type UserMediaData struct {
ModelTimestamps
UserID int `gorm:"primaryKey;autoIncrement:false"`
MediaID int `gorm:"primaryKey;autoIncrement:false"`
Favorite bool `gorm:"not null;default:false"`
}
2020-01-31 23:30:34 +01:00
2020-01-31 18:51:24 +01:00
type AccessToken struct {
Model
UserID int `gorm:"not null;index"`
User User `gorm:"constraint:OnDelete:CASCADE;"`
2021-01-19 18:35:19 +01:00
Value string `gorm:"not null;size:24;index"`
Expire time.Time `gorm:"not null;index"`
2020-01-31 17:36:48 +01:00
}
type UserPreferences struct {
Model
UserID int `gorm:"not null;index"`
User User `gorm:"constraint:OnDelete:CASCADE;"`
Language *LanguageTranslation
}
func (u *UserPreferences) BeforeSave(tx *gorm.DB) error {
if u.Language != nil && *u.Language == "" {
u.Language = nil
}
if u.Language != nil {
lang_str := string(*u.Language)
found_match := false
for _, lang := range AllLanguageTranslation {
if string(lang) == lang_str {
found_match = true
break
}
}
if !found_match {
return errors.New("invalid language value")
}
}
return nil
}
2020-01-31 23:30:34 +01:00
var ErrorInvalidUserCredentials = errors.New("invalid credentials")
2020-01-31 17:36:48 +01:00
func AuthorizeUser(db *gorm.DB, username string, password string) (*User, error) {
var user User
result := db.Where("username = ?", username).First(&user)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
2020-02-02 00:29:42 +01:00
return nil, ErrorInvalidUserCredentials
}
return nil, errors.Wrap(result.Error, "failed to get user by username when authorizing")
2020-01-31 17:36:48 +01:00
}
2020-02-16 12:22:00 +01:00
if user.Password == nil {
return nil, errors.New("user does not have a password")
}
if err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)); err != nil {
2020-01-31 17:36:48 +01:00
if err == bcrypt.ErrMismatchedHashAndPassword {
2020-01-31 23:30:34 +01:00
return nil, ErrorInvalidUserCredentials
2020-01-31 17:36:48 +01:00
} else {
2020-07-10 18:35:37 +02:00
return nil, errors.Wrap(err, "compare user password hash")
2020-01-31 17:36:48 +01:00
}
}
return &user, nil
2020-01-31 17:36:48 +01:00
}
2020-02-16 12:22:00 +01:00
var ErrorInvalidRootPath = errors.New("invalid root path")
func ValidRootPath(rootPath string) bool {
_, err := os.Stat(rootPath)
2020-01-31 17:36:48 +01:00
if err != nil {
2020-02-16 12:22:00 +01:00
log.Printf("Warn: invalid root path: '%s'\n%s\n", rootPath, err)
return false
2020-01-31 17:36:48 +01:00
}
2020-02-16 12:22:00 +01:00
return true
}
func RegisterUser(db *gorm.DB, username string, password *string, admin bool) (*User, error) {
user := User{
Username: username,
Admin: admin,
}
2020-02-16 12:22:00 +01:00
if password != nil {
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
if err != nil {
2020-07-10 18:35:37 +02:00
return nil, errors.Wrap(err, "failed to hash password")
2020-02-16 12:22:00 +01:00
}
hashedPass := string(hashedPassBytes)
user.Password = &hashedPass
2020-01-31 17:36:48 +01:00
}
result := db.Create(&user)
if result.Error != nil {
return nil, errors.Wrap(result.Error, "insert new user with password into database")
2020-01-31 17:36:48 +01:00
}
return &user, nil
2020-01-31 17:36:48 +01:00
}
2020-01-31 18:51:24 +01:00
func (user *User) GenerateAccessToken(db *gorm.DB) (*AccessToken, error) {
2020-01-31 18:51:24 +01:00
bytes := make([]byte, 24)
if _, err := rand.Read(bytes); err != nil {
return nil, errors.New(fmt.Sprintf("Could not generate token: %s\n", err.Error()))
}
2020-02-02 00:29:42 +01:00
const CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2020-01-31 18:51:24 +01:00
for i, b := range bytes {
bytes[i] = CHARACTERS[b%byte(len(CHARACTERS))]
}
token_value := string(bytes)
expire := time.Now().Add(14 * 24 * time.Hour)
token := AccessToken{
UserID: user.ID,
2020-01-31 18:51:24 +01:00
Value: token_value,
Expire: expire,
}
result := db.Create(&token)
if result.Error != nil {
return nil, errors.Wrap(result.Error, "saving access token to database")
}
2020-01-31 18:51:24 +01:00
return &token, nil
}
2020-01-31 23:30:34 +01:00
2020-12-22 01:14:43 +01:00
// FillAlbums fill user.Albums with albums from database
func (user *User) FillAlbums(db *gorm.DB) error {
// Albums already present
if len(user.Albums) > 0 {
return nil
}
if err := db.Model(&user).Association("Albums").Find(&user.Albums); err != nil {
return errors.Wrap(err, "fill user albums")
}
return nil
}
func (user *User) OwnsAlbum(db *gorm.DB, album *Album) (bool, error) {
// TODO: Implement this
panic("not implemented")
2020-12-22 01:14:43 +01:00
}
func (user *User) OwnsMedia(db *gorm.DB, media *Media) (bool, error) {
// TODO: implement this
panic("not implemented")
2020-12-22 01:14:43 +01:00
}