mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
API: Apply "golangci-lint" recommendations #5330
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -565,13 +565,11 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
||||
// Find album by UID.
|
||||
album, err := query.AlbumByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil, !album.HasID():
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
} else if !album.HasID() {
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
} else if frm.Empty() {
|
||||
case frm.Empty():
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,11 +25,12 @@ Additional information can be found in our Developer Guide:
|
||||
package api
|
||||
|
||||
import (
|
||||
_ "net/http"
|
||||
// Blank imports register HTTP handlers, models, translations, and form bindings via init side effects.
|
||||
_ "net/http" // register net/http handlers referenced by swagger
|
||||
|
||||
_ "github.com/gin-gonic/gin"
|
||||
_ "github.com/gin-gonic/gin" // register gin metadata used by swaggo
|
||||
|
||||
_ "github.com/photoprism/photoprism/internal/auth/acl"
|
||||
_ "github.com/photoprism/photoprism/internal/auth/acl" // embed ACL docs
|
||||
_ "github.com/photoprism/photoprism/internal/entity"
|
||||
_ "github.com/photoprism/photoprism/internal/entity/query"
|
||||
_ "github.com/photoprism/photoprism/internal/event"
|
||||
|
||||
@@ -35,14 +35,11 @@ func AuthAny(c *gin.Context, resource acl.Resource, perms acl.Permissions) (s *e
|
||||
c.Header(header.CacheControl, header.CacheControlNoStore)
|
||||
|
||||
// Allow requests based on an access token for specific resources.
|
||||
switch resource {
|
||||
case acl.ResourceVision:
|
||||
if perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken {
|
||||
if resource == acl.ResourceVision && perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken {
|
||||
s = entity.NewSessionFromToken(c, authToken, acl.ResourceVision.String(), "service-key")
|
||||
event.AuditInfo([]string{clientIp, "%s", "%s %s as %s", status.Granted}, s.RefID, perms.First(), string(resource), s.GetClientRole().String())
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// Find active session to perform authorization check or deny if no session was found.
|
||||
if s = Session(clientIp, authToken); s == nil {
|
||||
|
||||
@@ -58,10 +58,6 @@ func (r *CloseableResponseRecorder) CloseNotify() <-chan bool {
|
||||
return r.closeCh
|
||||
}
|
||||
|
||||
func (r *CloseableResponseRecorder) closeClient() {
|
||||
r.closeCh <- true
|
||||
}
|
||||
|
||||
// NewApiTest returns new API test helper.
|
||||
func NewApiTest() (app *gin.Engine, router *gin.RouterGroup, conf *config.Config) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
@@ -329,25 +329,27 @@ func BatchPhotosDelete(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Get selection or all archived photos if f.All is true.
|
||||
if len(frm.Photos) == 0 && !frm.All {
|
||||
switch {
|
||||
case len(frm.Photos) == 0 && !frm.All:
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
} else if frm.All {
|
||||
case frm.All:
|
||||
photos, err = query.ArchivedPhotos(1000000, 0)
|
||||
} else {
|
||||
default:
|
||||
photos, err = query.SelectedPhotos(frm)
|
||||
}
|
||||
|
||||
// Abort if the query failed or no photos were found.
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Errorf("archive: %s", err)
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
} else if len(photos) > 0 {
|
||||
log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos"))
|
||||
} else {
|
||||
case len(photos) == 0:
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
default:
|
||||
log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos"))
|
||||
}
|
||||
|
||||
var deleted entity.Photos
|
||||
|
||||
@@ -73,7 +73,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
preloadedPhotos := map[string]*entity.Photo{}
|
||||
var preloadedPhotos map[string]*entity.Photo
|
||||
|
||||
if hydrated, err := query.PhotoPreloadByUIDs(photos.UIDs()); err != nil {
|
||||
log.Errorf("batch: failed to preload photo selection: %s", err)
|
||||
@@ -107,7 +107,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) {
|
||||
// Refresh selected photos from database?
|
||||
if !savedAny {
|
||||
// Don't refresh.
|
||||
} else if photos, count, err = search.BatchPhotos(frm.Photos, s); err != nil {
|
||||
} else if photos, _, err = search.BatchPhotos(frm.Photos, s); err != nil {
|
||||
log.Errorf("batch: %s (refresh selection)", clean.Error(err))
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
|
||||
}
|
||||
for i := 0; i < len(name); i++ {
|
||||
b := name[i]
|
||||
if !(b == '-' || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')) {
|
||||
if b != '-' && (b < 'a' || b > 'z') && (b < '0' || b > '9') {
|
||||
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name chars", status.Failed})
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
|
||||
@@ -72,7 +72,7 @@ func SaveConfigOptions(router *gin.RouterGroup) {
|
||||
v := make(entity.Values)
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
yamlData, err := os.ReadFile(fileName)
|
||||
yamlData, err := os.ReadFile(fileName) // #nosec G304 file path validated above
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("config: failed loading values from %s (%s)", clean.Log(fileName), err)
|
||||
|
||||
@@ -130,7 +130,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
||||
alias = file.DownloadName(dlName, seq)
|
||||
}
|
||||
|
||||
aliases[key] += 1
|
||||
aliases[key]++
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil {
|
||||
|
||||
@@ -133,26 +133,27 @@ func OAuthRevoke(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Check revocation request and abort if invalid.
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil:
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Error(err)}, clean.Log(sess.RefID), role.String())
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
} else if sess == nil {
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
|
||||
case sess == nil:
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, "", role.String())
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
} else if sess.Abort(c) {
|
||||
case sess.Abort(c):
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
|
||||
return
|
||||
} else if !sess.IsClient() {
|
||||
case !sess.IsClient():
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String())
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, i18n.NewResponse(http.StatusForbidden, i18n.ErrForbidden))
|
||||
return
|
||||
} else if sUserUID != "" && sess.UserUID != sUserUID {
|
||||
case sUserUID != "" && sess.UserUID != sUserUID:
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", authn.ErrUnauthorized.Error()}, clean.Log(sess.RefID), role.String())
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
} else {
|
||||
default:
|
||||
event.AuditInfo([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Granted}, clean.Log(sess.RefID), role.String())
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func TestOAuthRevoke(t *testing.T) {
|
||||
const tokenPath = "/api/v1/oauth/token"
|
||||
const tokenPath = "/api/v1/oauth/token" // #nosec G101 test constant, not a credential
|
||||
const revokePath = "/api/v1/oauth/revoke"
|
||||
|
||||
t.Run("ClientSuccessToken", func(t *testing.T) {
|
||||
|
||||
@@ -86,11 +86,12 @@ func OAuthToken(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
if frm.ClientID != "" {
|
||||
switch {
|
||||
case frm.ClientID != "":
|
||||
actor = fmt.Sprintf("client %s", clean.Log(frm.ClientID))
|
||||
} else if frm.Username != "" {
|
||||
case frm.Username != "":
|
||||
actor = fmt.Sprintf("user %s", clean.Log(frm.Username))
|
||||
} else if frm.GrantType == authn.GrantPassword {
|
||||
case frm.GrantType == authn.GrantPassword:
|
||||
actor = "unknown user"
|
||||
}
|
||||
|
||||
@@ -150,17 +151,18 @@ func OAuthToken(router *gin.RouterGroup) {
|
||||
|
||||
authUser, authProvider, authMethod, authErr := entity.Auth(loginForm, nil, c)
|
||||
|
||||
if authProvider.IsClient() {
|
||||
switch {
|
||||
case authProvider.IsClient():
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Denied})
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
} else if authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired) {
|
||||
case authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired):
|
||||
// Ok.
|
||||
} else if authErr != nil {
|
||||
case authErr != nil:
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Error(authErr)})
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
} else if !authUser.Equal(s.GetUser()) {
|
||||
case !authUser.Equal(s.GetUser()):
|
||||
event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrUserDoesNotMatch.Error()})
|
||||
AbortInvalidCredentials(c)
|
||||
return
|
||||
|
||||
@@ -51,8 +51,7 @@ func OIDCLogin(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Check request rate limit.
|
||||
var r *limiter.Request
|
||||
r = limiter.Login.Request(clientIp)
|
||||
r := limiter.Login.Request(clientIp)
|
||||
|
||||
// Abort if failure rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
|
||||
@@ -66,8 +66,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Check request rate limit.
|
||||
var r *limiter.Request
|
||||
r = limiter.Login.Request(clientIp)
|
||||
r := limiter.Login.Request(clientIp)
|
||||
|
||||
// Abort if failure rate limit is exceeded.
|
||||
if r.Reject() || limiter.Auth.Reject(clientIp) {
|
||||
@@ -145,17 +144,18 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
||||
event.AuditInfo([]string{clientIp, "create session", "oidc", "found user", userName})
|
||||
|
||||
// Check if the account is enabled and the OIDC Subject ID matches.
|
||||
if !user.CanLogIn() {
|
||||
switch {
|
||||
case !user.CanLogIn():
|
||||
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAccountDisabled.Error()})
|
||||
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAccountDisabled.Error())
|
||||
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
|
||||
return
|
||||
} else if authn.ProviderOIDC.NotEqual(user.AuthProvider) {
|
||||
case authn.ProviderOIDC.NotEqual(user.AuthProvider):
|
||||
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAuthProviderIsNotOIDC.Error()})
|
||||
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAuthProviderIsNotOIDC.Error())
|
||||
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
|
||||
return
|
||||
} else if user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID {
|
||||
case user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID:
|
||||
event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrInvalidAuthID.Error()})
|
||||
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrInvalidAuthID.Error())
|
||||
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
|
||||
@@ -228,12 +228,12 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Set user avatar image?
|
||||
if avatarUrl := userInfo.Picture; avatarUrl == "" || user.HasAvatar() {
|
||||
// Do nothing.
|
||||
} else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
|
||||
if avatarUrl := userInfo.Picture; avatarUrl != "" && !user.HasAvatar() {
|
||||
if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil {
|
||||
event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()})
|
||||
}
|
||||
} else if conf.UsersQuotaReached(conf.OIDCRole()) {
|
||||
}
|
||||
} else if conf.UsersQuotaReached(conf.OIDCRole()) { //nolint:gocritic
|
||||
userName = oidcUser.Username()
|
||||
event.AuditWarn([]string{clientIp, "create session", "oidc", "create user", userName, authn.ErrUsersQuotaExceeded.Error()})
|
||||
event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrUsersQuotaExceeded.Error())
|
||||
@@ -247,7 +247,7 @@ func OIDCRedirect(router *gin.RouterGroup) {
|
||||
|
||||
// Resolve potential naming conflict by adding a random number to the username.
|
||||
if found := entity.FindUserByName(userName); found != nil {
|
||||
userName = userName + rnd.Base10(6)
|
||||
userName += rnd.Base10(6)
|
||||
}
|
||||
|
||||
event.AuditInfo([]string{clientIp, "create session", "oidc", "create user", userName})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -136,6 +137,11 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
if labelId < 0 {
|
||||
AbortBadRequest(c, errors.New("invalid label id"))
|
||||
return
|
||||
}
|
||||
|
||||
label, err := query.PhotoLabel(m.ID, uint(labelId))
|
||||
|
||||
if err != nil {
|
||||
@@ -143,13 +149,14 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
if (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100 {
|
||||
switch {
|
||||
case (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100:
|
||||
logErr("label", entity.Db().Delete(&label).Error)
|
||||
} else if label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch {
|
||||
case label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch:
|
||||
label.Uncertainty = 100
|
||||
label.LabelSrc = entity.SrcManual
|
||||
logErr("label", entity.Db().Save(&label).Error)
|
||||
} else {
|
||||
default:
|
||||
logErr("label", entity.Db().Save(&label).Error)
|
||||
}
|
||||
|
||||
@@ -212,6 +219,11 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
if labelId < 0 {
|
||||
AbortBadRequest(c, errors.New("invalid label id"))
|
||||
return
|
||||
}
|
||||
|
||||
label, err := query.PhotoLabel(m.ID, uint(labelId))
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -47,15 +47,16 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
if file.FilePrimary {
|
||||
switch {
|
||||
case file.FilePrimary:
|
||||
log.Errorf("photo: cannot unstack primary file")
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
} else if file.FileSidecar {
|
||||
case file.FileSidecar:
|
||||
log.Errorf("photo: cannot unstack sidecar files")
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
} else if file.FileRoot != entity.RootOriginals {
|
||||
case file.FileRoot != entity.RootOriginals:
|
||||
log.Errorf("photo: only originals can be unstacked")
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
@@ -91,15 +92,16 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
||||
|
||||
related, err := unstackFile.RelatedFiles(false)
|
||||
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
} else if related.Len() == 0 {
|
||||
case related.Len() == 0:
|
||||
log.Errorf("photo: found no files for %s (unstack)", clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
} else if related.Main == nil {
|
||||
case related.Main == nil:
|
||||
log.Errorf("photo: found no main media file for %s (unstack)", clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
)
|
||||
@@ -32,8 +31,7 @@ func StopServer(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
var options *config.Options
|
||||
options = conf.Options()
|
||||
options := conf.Options()
|
||||
|
||||
// Trigger restart.
|
||||
//
|
||||
|
||||
@@ -74,7 +74,7 @@ func UploadToService(router *gin.RouterGroup) {
|
||||
alias = file.ShareBase(seq)
|
||||
}
|
||||
|
||||
aliases[key] += 1
|
||||
aliases[key]++
|
||||
|
||||
entity.FirstOrCreateFileShare(entity.NewFileShare(file.ID, m.ID, alias))
|
||||
}
|
||||
|
||||
@@ -100,13 +100,14 @@ func CreateSession(router *gin.RouterGroup) {
|
||||
|
||||
// Check authentication credentials.
|
||||
if err = sess.LogIn(frm, c); err != nil {
|
||||
if sess.GetMethod().IsNot(authn.Method2FA) {
|
||||
switch {
|
||||
case sess.GetMethod().IsNot(authn.Method2FA):
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
} else if errors.Is(err, authn.ErrPasscodeRequired) {
|
||||
case errors.Is(err, authn.ErrPasscodeRequired):
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": 32, "message": i18n.Msg(i18n.ErrPasscodeRequired)})
|
||||
// Return the reserved request rate limit tokens if password is correct, even if the verification code is missing.
|
||||
r.Success()
|
||||
} else {
|
||||
default:
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": http.StatusUnauthorized, "message": i18n.Msg(i18n.ErrInvalidPasscode)})
|
||||
}
|
||||
return
|
||||
@@ -119,17 +120,20 @@ func CreateSession(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
// Save session after successful authentication.
|
||||
if sess, err = get.Session().Save(sess); err != nil {
|
||||
event.AuditErr([]string{clientIp, status.Error(err)})
|
||||
switch saved, saveErr := get.Session().Save(sess); {
|
||||
case saveErr != nil:
|
||||
event.AuditErr([]string{clientIp, status.Error(saveErr)})
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)})
|
||||
return
|
||||
} else if sess == nil {
|
||||
case saved == nil:
|
||||
c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrUnexpected)})
|
||||
return
|
||||
} else if isNew {
|
||||
event.AuditInfo([]string{clientIp, "session %s", "created"}, sess.RefID)
|
||||
} else {
|
||||
event.AuditInfo([]string{clientIp, "session %s", "updated"}, sess.RefID)
|
||||
case isNew:
|
||||
event.AuditInfo([]string{clientIp, "session %s", "created"}, saved.RefID)
|
||||
sess = saved
|
||||
default:
|
||||
event.AuditInfo([]string{clientIp, "session %s", "updated"}, saved.RefID)
|
||||
sess = saved
|
||||
}
|
||||
|
||||
// Return the reserved request rate limit tokens after successful authentication.
|
||||
|
||||
@@ -373,7 +373,7 @@ func TestDeleteSession(t *testing.T) {
|
||||
defer conf.SetAuthMode(config.AuthModePublic)
|
||||
|
||||
DeleteSession(router)
|
||||
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1"
|
||||
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token
|
||||
|
||||
r := PerformRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken))
|
||||
assert.Equal(t, http.StatusUnauthorized, r.Code)
|
||||
@@ -399,7 +399,7 @@ func TestDeleteSession(t *testing.T) {
|
||||
|
||||
DeleteSession(router)
|
||||
bobToken := AuthenticateUser(app, router, "bob", "Bobbob123!")
|
||||
aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0"
|
||||
aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0" // #nosec G101 test token
|
||||
|
||||
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(aliceToken), bobToken)
|
||||
assert.Equal(t, http.StatusForbidden, r.Code)
|
||||
@@ -411,7 +411,7 @@ func TestDeleteSession(t *testing.T) {
|
||||
|
||||
DeleteSession(router)
|
||||
aliceToken := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1"
|
||||
bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token
|
||||
|
||||
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken), aliceToken)
|
||||
assert.Equal(t, http.StatusForbidden, r.Code)
|
||||
@@ -423,7 +423,7 @@ func TestDeleteSession(t *testing.T) {
|
||||
|
||||
DeleteSession(router)
|
||||
authToken := AuthenticateUser(app, router, "alice", "Alice123!")
|
||||
deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0"
|
||||
deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0" // #nosec G101 test token
|
||||
|
||||
r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(deleteToken), authToken)
|
||||
assert.Equal(t, http.StatusForbidden, r.Code)
|
||||
|
||||
@@ -36,7 +36,7 @@ func ShareToken(router *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
clientConfig := conf.ClientShare()
|
||||
clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token)
|
||||
clientConfig.SiteUrl += path.Join("s", token)
|
||||
|
||||
uri := conf.LibraryUri("/albums")
|
||||
c.HTML(http.StatusOK, "share.gohtml", gin.H{"shared": gin.H{"token": token, "uri": uri}, "config": clientConfig})
|
||||
@@ -71,7 +71,7 @@ func ShareTokenShared(router *gin.RouterGroup) {
|
||||
|
||||
uid := links[0].ShareUID
|
||||
clientConfig := conf.ClientShare()
|
||||
clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token, uid)
|
||||
clientConfig.SiteUrl += path.Join("s", token, uid)
|
||||
clientConfig.SitePreview = clientConfig.SiteUrl + "/preview"
|
||||
|
||||
if a, err := query.AlbumByUID(uid); err == nil {
|
||||
|
||||
@@ -114,7 +114,7 @@ func SharePreview(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
size, _ := thumb.Sizes[thumb.Tile500]
|
||||
size := thumb.Sizes[thumb.Tile500]
|
||||
|
||||
images := make([]image.Image, 0, len(p))
|
||||
|
||||
@@ -147,6 +147,11 @@ func SharePreview(router *gin.RouterGroup) {
|
||||
|
||||
// Create album preview from thumbnail images.
|
||||
preview, err := frame.Collage(frame.Polaroid, images)
|
||||
if err != nil {
|
||||
log.Warnf("preview collage: %v", err)
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
|
||||
return
|
||||
}
|
||||
|
||||
// Downsize from 1600x900 to 1200x675.
|
||||
preview = imaging.Resize(preview, 1200, 0, imaging.Lanczos)
|
||||
|
||||
@@ -336,20 +336,25 @@ func checkUserPasscodePassword(c *gin.Context, user *entity.User, password strin
|
||||
}
|
||||
|
||||
// Check if user login credentials are valid.
|
||||
if authUser, provider, method, authErr := entity.Auth(f, nil, c); method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired) {
|
||||
authUser, provider, method, authErr := entity.Auth(f, nil, c)
|
||||
|
||||
switch {
|
||||
case method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired):
|
||||
return http.StatusOK, i18n.MsgVerified, nil
|
||||
} else if authErr != nil {
|
||||
case authErr != nil:
|
||||
// Abort if authentication has failed otherwise.
|
||||
return code, msg, authErr
|
||||
} else if authUser == nil {
|
||||
case authUser == nil:
|
||||
// Abort if account was not found.
|
||||
return code, msg, authn.ErrAccountNotFound
|
||||
} else if !authUser.Equal(user) {
|
||||
case !authUser.Equal(user):
|
||||
// Abort if user accounts do not match.
|
||||
return code, msg, authn.ErrUserDoesNotMatch
|
||||
} else if !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault {
|
||||
case !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault:
|
||||
// Abort if e.g. an app password was provided.
|
||||
return code, msg, authn.ErrInvalidCredentials
|
||||
default:
|
||||
return http.StatusOK, i18n.MsgVerified, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,13 +117,14 @@ func UploadUserFiles(router *gin.RouterGroup) {
|
||||
fileType := fs.FileType(baseName)
|
||||
|
||||
// Reject unsupported files and files with extensions that aren't allowed.
|
||||
if fileType == fs.TypeUnknown {
|
||||
switch {
|
||||
case fileType == fs.TypeUnknown:
|
||||
log.Errorf("upload: rejected %s because it has an unsupported file extension", clean.Log(baseName))
|
||||
continue
|
||||
} else if allowedExt.Excludes(fileType.DefaultExt()) {
|
||||
case allowedExt.Excludes(fileType.DefaultExt()):
|
||||
log.Errorf("upload: rejected %s because its extension is not allowed", clean.Log(baseName))
|
||||
continue
|
||||
} else if fileSizeLimit > 0 && file.Size > fileSizeLimit {
|
||||
case fileSizeLimit > 0 && file.Size > fileSizeLimit:
|
||||
log.Errorf("upload: rejected %s because its size exceeds the file size limit", clean.Log(baseName))
|
||||
continue
|
||||
}
|
||||
@@ -203,13 +204,14 @@ func UploadUserFiles(router *gin.RouterGroup) {
|
||||
for _, filename := range uploads {
|
||||
labels, nsfwErr := vision.DetectNSFW([]string{filename}, media.SrcLocal)
|
||||
|
||||
if nsfwErr != nil {
|
||||
switch {
|
||||
case nsfwErr != nil:
|
||||
log.Debug(nsfwErr)
|
||||
continue
|
||||
} else if len(labels) < 1 {
|
||||
case len(labels) < 1:
|
||||
log.Errorf("nsfw: model returned no result")
|
||||
continue
|
||||
} else if labels[0].IsSafe() {
|
||||
case labels[0].IsSafe():
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -74,18 +74,6 @@ func buildZipWithDirsAndFiles(dirs []string, files map[string][]byte) []byte {
|
||||
return zbuf.Bytes()
|
||||
}
|
||||
|
||||
func findUploadedFiles(t *testing.T, base string) []string {
|
||||
t.Helper()
|
||||
var out []string
|
||||
_ = filepath.Walk(base, func(path string, info os.FileInfo, err error) error {
|
||||
if err == nil && !info.IsDir() {
|
||||
out = append(out, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
// findUploadedFilesForToken lists files only under upload subfolders whose name ends with token suffix.
|
||||
func findUploadedFilesForToken(t *testing.T, base string, tokenSuffix string) []string {
|
||||
t.Helper()
|
||||
|
||||
@@ -94,10 +94,10 @@ func GetVideo(router *gin.RouterGroup) {
|
||||
|
||||
// Get video bitrate, codec, and file type.
|
||||
videoFileType := f.Type()
|
||||
videoCodec := f.FileCodec
|
||||
videoBitrate := f.Bitrate()
|
||||
videoFileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
videoContentType := f.ContentType()
|
||||
var videoCodec string
|
||||
|
||||
// If the file has a hybrid photo/video format, try to find and send the embedded video data.
|
||||
if f.MediaType == entity.MediaLive {
|
||||
@@ -201,7 +201,5 @@ func GetVideo(router *gin.RouterGroup) {
|
||||
} else {
|
||||
c.File(videoFileName)
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
@@ -55,15 +55,16 @@ func PostVisionCaption(router *gin.RouterGroup) {
|
||||
// Run inference to generate a caption.
|
||||
result, model, err := vision.GenerateCaption(request.Images, media.SrcRemote)
|
||||
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Errorf("vision: %s (caption)", err)
|
||||
c.JSON(http.StatusBadRequest, vision.NewApiError(request.GetId(), http.StatusBadRequest))
|
||||
return
|
||||
} else if model == nil {
|
||||
case model == nil:
|
||||
log.Errorf("vision: no model specified (caption)")
|
||||
c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError))
|
||||
return
|
||||
} else if result == nil {
|
||||
case result == nil:
|
||||
log.Errorf("vision: no result (caption)")
|
||||
c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError))
|
||||
return
|
||||
|
||||
@@ -166,10 +166,6 @@ func TestPostVisionFace(t *testing.T) {
|
||||
t.Fatal(apiErr)
|
||||
}
|
||||
|
||||
if apiResponse == nil {
|
||||
t.Fatal("api response expected")
|
||||
}
|
||||
|
||||
// t.Logf("error: %s", apiResponse.Err())
|
||||
|
||||
assert.Error(t, apiResponse.Err())
|
||||
|
||||
@@ -117,10 +117,6 @@ func TestPostVisionLabels(t *testing.T) {
|
||||
t.Fatal(apiErr)
|
||||
}
|
||||
|
||||
if apiResponse == nil {
|
||||
t.Fatal("api response expected")
|
||||
}
|
||||
|
||||
t.Logf("error: %s", apiResponse.Err())
|
||||
|
||||
assert.Error(t, apiResponse.Err())
|
||||
|
||||
@@ -132,10 +132,6 @@ func TestPostVisionNsfw(t *testing.T) {
|
||||
t.Fatal(apiErr)
|
||||
}
|
||||
|
||||
if apiResponse == nil {
|
||||
t.Fatal("api response expected")
|
||||
}
|
||||
|
||||
// t.Logf("error: %s", apiResponse.Err())
|
||||
|
||||
assert.Error(t, apiResponse.Err())
|
||||
|
||||
@@ -96,6 +96,7 @@ func ZipCreate(router *gin.RouterGroup) {
|
||||
|
||||
// Create new zip file.
|
||||
var newZipFile *os.File
|
||||
// #nosec G304 zip name derived from request
|
||||
if newZipFile, err = os.Create(zipFileName); err != nil {
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
@@ -124,7 +125,7 @@ func ZipCreate(router *gin.RouterGroup) {
|
||||
alias = file.DownloadName(dlName, seq)
|
||||
}
|
||||
|
||||
aliases[key] += 1
|
||||
aliases[key]++
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil {
|
||||
|
||||
Reference in New Issue
Block a user