mirror of
https://github.com/photoprism/photoprism.git
synced 2025-10-06 09:19:33 +08:00
151 lines
3.5 KiB
Go
151 lines
3.5 KiB
Go
package entity
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"github.com/photoprism/photoprism/pkg/authn"
|
|
"github.com/photoprism/photoprism/pkg/clean"
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
)
|
|
|
|
// DefaultPasswordCost specifies the cost of the BCrypt Password Hash,
|
|
// see https://github.com/photoprism/photoprism/issues/3718.
|
|
var DefaultPasswordCost = 12
|
|
|
|
// Password represents a password hash.
|
|
type Password struct {
|
|
UID string `gorm:"type:VARBINARY(255);primary_key;" json:"UID"`
|
|
Hash string `deepcopier:"skip" gorm:"type:VARBINARY(255);" json:"Hash"`
|
|
CreatedAt time.Time `deepcopier:"skip" json:"CreatedAt"`
|
|
UpdatedAt time.Time `deepcopier:"skip" json:"UpdatedAt"`
|
|
}
|
|
|
|
// TableName returns the entity table name.
|
|
func (Password) TableName() string {
|
|
return "passwords"
|
|
}
|
|
|
|
// NewPassword creates a new password instance.
|
|
func NewPassword(uid, pw string, allowHash bool) Password {
|
|
if uid == "" {
|
|
panic("auth: cannot set password without uid")
|
|
}
|
|
|
|
m := Password{UID: uid}
|
|
|
|
if pw != "" {
|
|
if err := m.SetPassword(pw, allowHash); err != nil {
|
|
log.Errorf("auth: %s", err)
|
|
}
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
// SetPassword sets a new password stored as hash.
|
|
func (m *Password) SetPassword(pw string, allowHash bool) error {
|
|
// Remove leading and trailing white space.
|
|
pw = clean.Password(pw)
|
|
|
|
// Check if password is too short or too long.
|
|
if len([]rune(pw)) < 1 {
|
|
return authn.ErrPasswordTooShort
|
|
} else if len(pw) > txt.ClipPassword {
|
|
return authn.ErrPasswordTooLong
|
|
}
|
|
|
|
// Check if string already is a bcrypt hash.
|
|
if allowHash {
|
|
if cost, err := bcrypt.Cost([]byte(pw)); err == nil && cost >= bcrypt.MinCost {
|
|
m.Hash = pw
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Generate hash from plain text string using the default password cost.
|
|
if bytes, err := bcrypt.GenerateFromPassword([]byte(pw), DefaultPasswordCost); err != nil {
|
|
return err
|
|
} else {
|
|
m.Hash = string(bytes)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Valid checks if the password is correct.
|
|
func (m *Password) Valid(s string) bool {
|
|
return !m.Invalid(s)
|
|
}
|
|
|
|
// Invalid checks if the specified password is incorrect.
|
|
func (m *Password) Invalid(s string) bool {
|
|
if m.Empty() {
|
|
// Invalid, no password set.
|
|
return true
|
|
} else if s = clean.Password(s); s == "" {
|
|
// Invalid, no password provided.
|
|
return true
|
|
} else if err := bcrypt.CompareHashAndPassword([]byte(m.Hash), []byte(s)); err != nil {
|
|
// Invalid, does not match.
|
|
return true
|
|
}
|
|
|
|
// Not invalid.
|
|
return false
|
|
}
|
|
|
|
// Create inserts a new row to the database.
|
|
func (m *Password) Create() error {
|
|
return Db().Create(m).Error
|
|
}
|
|
|
|
// Save updates the record in the database or inserts a new record if it does not already exist.
|
|
func (m *Password) Save() error {
|
|
return Db().Save(m).Error
|
|
}
|
|
|
|
// Delete removes the password record from the database.
|
|
func (m *Password) Delete() error {
|
|
if m.UID == "" {
|
|
return fmt.Errorf("missing password uid")
|
|
}
|
|
|
|
return Db().Delete(m).Error
|
|
}
|
|
|
|
// FindPassword returns an entity pointer if exists.
|
|
func FindPassword(uid string) *Password {
|
|
result := Password{}
|
|
|
|
if err := Db().Where("uid = ?", uid).First(&result).Error; err == nil {
|
|
return &result
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Cost returns the hashing cost of the currently set password.
|
|
func (m *Password) Cost() (int, error) {
|
|
if m.Empty() {
|
|
return 0, authn.ErrPasswordRequired
|
|
}
|
|
|
|
return bcrypt.Cost([]byte(m.Hash))
|
|
}
|
|
|
|
// Empty checks if a password has not been set yet.
|
|
func (m *Password) Empty() bool {
|
|
return m.Hash == ""
|
|
}
|
|
|
|
// String returns the BCrypt Password Hash.
|
|
func (m *Password) String() string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
|
|
return m.Hash
|
|
}
|