Auth: Improve user entity model and add unit tests #98

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2023-02-20 15:54:33 +01:00
parent 1942f93bf1
commit 43d6a5e193
8 changed files with 220 additions and 23 deletions

View file

@ -81,7 +81,7 @@ func StartImport(router *gin.RouterGroup) {
RemoveFromFolderCache(entity.RootImport)
var destFolder string
if destFolder = s.User().UploadPath; destFolder == "" {
if destFolder = s.User().GetUploadPath(); destFolder == "" {
destFolder = conf.ImportDest()
}

View file

@ -175,7 +175,7 @@ func ProcessUserUpload(router *gin.RouterGroup) {
imp := get.Import()
var destFolder string
if destFolder = s.User().UploadPath; destFolder == "" {
if destFolder = s.User().GetUploadPath(); destFolder == "" {
destFolder = conf.ImportDest()
}

View file

@ -15,6 +15,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/list"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -352,19 +353,49 @@ func (m *User) CanUpload() bool {
}
}
// SetBasePath changes the user's base folder.
// GetBasePath returns the user's relative base path.
func (m *User) GetBasePath() string {
return m.BasePath
}
// SetBasePath changes the user's relative base path.
func (m *User) SetBasePath(dir string) *User {
m.BasePath = clean.UserPath(dir)
if list.Contains(list.List{"", ".", "./", "/", "\\"}, dir) {
m.BasePath = ""
} else if dir == "~" && m.UserUID != "" {
m.BasePath = fmt.Sprintf("users/%s", m.UserUID)
} else {
m.BasePath = clean.UserPath(dir)
}
return m
}
// SetUploadPath changes the user's upload folder.
// GetUploadPath returns the user's relative upload path.
func (m *User) GetUploadPath() string {
basePath := m.GetBasePath()
if list.Contains(list.List{"", ".", "./"}, m.UploadPath) {
return basePath
} else if basePath != "" && strings.HasPrefix(m.UploadPath, basePath+"/") {
return m.UploadPath
} else if basePath == "" && m.UploadPath == "~" && m.UserUID != "" {
return fmt.Sprintf("users/%s", m.UserUID)
}
return path.Join(basePath, m.UploadPath)
}
// SetUploadPath changes the user's relative upload path.
func (m *User) SetUploadPath(dir string) *User {
if m.BasePath == "" {
m.UploadPath = clean.UserPath(dir)
basePath := m.GetBasePath()
if list.Contains(list.List{"", ".", "./", "/", "\\"}, dir) {
m.UploadPath = ""
} else if basePath == "" && dir == "~" && m.UserUID != "" {
m.UploadPath = fmt.Sprintf("users/%s", m.UserUID)
} else {
m.UploadPath = path.Join(m.BasePath, clean.UserPath(dir))
m.UploadPath = clean.UserPath(dir)
}
return m
@ -432,13 +463,19 @@ func (m *User) Email() string {
return clean.Email(m.UserEmail)
}
// Handle returns the user's login handle.
func (m *User) Handle() string {
handle, _, _ := strings.Cut(m.UserName, "@")
return handle
}
// FullName returns the name of the user for display purposes.
func (m *User) FullName() string {
switch {
case m.DisplayName != "":
return m.DisplayName
default:
return m.UserName
return clean.NameCapitalized(strings.ReplaceAll(m.Handle(), ".", " "))
}
}
@ -837,19 +874,14 @@ func (m *User) SaveForm(f form.User) error {
func (m *User) SetDisplayName(name string) *User {
name = clean.Name(name)
// Empty?
if name == "" {
d := m.Details()
if name == "" || SrcPriority[SrcAuto] < SrcPriority[d.NameSrc] {
return m
}
m.DisplayName = name
d := m.Details()
if SrcPriority[SrcAuto] < SrcPriority[d.NameSrc] {
return m
}
// Try to parse name into components.
n := txt.ParseName(name)
@ -863,6 +895,18 @@ func (m *User) SetDisplayName(name string) *User {
return m
}
// SetGivenName updates the user's given name.
func (m *User) SetGivenName(name string) *User {
m.Details().SetGivenName(name)
return m
}
// SetFamilyName updates the user's family name.
func (m *User) SetFamilyName(name string) *User {
m.Details().SetFamilyName(name)
return m
}
// SetAvatar updates the user avatar image.
func (m *User) SetAvatar(thumb, thumbSrc string) error {
if m.UserName == "" || m.ID <= 0 {

View file

@ -4,6 +4,7 @@ import (
"fmt"
"time"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/rnd"
)
@ -100,3 +101,29 @@ func (m *UserDetails) Save() error {
func (m *UserDetails) Updates(values interface{}) error {
return UnscopedDb().Model(m).Updates(values).Error
}
// SetGivenName updates the user's given name.
func (m *UserDetails) SetGivenName(name string) *UserDetails {
name = clean.Name(name)
if name == "" || SrcPriority[SrcAuto] < SrcPriority[m.NameSrc] {
return m
}
m.GivenName = name
return m
}
// SetFamilyName updates the user's family name.
func (m *UserDetails) SetFamilyName(name string) *UserDetails {
name = clean.Name(name)
if name == "" || SrcPriority[SrcAuto] < SrcPriority[m.NameSrc] {
return m
}
m.FamilyName = name
return m
}

View file

@ -838,3 +838,129 @@ func TestUser_Provider(t *testing.T) {
assert.Equal(t, "password", Admin.Provider())
})
}
func TestUser_GetBasePath(t *testing.T) {
t.Run("Visitor", func(t *testing.T) {
assert.Equal(t, "", Visitor.GetBasePath())
})
t.Run("UnknownUser", func(t *testing.T) {
assert.Equal(t, "", UnknownUser.GetBasePath())
})
t.Run("Admin", func(t *testing.T) {
assert.Equal(t, "", Admin.GetBasePath())
})
}
func TestUser_SetBasePath(t *testing.T) {
t.Run("Test", func(t *testing.T) {
u := User{
ID: 1234567,
UserUID: "urqdrfb72479n047",
UserName: "test",
UserRole: acl.RoleAdmin.String(),
DisplayName: "Test",
SuperAdmin: false,
CanLogin: true,
WebDAV: true,
CanInvite: false,
}
assert.Equal(t, "base", u.SetBasePath("base").GetBasePath())
assert.Equal(t, "users/urqdrfb72479n047", u.SetBasePath("~").GetBasePath())
assert.Equal(t, "users/urqdrfb72479n047", u.GetUploadPath())
})
}
func TestUser_GetUploadPath(t *testing.T) {
t.Run("Visitor", func(t *testing.T) {
assert.Equal(t, "", Visitor.GetUploadPath())
})
t.Run("UnknownUser", func(t *testing.T) {
assert.Equal(t, "", UnknownUser.GetUploadPath())
})
t.Run("Admin", func(t *testing.T) {
assert.Equal(t, "", Admin.GetUploadPath())
})
}
func TestUser_SetUploadPath(t *testing.T) {
t.Run("Test", func(t *testing.T) {
u := User{
ID: 1234567,
UserUID: "urqdrfb72479n047",
UserName: "test",
UserRole: acl.RoleAdmin.String(),
DisplayName: "Test",
SuperAdmin: false,
CanLogin: true,
WebDAV: true,
CanInvite: false,
}
assert.Equal(t, "upload", u.SetUploadPath("upload").GetUploadPath())
assert.Equal(t, "base/upload", u.SetBasePath("base").GetUploadPath())
assert.Equal(t, "base", u.SetUploadPath("~").GetUploadPath())
assert.Equal(t, "users/urqdrfb72479n047", u.SetBasePath("~").GetUploadPath())
})
}
func TestUser_Handle(t *testing.T) {
t.Run("Default", func(t *testing.T) {
u := User{
ID: 1234567,
UserUID: "urqdrfb72479n047",
UserName: "mr-happy@cat.com",
UserRole: acl.RoleAdmin.String(),
DisplayName: "",
SuperAdmin: false,
CanLogin: true,
WebDAV: true,
CanInvite: false,
}
assert.Equal(t, "mr-happy@cat.com", u.Login())
assert.Equal(t, "mr-happy", u.Handle())
u.UserName = "mr.happy@cat.com"
assert.Equal(t, "mr.happy", u.Handle())
u.UserName = "mr.happy"
assert.Equal(t, "mr.happy", u.Handle())
})
}
func TestUser_FullName(t *testing.T) {
t.Run("Default", func(t *testing.T) {
u := User{
ID: 1234567,
UserUID: "urqdrfb72479n047",
UserName: "mr-happy@cat.com",
UserRole: acl.RoleAdmin.String(),
DisplayName: "",
SuperAdmin: false,
CanLogin: true,
WebDAV: true,
CanInvite: false,
}
assert.Equal(t, "Mr-Happy", u.FullName())
u.UserName = "mr.happy@cat.com"
assert.Equal(t, "Mr Happy", u.FullName())
u.UserName = "mr.happy"
assert.Equal(t, "Mr Happy", u.FullName())
u.UserName = "foo@bar.com"
assert.Equal(t, "Foo", u.FullName())
u.SetDisplayName("Jane Doe")
assert.Equal(t, "Jane Doe", u.FullName())
})
}

View file

@ -65,11 +65,11 @@ func UserAlbums(f form.SearchAlbums, sess *entity.Session) (results AlbumResults
if sess.IsVisitor() || sess.NotRegistered() {
s = s.Where("albums.album_uid IN (?) OR albums.published_at > ?", sess.SharedUIDs(), entity.TimeStamp())
} else if acl.Resources.DenyAll(aclResource, aclRole, acl.Permissions{acl.AccessAll, acl.AccessLibrary}) {
if user.BasePath == "" {
if basePath := user.GetBasePath(); basePath == "" {
s = s.Where("albums.album_uid IN (?) OR albums.created_by = ? OR albums.published_at > ?", sess.SharedUIDs(), user.UserUID, entity.TimeStamp())
} else {
s = s.Where("albums.album_uid IN (?) OR albums.created_by = ? OR albums.published_at > ? OR albums.album_type = ? AND (albums.album_path = ? OR albums.album_path LIKE ?)",
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), entity.AlbumFolder, user.BasePath, user.BasePath+"/%")
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), entity.AlbumFolder, basePath, basePath+"/%")
}
}

View file

@ -127,11 +127,11 @@ func searchPhotos(f form.SearchPhotos, sess *entity.Session, resultCols string)
if sess.IsVisitor() || sess.NotRegistered() {
s = s.Where(sharedAlbums+"photos.published_at > ?", sess.SharedUIDs(), entity.TimeStamp())
} else if user.BasePath == "" {
} else if basePath := user.GetBasePath(); basePath == "" {
s = s.Where(sharedAlbums+"photos.created_by = ? OR photos.published_at > ?", sess.SharedUIDs(), user.UserUID, entity.TimeStamp())
} else {
s = s.Where(sharedAlbums+"photos.created_by = ? OR photos.published_at > ? OR photos.photo_path = ? OR photos.photo_path LIKE ?",
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), user.BasePath, user.BasePath+"/%")
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), basePath, basePath+"/%")
}
}
}

View file

@ -125,11 +125,11 @@ func UserPhotosGeo(f form.SearchPhotosGeo, sess *entity.Session) (results GeoRes
if sess.IsVisitor() || sess.NotRegistered() {
s = s.Where(sharedAlbums+"photos.published_at > ?", sess.SharedUIDs(), entity.TimeStamp())
} else if user.BasePath == "" {
} else if basePath := user.GetBasePath(); basePath == "" {
s = s.Where(sharedAlbums+"photos.created_by = ? OR photos.published_at > ?", sess.SharedUIDs(), user.UserUID, entity.TimeStamp())
} else {
s = s.Where(sharedAlbums+"photos.created_by = ? OR photos.published_at > ? OR photos.photo_path = ? OR photos.photo_path LIKE ?",
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), user.BasePath, user.BasePath+"/%")
sess.SharedUIDs(), user.UserUID, entity.TimeStamp(), basePath, basePath+"/%")
}
}
}