Auth: Add unique index to user_slug in auth_users table #98
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
85561547cc
commit
5e7ff6b1b2
10 changed files with 31 additions and 35 deletions
|
@ -14,7 +14,6 @@ import (
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/pkg/clean"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResetCommand resets the index, clears the cache, and removes sidecar files after confirmation.
|
// ResetCommand resets the index, clears the cache, and removes sidecar files after confirmation.
|
||||||
|
@ -181,8 +180,8 @@ func resetIndexDb(c *config.Config) {
|
||||||
// Reset admin account?
|
// Reset admin account?
|
||||||
if c.AdminPassword() == "" {
|
if c.AdminPassword() == "" {
|
||||||
log.Warnf("password required to reset admin account")
|
log.Warnf("password required to reset admin account")
|
||||||
} else if entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword()) {
|
} else {
|
||||||
log.Infof("user %s has been restored", clean.LogQuote(c.AdminUser()))
|
entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword())
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("database reset completed in %s", time.Since(start))
|
log.Infof("database reset completed in %s", time.Since(start))
|
||||||
|
|
|
@ -275,8 +275,8 @@ func (c *Config) MigrateDb(runFailed bool, ids []string) {
|
||||||
// Init admin account?
|
// Init admin account?
|
||||||
if c.AdminPassword() == "" {
|
if c.AdminPassword() == "" {
|
||||||
log.Warnf("config: password required to initialize %s account", clean.LogQuote(c.AdminUser()))
|
log.Warnf("config: password required to initialize %s account", clean.LogQuote(c.AdminUser()))
|
||||||
} else if entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword()) {
|
} else {
|
||||||
log.Infof("config: %s account has been initialized", clean.LogQuote(c.AdminUser()))
|
entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword())
|
||||||
}
|
}
|
||||||
|
|
||||||
go entity.SaveErrorMessages()
|
go entity.SaveErrorMessages()
|
||||||
|
@ -290,8 +290,8 @@ func (c *Config) InitTestDb() {
|
||||||
|
|
||||||
if c.AdminPassword() == "" {
|
if c.AdminPassword() == "" {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
} else if entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword()) {
|
} else {
|
||||||
log.Debugf("config: %s account has been initialized", clean.LogQuote(c.AdminUser()))
|
entity.Admin.InitAccount(c.AdminUser(), c.AdminPassword())
|
||||||
}
|
}
|
||||||
|
|
||||||
go entity.SaveErrorMessages()
|
go entity.SaveErrorMessages()
|
||||||
|
|
|
@ -25,7 +25,7 @@ type Users []User
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `gorm:"primary_key" json:"-" yaml:"-"`
|
ID int `gorm:"primary_key" json:"-" yaml:"-"`
|
||||||
UserUID string `gorm:"type:VARBINARY(42);unique_index;" json:"UID" yaml:"UID"`
|
UserUID string `gorm:"type:VARBINARY(42);unique_index;" json:"UID" yaml:"UID"`
|
||||||
UserSlug string `gorm:"type:VARBINARY(160);index;" json:"Slug" yaml:"Slug,omitempty"`
|
UserSlug string `gorm:"type:VARBINARY(160);unique_index;" json:"Slug" yaml:"Slug,omitempty"`
|
||||||
Username string `gorm:"size:64;index;" json:"Username" yaml:"Username,omitempty"`
|
Username string `gorm:"size:64;index;" json:"Username" yaml:"Username,omitempty"`
|
||||||
Email string `gorm:"size:255;index;" json:"Email" yaml:"Email,omitempty"`
|
Email string `gorm:"size:255;index;" json:"Email" yaml:"Email,omitempty"`
|
||||||
UserRole string `gorm:"size:32;" json:"Role" yaml:"Role,omitempty"`
|
UserRole string `gorm:"size:32;" json:"Role" yaml:"Role,omitempty"`
|
||||||
|
@ -102,12 +102,6 @@ func (m *User) InitAccount(login, password string) (updated bool) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update username as well if needed.
|
|
||||||
if err := m.UpdateName(login); err != nil {
|
|
||||||
log.Errorf("user: %s", err.Error())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
existing := FindPassword(m.UserUID)
|
existing := FindPassword(m.UserUID)
|
||||||
|
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
|
@ -116,12 +110,17 @@ func (m *User) InitAccount(login, password string) (updated bool) {
|
||||||
|
|
||||||
pw := NewPassword(m.UserUID, password)
|
pw := NewPassword(m.UserUID, password)
|
||||||
|
|
||||||
// Update password in database.
|
// Save password.
|
||||||
if err := pw.Save(); err != nil {
|
if err := pw.Save(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change username.
|
||||||
|
if err := m.UpdateName(login); err != nil {
|
||||||
|
log.Debugf("auth: cannot change username of %s to %s (%s)", clean.Log(m.UserUID), clean.LogQuote(login), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +173,7 @@ func FirstOrCreateUser(m *User) *User {
|
||||||
|
|
||||||
m.UserSlug = m.GenerateSlug()
|
m.UserSlug = m.GenerateSlug()
|
||||||
|
|
||||||
if err := Db().Where("id = ? OR user_uid = ?", m.ID, m.UserUID).First(&result).Error; err == nil {
|
if err := Db().Where("id = ? OR (user_uid = ? AND user_uid <> '') OR (user_slug = ? AND user_slug <> '') OR (username = ? AND username <> '')", m.ID, m.UserUID, m.UserSlug, m.Username).First(&result).Error; err == nil {
|
||||||
return &result
|
return &result
|
||||||
} else if err := m.Create(); err != nil {
|
} else if err := m.Create(); err != nil {
|
||||||
log.Debugf("user: %s", err)
|
log.Debugf("user: %s", err)
|
||||||
|
@ -307,7 +306,7 @@ func (m *User) SetUsername(login string) (err error) {
|
||||||
m.UserSlug = m.GenerateSlug()
|
m.UserSlug = m.GenerateSlug()
|
||||||
|
|
||||||
// Update display name.
|
// Update display name.
|
||||||
if m.DisplayName == "" || m.DisplayName == DefaultAdminFullName {
|
if m.DisplayName == "" || m.DisplayName == AdminDisplayName {
|
||||||
m.DisplayName = clean.Name(login)
|
m.DisplayName = clean.Name(login)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,19 +5,17 @@ import (
|
||||||
"github.com/photoprism/photoprism/pkg/rnd"
|
"github.com/photoprism/photoprism/pkg/rnd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultAdminUserName = "admin"
|
const AdminUserName = "admin"
|
||||||
const DefaultAdminFullName = "Admin"
|
const AdminDisplayName = "Admin"
|
||||||
|
const GuestDisplayName = "Guest"
|
||||||
const DisplayNameUnknown = "Public"
|
|
||||||
const DisplayNameGuest = "Guest"
|
|
||||||
|
|
||||||
// Admin is the default admin user.
|
// Admin is the default admin user.
|
||||||
var Admin = User{
|
var Admin = User{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
UserSlug: "admin",
|
UserSlug: "admin",
|
||||||
Username: DefaultAdminUserName,
|
Username: AdminUserName,
|
||||||
UserRole: acl.RoleAdmin.String(),
|
UserRole: acl.RoleAdmin.String(),
|
||||||
DisplayName: DefaultAdminFullName,
|
DisplayName: AdminDisplayName,
|
||||||
SuperAdmin: true,
|
SuperAdmin: true,
|
||||||
CanLogin: true,
|
CanLogin: true,
|
||||||
CanInvite: true,
|
CanInvite: true,
|
||||||
|
@ -27,11 +25,11 @@ var Admin = User{
|
||||||
// UnknownUser is an anonymous, public user without own account.
|
// UnknownUser is an anonymous, public user without own account.
|
||||||
var UnknownUser = User{
|
var UnknownUser = User{
|
||||||
ID: -1,
|
ID: -1,
|
||||||
UserSlug: "1",
|
UserSlug: "",
|
||||||
UserUID: "u000000000000001",
|
UserUID: "u000000000000001",
|
||||||
UserRole: "",
|
UserRole: "",
|
||||||
Username: "",
|
Username: "",
|
||||||
DisplayName: DisplayNameUnknown,
|
DisplayName: "",
|
||||||
SuperAdmin: false,
|
SuperAdmin: false,
|
||||||
CanLogin: false,
|
CanLogin: false,
|
||||||
CanInvite: false,
|
CanInvite: false,
|
||||||
|
@ -41,11 +39,11 @@ var UnknownUser = User{
|
||||||
// Guest is a user without own account e.g. for link sharing.
|
// Guest is a user without own account e.g. for link sharing.
|
||||||
var Guest = User{
|
var Guest = User{
|
||||||
ID: -2,
|
ID: -2,
|
||||||
UserSlug: "2",
|
UserSlug: "guest",
|
||||||
UserUID: "u000000000000002",
|
UserUID: "u000000000000002",
|
||||||
UserRole: acl.RoleGuest.String(),
|
UserRole: acl.RoleGuest.String(),
|
||||||
Username: "",
|
Username: "guest",
|
||||||
DisplayName: DisplayNameGuest,
|
DisplayName: GuestDisplayName,
|
||||||
SuperAdmin: false,
|
SuperAdmin: false,
|
||||||
CanLogin: false,
|
CanLogin: false,
|
||||||
CanInvite: false,
|
CanInvite: false,
|
||||||
|
|
|
@ -232,7 +232,7 @@ func TestFindUserByUID(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, -2, m.ID)
|
assert.Equal(t, -2, m.ID)
|
||||||
assert.NotEmpty(t, m.UserUID)
|
assert.NotEmpty(t, m.UserUID)
|
||||||
assert.Equal(t, "", m.Username)
|
assert.Equal(t, "guest", m.Username)
|
||||||
assert.Equal(t, "Guest", m.DisplayName)
|
assert.Equal(t, "Guest", m.DisplayName)
|
||||||
assert.NotEmpty(t, m.CreatedAt)
|
assert.NotEmpty(t, m.CreatedAt)
|
||||||
assert.NotEmpty(t, m.UpdatedAt)
|
assert.NotEmpty(t, m.UpdatedAt)
|
||||||
|
|
|
@ -101,6 +101,6 @@ var DialectMySQL = Migrations{
|
||||||
{
|
{
|
||||||
ID: "20220901-000100",
|
ID: "20220901-000100",
|
||||||
Dialect: "mysql",
|
Dialect: "mysql",
|
||||||
Statements: []string{"INSERT IGNORE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, role_admin, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND role_admin = 1 AND user_disabled = 0;"},
|
Statements: []string{"REPLACE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, role_admin, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE role_admin = 1 AND user_name NOT IN (SELECT user_slug FROM auth_users UNION SELECT user_slug FROM auth_users) AND user_name <> '' AND user_name IS NOT NULL;"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,6 @@ var DialectSQLite3 = Migrations{
|
||||||
{
|
{
|
||||||
ID: "20220901-000100",
|
ID: "20220901-000100",
|
||||||
Dialect: "sqlite3",
|
Dialect: "sqlite3",
|
||||||
Statements: []string{"INSERT OR IGNORE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, 1, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND user_uid <> '' AND user_uid IS NOT NULL AND role_admin = 1 AND user_disabled = 0 ON CONFLICT DO NOTHING;"},
|
Statements: []string{"REPLACE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, 1, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND user_uid <> '' AND user_uid IS NOT NULL AND role_admin = 1 AND user_disabled = 0;"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (m *Migration) Execute(db *gorm.DB) error {
|
||||||
} else if strings.HasPrefix(q, "DROP TABLE ") &&
|
} else if strings.HasPrefix(q, "DROP TABLE ") &&
|
||||||
strings.Contains(e, "DROP") {
|
strings.Contains(e, "DROP") {
|
||||||
log.Tracef("migrate: %s (ignored, probably didn't exist anymore)", err)
|
log.Tracef("migrate: %s (ignored, probably didn't exist anymore)", err)
|
||||||
} else if strings.Contains(q, " IGNORE ") &&
|
} else if (strings.Contains(q, " IGNORE ") || strings.Contains(q, "REPLACE")) &&
|
||||||
(strings.Contains(e, "NO SUCH TABLE") || strings.Contains(e, "DOESN'T EXIST")) {
|
(strings.Contains(e, "NO SUCH TABLE") || strings.Contains(e, "DOESN'T EXIST")) {
|
||||||
log.Tracef("migrate: %s (ignored, old table does not exist anymore)", err)
|
log.Tracef("migrate: %s (ignored, old table does not exist anymore)", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
INSERT IGNORE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, role_admin, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND role_admin = 1 AND user_disabled = 0;
|
REPLACE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, role_admin, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE role_admin = 1 AND user_name NOT IN (SELECT user_slug FROM auth_users UNION SELECT user_slug FROM auth_users) AND user_name <> '' AND user_name IS NOT NULL;
|
|
@ -1 +1 @@
|
||||||
INSERT OR IGNORE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, 1, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND user_uid <> '' AND user_uid IS NOT NULL AND role_admin = 1 AND user_disabled = 0 ON CONFLICT DO NOTHING;
|
REPLACE INTO auth_users (id, user_uid, super_admin, user_role, display_name, user_slug, username, email, login_attempts, login_at, created_at, updated_at) SELECT id, user_uid, 1, 'admin', full_name, user_name, user_name, primary_email, login_attempts, login_at, created_at, updated_at FROM users WHERE user_name <> '' AND user_name IS NOT NULL AND user_uid <> '' AND user_uid IS NOT NULL AND role_admin = 1 AND user_disabled = 0;
|
Loading…
Reference in a new issue