Auth: Refactor user management commands #98

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2023-02-14 20:28:47 +01:00
parent b7b811b125
commit f54512e334
6 changed files with 85 additions and 10 deletions

View file

@ -16,12 +16,17 @@ debug = true
[backend] [backend]
datastore = "config" datastore = "config"
baseDN = "dc=localssl,dc=dev" baseDN = "dc=localssl,dc=dev"
[[users]] [[users]]
name = "user" name = "user"
givenname = "John" givenname = "John"
objectClass = "user" objectClass = "user"
displayName = "John Doe" displayName = "John Doe"
sn = "Doe" sn = "Doe"
uidnumber = 5003
primarygroup = 5501
loginShell = "/bin/sh"
otherGroups = [5505,5506,5507]
userPrincipalName = "jdoe@example.com" userPrincipalName = "jdoe@example.com"
mail = "jdoe@example.com" mail = "jdoe@example.com"
passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism
@ -32,6 +37,7 @@ debug = true
[[users.capabilities]] [[users.capabilities]]
action = "search" action = "search"
object = "*" object = "*"
[[users]] [[users]]
name = "bob" name = "bob"
givenname = "Bob" givenname = "Bob"
@ -40,6 +46,10 @@ debug = true
sn = "Jones" sn = "Jones"
userPrincipalName = "bob@example.com" userPrincipalName = "bob@example.com"
mail = "bob@example.com" mail = "bob@example.com"
uidnumber = 5005
primarygroup = 5502
loginShell = "/bin/bash"
otherGroups = [5505,5506]
passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism
[[users.customattributes]] [[users.customattributes]]
photoprismRoleUser = ["true"] photoprismRoleUser = ["true"]
@ -49,6 +59,7 @@ debug = true
[[users.capabilities]] [[users.capabilities]]
action = "search" action = "search"
object = "*" object = "*"
[[users]] [[users]]
name = "guest" name = "guest"
objectClass = "user" objectClass = "user"
@ -56,9 +67,41 @@ debug = true
displayName = "Guest User" displayName = "Guest User"
userPrincipalName = "guest@example.com" userPrincipalName = "guest@example.com"
mail = "guest@example.com" mail = "guest@example.com"
uidnumber = 5006
primarygroup = 5503
loginShell = "/bin/sh"
otherGroups = [5505,5507]
passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism passsha256 = "4314c1fe282face45336b1422a3285c5ff31a39c8e24425615fa53a43b718493" # photoprism
[[users.customattributes]] [[users.customattributes]]
photoprismRole = ["guest"] photoprismRole = ["guest"]
[[users.capabilities]] [[users.capabilities]]
action = "search" action = "search"
object = "*" object = "*"
[[groups]]
name = "PhotoPrism-admin"
gidnumber = 5501
[[groups]]
name = "PhotoPrism-user"
gidnumber = 5502
[[groups]]
name = "PhotoPrism-guest"
gidnumber = 5503
[[groups]]
name = "PhotoPrism-visitor"
gidnumber = 5504
[[groups]]
name = "gmail"
gidnumber = 5505
[[groups]]
name = "email"
gidnumber = 5506
[[groups]]
name = "ssh"
gidnumber = 5507

View file

@ -51,6 +51,7 @@ services:
PHOTOPRISM_LDAP_BIND: "simple" PHOTOPRISM_LDAP_BIND: "simple"
PHOTOPRISM_LDAP_BIND_DN: "cn" PHOTOPRISM_LDAP_BIND_DN: "cn"
PHOTOPRISM_LDAP_BASE_DN: "dc=localssl,dc=dev" PHOTOPRISM_LDAP_BASE_DN: "dc=localssl,dc=dev"
PHOTOPRISM_LDAP_ROLE_DN: "ou=photoprism-*,ou=groups,dc=localssl,dc=dev"
PHOTOPRISM_LDAP_SYNC: "true" PHOTOPRISM_LDAP_SYNC: "true"
## OpenID Connect (pre-configured for local tests): ## OpenID Connect (pre-configured for local tests):
PHOTOPRISM_OIDC_URI: "https://keycloak.localssl.dev/auth/realms/master" PHOTOPRISM_OIDC_URI: "https://keycloak.localssl.dev/auth/realms/master"

View file

