Auth: Refactor users path configuration and base path default

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2023-03-14 21:47:14 +01:00
parent 3755421945
commit addc5e8251
12 changed files with 78 additions and 27 deletions

View file

@ -121,6 +121,22 @@ export class User extends RestModel {
return s[0].trim();
}
defaultBasePath() {
const handle = this.getHandle();
if (!handle) {
return "";
}
let dir = config.get("usersPath");
if (dir) {
return `${dir}/${handle}`;
} else {
return `users/${handle}`;
}
}
getDisplayName() {
if (this.DisplayName) {
return this.DisplayName;

View file

@ -80,6 +80,7 @@ func StartImport(router *gin.RouterGroup) {
RemoveFromFolderCache(entity.RootImport)
// Get destination folder.
var destFolder string
if destFolder = s.User().GetUploadPath(); destFolder == "" {
destFolder = conf.ImportDest()

View file

@ -185,6 +185,7 @@ func ProcessUserUpload(router *gin.RouterGroup) {
imp := get.Import()
// Get destination folder.
var destFolder string
if destFolder = s.User().GetUploadPath(); destFolder == "" {
destFolder = conf.ImportDest()

View file

@ -60,6 +60,7 @@ type ClientConfig struct {
UploadNSFW bool `json:"uploadNSFW"`
Public bool `json:"public"`
AuthMode string `json:"authMode"`
UsersPath string `json:"usersPath"`
LoginUri string `json:"loginUri"`
RegisterUri string `json:"registerUri"`
PasswordLength int `json:"passwordLength"`
@ -278,6 +279,7 @@ func (c *Config) ClientPublic() ClientConfig {
ReadOnly: c.ReadOnly(),
Public: c.Public(),
AuthMode: c.AuthMode(),
UsersPath: c.UsersPath(),
LoginUri: c.LoginUri(),
RegisterUri: c.RegisterUri(),
PasswordResetUri: c.PasswordResetUri(),
@ -364,6 +366,7 @@ func (c *Config) ClientShare() ClientConfig {
UploadNSFW: c.UploadNSFW(),
Public: c.Public(),
AuthMode: c.AuthMode(),
UsersPath: "",
LoginUri: c.LoginUri(),
RegisterUri: c.RegisterUri(),
PasswordResetUri: c.PasswordResetUri(),
@ -455,6 +458,7 @@ func (c *Config) ClientUser(withSettings bool) ClientConfig {
UploadNSFW: c.UploadNSFW(),
Public: c.Public(),
AuthMode: c.AuthMode(),
UsersPath: c.UsersPath(),
LoginUri: c.LoginUri(),
RegisterUri: c.RegisterUri(),
PasswordLength: c.PasswordLength(),

View file

@ -173,6 +173,9 @@ func (c *Config) Propagate() {
// Set minimum password length.
entity.PasswordLength = c.PasswordLength()
// Set path for user assets.
entity.UsersPath = c.UsersPath()
// Set API preview and download default tokens.
entity.PreviewToken.Set(c.PreviewToken(), entity.TokenConfig)
entity.DownloadToken.Set(c.DownloadToken(), entity.TokenConfig)

View file

@ -90,8 +90,8 @@ func (c *Config) CreateDirectories() error {
if c.UsersPath() == "" {
return notFoundError("users")
} else if err := os.MkdirAll(c.UsersPath(), fs.ModeDir); err != nil {
return createError(c.UsersPath(), err)
} else if err := os.MkdirAll(c.UsersStoragePath(), fs.ModeDir); err != nil {
return createError(c.UsersStoragePath(), err)
}
if c.CmdCachePath() == "" {
@ -325,19 +325,28 @@ func (c *Config) SidecarWritable() bool {
return !c.ReadOnly() || c.SidecarPathIsAbs()
}
// UsersPath returns the storage base path for user assets like
// avatar images and other media that should not be indexed.
// UsersPath returns the relative base path for user assets.
func (c *Config) UsersPath() string {
// Set default.
if c.options.UsersPath == "" {
c.options.UsersPath = filepath.Join(c.StoragePath(), "users")
return "users"
}
return c.options.UsersPath
return clean.UserPath(c.options.UsersPath)
}
// UserPath returns the storage path for user assets.
func (c *Config) UserPath(userUid string) string {
// UsersStoragePath returns the users storage base path.
func (c *Config) UsersStoragePath() string {
return filepath.Join(c.StoragePath(), c.UsersPath())
}
// UsersOriginalsPath returns the users originals base path.
func (c *Config) UsersOriginalsPath() string {
return filepath.Join(c.OriginalsPath(), c.UsersPath())
}
// UserStoragePath returns the storage path for user assets.
func (c *Config) UserStoragePath(userUid string) string {
if !rnd.IsUID(userUid, 0) {
return ""
}
@ -357,7 +366,7 @@ func (c *Config) UserUploadPath(userUid, token string) (string, error) {
return "", fmt.Errorf("invalid uid")
}
dir := filepath.Join(c.UserPath(userUid), "upload", clean.Token(token))
dir := filepath.Join(c.UserStoragePath(userUid), "upload", clean.Token(token))
if err := os.MkdirAll(dir, fs.ModeDir); err != nil {
return "", err

View file

@ -25,14 +25,14 @@ func TestConfig_SidecarPath(t *testing.T) {
func TestConfig_UsersPath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Contains(t, c.UsersPath(), "testdata/users")
assert.Contains(t, c.UsersPath(), "users")
}
func TestConfig_UserPath(t *testing.T) {
func TestConfig_UserStoragePath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Equal(t, "", c.UserPath(""))
assert.Equal(t, "", c.UserPath("etaetyget"))
assert.Contains(t, c.UserPath("urjult03ceelhw6k"), "testdata/users/urjult03ceelhw6k")
assert.Equal(t, "", c.UserStoragePath(""))
assert.Equal(t, "", c.UserStoragePath("etaetyget"))
assert.Contains(t, c.UserStoragePath("urjult03ceelhw6k"), "users/urjult03ceelhw6k")
}
func TestConfig_UserUploadPath(t *testing.T) {
@ -50,12 +50,12 @@ func TestConfig_UserUploadPath(t *testing.T) {
if dir, err := c.UserUploadPath("urjult03ceelhw6k", ""); err != nil {
t.Fatal(err)
} else {
assert.Contains(t, dir, "testdata/users/urjult03ceelhw6k/upload")
assert.Contains(t, dir, "users/urjult03ceelhw6k/upload")
}
if dir, err := c.UserUploadPath("urjult03ceelhw6k", "foo"); err != nil {
t.Fatal(err)
} else {
assert.Contains(t, dir, "testdata/users/urjult03ceelhw6k/upload/foo")
assert.Contains(t, dir, "users/urjult03ceelhw6k/upload/foo")
}
}

View file

@ -142,11 +142,6 @@ var Flags = CliFlags{
Usage: "custom relative or absolute sidecar `PATH`*optional*",
EnvVar: "PHOTOPRISM_SIDECAR_PATH",
}}, {
Flag: cli.StringFlag{
Name: "users-path",
Usage: "custom users storage `PATH`*optional*",
EnvVar: "PHOTOPRISM_USERS_PATH",
}}, {
Flag: cli.StringFlag{
Name: "backup-path, ba",
Usage: "custom backup `PATH` for index backup files*optional*",
@ -167,6 +162,12 @@ var Flags = CliFlags{
Usage: "relative originals `PATH` to which the files should be imported by default*optional*",
EnvVar: "PHOTOPRISM_IMPORT_DEST",
}}, {
Flag: cli.StringFlag{
Name: "users-path",
Usage: "relative `PATH` to create base and upload subdirectories for users",
EnvVar: "PHOTOPRISM_USERS_PATH",
Value: "users",
}}, {
Flag: cli.StringFlag{
Name: "assets-path, as",
Usage: "assets `PATH` containing static resources like icons, models, and translations",

View file

@ -57,10 +57,9 @@ func (c *Config) Report() (rows [][]string, cols []string) {
{"originals-limit", fmt.Sprintf("%d", c.OriginalsLimit())},
{"resolution-limit", fmt.Sprintf("%d", c.ResolutionLimit())},
// Other paths.
// Storage.
{"storage-path", c.StoragePath()},
{"sidecar-path", c.SidecarPath()},
{"users-path", c.UsersPath()},
{"albums-path", c.AlbumsPath()},
{"backup-path", c.BackupPath()},
{"cache-path", c.CachePath()},
@ -69,6 +68,9 @@ func (c *Config) Report() (rows [][]string, cols []string) {
{"thumb-cache-path", c.ThumbCachePath()},
{"import-path", c.ImportPath()},
{"import-dest", c.ImportDest()},
{"users-path", c.UsersPath()},
{"users-storage-path", c.UsersStoragePath()},
{"users-originals-path", c.UsersOriginalsPath()},
{"assets-path", c.AssetsPath()},
{"static-path", c.StaticPath()},
{"build-path", c.BuildPath()},

View file

@ -34,6 +34,9 @@ var UsernameLength = 1
// PasswordLength specifies the minimum length of the password in characters.
var PasswordLength = 4
// UsersPath is the relative path for user assets.
var UsersPath = "users"
// Users represents a list of users.
type Users []User
@ -118,6 +121,8 @@ func FindUser(find User) *User {
stmt = stmt.Where("id = ?", find.ID)
} else if rnd.IsUID(find.UserUID, UserUID) {
stmt = stmt.Where("user_uid = ?", find.UserUID)
} else if find.AuthProvider != "" && find.AuthID != "" && find.UserName != "" {
stmt = stmt.Where("auth_provider = ? AND auth_id = ? OR user_name = ?", find.AuthProvider, find.AuthID, find.UserName)
} else if find.UserName != "" {
stmt = stmt.Where("user_name = ?", find.UserName)
} else if find.UserEmail != "" {
@ -432,7 +437,7 @@ func (m *User) DefaultBasePath() string {
if s := m.Handle(); s == "" {
return ""
} else {
return fmt.Sprintf("users/%s", s)
return path.Join(UsersPath, s)
}
}

View file

@ -5,6 +5,8 @@ import (
"encoding/base64"
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
@ -13,11 +15,13 @@ import (
gc "github.com/patrickmn/go-cache"
"github.com/photoprism/photoprism/internal/api"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/server/limiter"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
)
// Authentication cache with an expiration time of 5 minutes.
@ -38,7 +42,7 @@ func GetAuthUser(key string) *entity.User {
}
// BasicAuth implements an HTTP request handler that adds basic authentication.
func BasicAuth() gin.HandlerFunc {
func BasicAuth(conf *config.Config) gin.HandlerFunc {
var validate = func(c *gin.Context) (name, password, key string, valid bool) {
name, password, key = GetCredentials(c)
@ -106,6 +110,11 @@ func BasicAuth() gin.HandlerFunc {
// Sync disabled for this account.
message := "sync disabled"
event.AuditWarn([]string{clientIp, "webdav login as %s", message}, clean.LogQuote(name))
event.LoginError(clientIp, "webdav", name, api.UserAgent(c), message)
} else if err = os.MkdirAll(filepath.Join(conf.OriginalsPath(), user.GetUploadPath()), fs.ModeDir); err != nil {
message := "failed to create user upload path"
event.AuditWarn([]string{clientIp, "webdav login as %s", message}, clean.LogQuote(name))
event.LoginError(clientIp, "webdav", name, api.UserAgent(c), message)
} else {

View file

@ -31,11 +31,11 @@ func registerWebDAVRoutes(router *gin.Engine, conf *config.Config) {
info = ""
}
WebDAV(conf.OriginalsPath(), router.Group(conf.BaseUri(WebDAVOriginals), BasicAuth()), conf)
WebDAV(conf.OriginalsPath(), router.Group(conf.BaseUri(WebDAVOriginals), BasicAuth(conf)), conf)
log.Infof("webdav: shared %s/%s", conf.BaseUri(WebDAVOriginals), info)
if conf.ImportPath() != "" {
WebDAV(conf.ImportPath(), router.Group(conf.BaseUri(WebDAVImport), BasicAuth()), conf)
WebDAV(conf.ImportPath(), router.Group(conf.BaseUri(WebDAVImport), BasicAuth(conf)), conf)
log.Infof("webdav: shared %s/%s", conf.BaseUri(WebDAVImport), info)
}
}