mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -93,24 +94,31 @@ func WebDAVAuth(conf *config.Config) gin.HandlerFunc {
|
||||
// Ignore and try basic auth next.
|
||||
} else if !sess.HasUser() || user == nil {
|
||||
// Log error if session does not belong to an authorized user account.
|
||||
event.AuditErr([]string{clientIp, "session %s", "access webdav without authorized user account", "denied"}, sess.RefID)
|
||||
event.AuditErr([]string{clientIp, "session %s", "access webdav without user account", "denied"}, sess.RefID)
|
||||
WebDAVAbortUnauthorized(c)
|
||||
return
|
||||
} else if sess.IsClient() && !sess.HasScope(acl.ResourceWebDAV.String()) {
|
||||
// Log error if the client is allowed to access webdav based on its scope.
|
||||
event.AuditErr([]string{clientIp, "client %s", "session %s", "access webdav without scope authorization", "denied"}, clean.Log(sess.AuthID), sess.RefID)
|
||||
message := "denied"
|
||||
event.AuditWarn([]string{clientIp, "client %s", "session %s", "access webdav as %s", message}, clean.Log(sess.AuthID), sess.RefID, clean.LogQuote(user.Username()))
|
||||
WebDAVAbortUnauthorized(c)
|
||||
return
|
||||
} else if !user.CanUseWebDAV() {
|
||||
// Log warning if WebDAV is disabled for this account.
|
||||
message := "webdav access disabled"
|
||||
event.AuditWarn([]string{clientIp, "access webdav as %s", message}, clean.LogQuote(username))
|
||||
message := "webdav access is disabled"
|
||||
event.AuditWarn([]string{clientIp, "client %s", "session %s", "access webdav as %s", message}, clean.Log(sess.AuthID), sess.RefID, clean.LogQuote(user.Username()))
|
||||
WebDAVAbortUnauthorized(c)
|
||||
return
|
||||
} else if username != "" && !strings.EqualFold(clean.Username(username), user.Username()) {
|
||||
// Log warning if WebDAV is disabled for this account.
|
||||
message := "basic auth username does not match"
|
||||
event.AuditWarn([]string{clientIp, "client %s", "session %s", "access webdav as %s", message}, clean.Log(sess.AuthID), sess.RefID, clean.LogQuote(user.Username()))
|
||||
WebDAVAbortUnauthorized(c)
|
||||
return
|
||||
} else if err := os.MkdirAll(filepath.Join(conf.OriginalsPath(), user.GetUploadPath()), fs.ModeDir); err != nil {
|
||||
// Log warning if upload path could not be created.
|
||||
message := "failed to create user upload path"
|
||||
event.AuditWarn([]string{clientIp, "access webdav as %s", message}, clean.LogQuote(username))
|
||||
event.AuditWarn([]string{clientIp, "client %s", "session %s", "access webdav as %s", message}, clean.Log(sess.AuthID), sess.RefID, clean.LogQuote(user.Username()))
|
||||
WebDAVAbortServerError(c)
|
||||
return
|
||||
} else {
|
||||
@@ -159,7 +167,7 @@ func WebDAVAuth(conf *config.Config) gin.HandlerFunc {
|
||||
event.LoginError(clientIp, "webdav", username, api.UserAgent(c), message)
|
||||
} else if !user.CanUseWebDAV() {
|
||||
// Abort if WebDAV is disabled for this account.
|
||||
message := "webdav access disabled"
|
||||
message := "webdav access is disabled"
|
||||
event.AuditWarn([]string{clientIp, "webdav login as %s", message}, clean.LogQuote(username))
|
||||
event.LoginError(clientIp, "webdav", username, api.UserAgent(c), message)
|
||||
} else if err = os.MkdirAll(filepath.Join(conf.OriginalsPath(), user.GetUploadPath()), fs.ModeDir); err != nil {
|
||||
|
||||
@@ -28,6 +28,7 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
Header: make(http.Header),
|
||||
}
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
||||
@@ -40,6 +41,8 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
Header: make(http.Header),
|
||||
}
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
|
||||
sess := entity.SessionFixtures.Get("alice_token")
|
||||
header.SetAuthorization(c.Request, sess.AuthToken())
|
||||
|
||||
@@ -56,14 +59,32 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
}
|
||||
|
||||
sess := entity.SessionFixtures.Get("alice_token_webdav")
|
||||
basicAuth := []byte(fmt.Sprintf("access-token:%s", sess.AuthToken()))
|
||||
basicAuth := []byte(fmt.Sprintf("alice:%s", sess.AuthToken()))
|
||||
c.Request.Header.Add(header.Auth, fmt.Sprintf("%s %s", header.AuthBasic, base64.StdEncoding.EncodeToString(basicAuth)))
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, c.Writer.Status())
|
||||
assert.Equal(t, "", c.Writer.Header().Get("WWW-Authenticate"))
|
||||
})
|
||||
t.Run("AliceTokenWebdavWrongUsername", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = &http.Request{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
|
||||
sess := entity.SessionFixtures.Get("alice_token_webdav")
|
||||
basicAuth := []byte(fmt.Sprintf("bob:%s", sess.AuthToken()))
|
||||
c.Request.Header.Add(header.Auth, fmt.Sprintf("%s %s", header.AuthBasic, base64.StdEncoding.EncodeToString(basicAuth)))
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
||||
assert.Equal(t, BasicAuthRealm, c.Writer.Header().Get("WWW-Authenticate"))
|
||||
})
|
||||
t.Run("AliceTokenWebdavWithoutUsername", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
@@ -75,6 +96,7 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
basicAuth := []byte(fmt.Sprintf(":%s", sess.AuthToken()))
|
||||
c.Request.Header.Add(header.Auth, fmt.Sprintf("%s %s", header.AuthBasic, base64.StdEncoding.EncodeToString(basicAuth)))
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, c.Writer.Status())
|
||||
@@ -90,6 +112,7 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
sess := entity.SessionFixtures.Get("alice_token_scope")
|
||||
header.SetAuthorization(c.Request, sess.AuthToken())
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
||||
@@ -104,6 +127,7 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
|
||||
header.SetAuthorization(c.Request, rnd.AuthToken())
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
||||
@@ -118,6 +142,7 @@ func TestWebDAVAuth(t *testing.T) {
|
||||
|
||||
header.SetAuthorization(c.Request, rnd.AuthSecret())
|
||||
|
||||
webdavAuthCache.Flush()
|
||||
webdavHandler(c)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
|
||||
@@ -136,22 +161,36 @@ func TestWebDAVAuthSession(t *testing.T) {
|
||||
s := entity.SessionFixtures.Get("alice_token_webdav")
|
||||
|
||||
// Get session with authorized user and webdav scope.
|
||||
webdavAuthCache.Flush()
|
||||
sess, user, sid, cached := WebDAVAuthSession(c, s.AuthToken())
|
||||
|
||||
// Check result.
|
||||
if cached {
|
||||
assert.Nil(t, sess)
|
||||
assert.NotNil(t, user)
|
||||
assert.True(t, cached)
|
||||
} else {
|
||||
assert.NotNil(t, sess)
|
||||
assert.NotNil(t, user)
|
||||
assert.True(t, sess.HasUser())
|
||||
assert.Equal(t, user.UserUID, sess.UserUID)
|
||||
assert.Equal(t, entity.UserFixtures.Get("alice").UserUID, sess.UserUID)
|
||||
assert.True(t, sess.HasScope(acl.ResourceWebDAV.String()))
|
||||
assert.False(t, cached)
|
||||
}
|
||||
assert.NotNil(t, sess)
|
||||
assert.NotNil(t, user)
|
||||
assert.True(t, sess.HasUser())
|
||||
assert.Equal(t, user.UserUID, sess.UserUID)
|
||||
assert.Equal(t, entity.UserFixtures.Get("alice").UserUID, sess.UserUID)
|
||||
assert.True(t, sess.HasScope(acl.ResourceWebDAV.String()))
|
||||
assert.False(t, cached)
|
||||
|
||||
assert.Equal(t, s.ID, sid)
|
||||
assert.Equal(t, entity.UserFixtures.Get("alice").UserUID, user.UserUID)
|
||||
assert.True(t, user.CanUseWebDAV())
|
||||
|
||||
// WebDAVAuthSession should not set a status code or any headers.
|
||||
assert.Equal(t, http.StatusOK, c.Writer.Status())
|
||||
assert.Equal(t, "", c.Writer.Header().Get("WWW-Authenticate"))
|
||||
|
||||
// Cache authentication.
|
||||
webdavAuthCache.SetDefault(sid, user)
|
||||
|
||||
// Get cached user.
|
||||
sess, user, sid, cached = WebDAVAuthSession(c, s.AuthToken())
|
||||
|
||||
// Check result.
|
||||
assert.Nil(t, sess)
|
||||
assert.NotNil(t, user)
|
||||
assert.True(t, cached)
|
||||
|
||||
assert.Equal(t, s.ID, sid)
|
||||
assert.Equal(t, entity.UserFixtures.Get("alice").UserUID, user.UserUID)
|
||||
|
||||
Reference in New Issue
Block a user