@ -23,7 +23,7 @@ var UsersLegacyCommand = cli.Command{
// usersLegacyAction displays legacy user accounts. // usersLegacyAction displays legacy user accounts.
func usersLegacyAction(ctx *cli.Context) error { func usersLegacyAction(ctx *cli.Context) error {
return CallWithDependencies(ctx, func(conf *config.Config) error { return CallWithDependencies(ctx, func(conf *config.Config) error {
cols := []string{"ID", "UID", "User Name", "Display Name", "Email", "Admin", "Created At"} cols := []string{"ID", "UID", "Name", "User", "Email", "Admin", "Created At"}
// Fetch users from database. // Fetch users from database.
users := entity.FindLegacyUsers(ctx.Args().First()) users := entity.FindLegacyUsers(ctx.Args().First())
@ -37,8 +37,8 @@ func usersLegacyAction(ctx *cli.Context) error {
rows[i] = []string{ rows[i] = []string{
fmt.Sprintf("%d", user.ID), fmt.Sprintf("%d", user.ID),
user.UserUID, user.UserUID,
user.UserName,
user.FullName, user.FullName,
user.UserName,
user.PrimaryEmail, user.PrimaryEmail,
report.Bool(user.Admin(), report.Yes, report.No), report.Bool(user.Admin(), report.Yes, report.No),
user.CreatedAt.Format("2006-01-02 15:04:05"), user.CreatedAt.Format("2006-01-02 15:04:05"),

View file

@ -23,7 +23,7 @@ var UsersListCommand = cli.Command{
// usersListAction displays existing user accounts. // usersListAction displays existing user accounts.
func usersListAction(ctx *cli.Context) error { func usersListAction(ctx *cli.Context) error {
return CallWithDependencies(ctx, func(conf *config.Config) error { return CallWithDependencies(ctx, func(conf *config.Config) error {
cols := []string{"User", "Login", "Full Name", "Email", "Role", "Super Admin", "Web UI", "WebDAV", "Attributes", "Created At"} cols := []string{"UID", "Name", "User", "Email", "Role", "Super Admin", "Web Login", "WebDAV", "Attributes", "Created At"}
// Fetch users from database. // Fetch users from database.
users := query.RegisteredUsers() users := query.RegisteredUsers()
@ -36,8 +36,8 @@ func usersListAction(ctx *cli.Context) error {
for i, user := range users { for i, user := range users {
rows[i] = []string{ rows[i] = []string{
user.UID(), user.UID(),
user.Login(),
user.FullName(), user.FullName(),
user.Login(),
user.Email(), user.Email(),
user.AclRole().String(), user.AclRole().String(),
report.Bool(user.SuperAdmin, report.Yes, report.No), report.Bool(user.SuperAdmin, report.Yes, report.No),

View file

@ -879,11 +879,18 @@ func (m *User) SetAvatar(thumb, thumbSrc string) error {
return m.Updates(Values{"Thumb": m.Thumb, "ThumbSrc": m.ThumbSrc}) return m.Updates(Values{"Thumb": m.Thumb, "ThumbSrc": m.ThumbSrc})
} }
// Login returns the login name and provider. // Login returns the username.
func (m *User) Login() string { func (m *User) Login() string {
if m.AuthProvider == "" || strings.ContainsRune(m.UserName, '@') { return m.UserName
return m.UserName }
} else {
return fmt.Sprintf("%s@%s", m.UserName, m.AuthProvider) // Provider returns the authentication provider name.
} func (m *User) Provider() string {
if m.AuthProvider != "" {
return m.AuthProvider
} else if m.UserName != "" && m.ID > 0 {
return "password"
}
return ""
} }

View file

@ -814,3 +814,27 @@ func TestUser_SetAvatar(t *testing.T) {
assert.Equal(t, SrcManual, m.ThumbSrc) assert.Equal(t, SrcManual, m.ThumbSrc)
}) })
} }
func TestUser_Login(t *testing.T) {
t.Run("Visitor", func(t *testing.T) {
assert.Equal(t, "", Visitor.Login())
})
t.Run("UnknownUser", func(t *testing.T) {
assert.Equal(t, "", UnknownUser.Login())
})
t.Run("Admin", func(t *testing.T) {
assert.Equal(t, "admin", Admin.Login())
})
}
func TestUser_Provider(t *testing.T) {
t.Run("Visitor", func(t *testing.T) {
assert.Equal(t, "", Visitor.Provider())
})
t.Run("UnknownUser", func(t *testing.T) {
assert.Equal(t, "", UnknownUser.Provider())
})
t.Run("Admin", func(t *testing.T) {
assert.Equal(t, "password", Admin.Provider())
})
}