feat: more role backend and admin/writer roles additions and adjustments
This commit is contained in:
1 parent
8c6c6456da
commit
de142f6c5f
131 files changed
+2663
-435
No files matched your search
@@ -64,6 +64,7 @@ type UseCases struct {
|
||||
DeleteAuthorUseCase *useCases.DeleteAuthorUseCase
|
||||
GetMyAuthorUseCase *useCases.GetMyAuthorUseCase
|
||||
GetMyAccessUseCase *useCases.GetMyAccessUseCase
|
||||
CheckPermissionUseCase *useCases.CheckPermissionUseCase
|
||||
ListPermissionsUseCase *useCases.ListPermissionsUseCase
|
||||
ListSitesUseCase *useCases.ListSitesUseCase
|
||||
CreateSiteUseCase *useCases.CreateSiteUseCase
|
||||
@@ -372,6 +373,7 @@ func getUseCases(repos *Repositories, imageStorage domainImage.Storage, settings
|
||||
DeleteAuthorUseCase: useCases.NewDeleteAuthorUseCase(repos.AuthorRepo, recorder),
|
||||
GetMyAuthorUseCase: useCases.NewGetMyAuthorUseCase(repos.AuthorRepo),
|
||||
GetMyAccessUseCase: useCases.NewGetMyAccessUseCase(repos.RoleRepo),
|
||||
CheckPermissionUseCase: useCases.NewCheckPermissionUseCase(repos.RoleRepo),
|
||||
ListPermissionsUseCase: useCases.NewListPermissionsUseCase(),
|
||||
ListSitesUseCase: useCases.NewListSitesUseCase(repos.SiteRepo),
|
||||
CreateSiteUseCase: useCases.NewCreateSiteUseCase(repos.SiteRepo, repos.ImageRepo, recorder),
|
||||
@@ -426,7 +428,7 @@ func getUseCases(repos *Repositories, imageStorage domainImage.Storage, settings
|
||||
RecordRunningInfoUseCase: useCases.NewRecordRunningInfoUseCase(repos.RunningInfo, settings.Version, os.Getpid(), time.Now),
|
||||
CreateAPIKeyUseCase: useCases.NewCreateAPIKeyUseCase(repos.APIKeyRepo, keyGenerator, time.Now, recorder),
|
||||
ListAPIKeysUseCase: useCases.NewListAPIKeysUseCase(repos.APIKeyRepo, repos.UserRepo, time.Now),
|
||||
RevokeAPIKeyUseCase: useCases.NewRevokeAPIKeyUseCase(repos.APIKeyRepo, repos.UserRepo, time.Now, recorder),
|
||||
RevokeAPIKeyUseCase: useCases.NewRevokeAPIKeyUseCase(repos.APIKeyRepo, time.Now, recorder),
|
||||
AuthenticateAPIKeyUseCase: useCases.NewAuthenticateAPIKeyUseCase(repos.APIKeyRepo, keyGenerator, repos.UserRepo, time.Now),
|
||||
ListActivityUseCase: useCases.NewListActivityUseCase(repos.AuditRepo, repos.UserRepo, repos.ArticleRepo, repos.ImageRepo, repos.APIKeyRepo, time.Now, repos.SiteRepo, repos.AuthorRepo),
|
||||
GetActivitySummaryUseCase: useCases.NewGetActivitySummaryUseCase(repos.AuditRepo, time.Now),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package activity
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/access"
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/audit"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/domain/role"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -134,9 +135,8 @@ func writeActivityError(w http.ResponseWriter, err error) {
|
||||
case errors.Is(err, audit.ErrInvalidFilter):
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
case errors.Is(err, audit.ErrActorNotAllowed):
|
||||
http.Error(w, apperr.OwnerOrAdminRequiredErrorMessage, http.StatusForbidden)
|
||||
case errors.Is(err, user.ErrUserNotFound):
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
access.WriteMissingPermission(w, role.KeyActivityView)
|
||||
case access.WriteIfMissingPermission(w, err):
|
||||
default:
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
activityQuery := toActivityQuery(query.filterQuery)
|
||||
activityQuery.Page, activityQuery.Size = query.Page, query.Size
|
||||
page, err := h.ListUseCase.ListActivity(useCases.ListActivityCommand{ViewerID: viewerId, Query: activityQuery})
|
||||
page, err := h.ListUseCase.ListActivity(useCases.ListActivityCommand{ViewerID: viewerId, Grants: access.Grants(r), Query: activityQuery})
|
||||
if err != nil {
|
||||
writeActivityError(w, err)
|
||||
return
|
||||
@@ -220,7 +220,13 @@ func (h *Handler) Export(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
prepared, err := h.ExportUseCase.Prepare(toActivityQuery(query))
|
||||
viewerId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
prepared, err := h.ExportUseCase.Prepare(useCases.ExportActivityCommand{ViewerID: viewerId, Grants: access.Grants(r), Query: toActivityQuery(query)})
|
||||
if err != nil {
|
||||
writeActivityError(w, err)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/access"
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
@@ -109,8 +110,11 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
entries, err := h.ListUseCase.ListAPIKeys(userId)
|
||||
entries, err := h.ListUseCase.ListAPIKeys(userId, access.Grants(r))
|
||||
if err != nil {
|
||||
if access.WriteIfMissingPermission(w, err) {
|
||||
return
|
||||
}
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
@@ -161,8 +165,9 @@ func (h *Handler) Revoke(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.RevokeUseCase.RevokeAPIKey(r.Context(), userId, uint32(id)); err != nil {
|
||||
if err := h.RevokeUseCase.RevokeAPIKey(r.Context(), userId, uint32(id), access.Grants(r)); err != nil {
|
||||
switch {
|
||||
case access.WriteIfMissingPermission(w, err):
|
||||
case errors.Is(err, domain.ErrAPIKeyNotFound):
|
||||
http.Error(w, apperr.APIKeyNotFoundErrorMessage, http.StatusNotFound)
|
||||
case errors.Is(err, user.ErrUserNotFound):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package author
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/access"
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
@@ -33,11 +34,10 @@ type Handler struct {
|
||||
CreateMyUseCase *useCases.CreateMyAuthorUseCase
|
||||
UpdateUseCase *useCases.UpdateAuthorUseCase
|
||||
DeleteUseCase *useCases.DeleteAuthorUseCase
|
||||
GetUserUseCase *useCases.GetUserUseCase
|
||||
}
|
||||
|
||||
func NewHandler(list *useCases.ListAuthorsUseCase, create *useCases.CreateAuthorUseCase, createMy *useCases.CreateMyAuthorUseCase, update *useCases.UpdateAuthorUseCase, deleteUseCase *useCases.DeleteAuthorUseCase, getUser *useCases.GetUserUseCase) *Handler {
|
||||
return &Handler{ListUseCase: list, CreateUseCase: create, CreateMyUseCase: createMy, UpdateUseCase: update, DeleteUseCase: deleteUseCase, GetUserUseCase: getUser}
|
||||
func NewHandler(list *useCases.ListAuthorsUseCase, create *useCases.CreateAuthorUseCase, createMy *useCases.CreateMyAuthorUseCase, update *useCases.UpdateAuthorUseCase, deleteUseCase *useCases.DeleteAuthorUseCase) *Handler {
|
||||
return &Handler{ListUseCase: list, CreateUseCase: create, CreateMyUseCase: createMy, UpdateUseCase: update, DeleteUseCase: deleteUseCase}
|
||||
}
|
||||
|
||||
type socialLinkRequest struct {
|
||||
@@ -180,6 +180,7 @@ func writeJSON(w http.ResponseWriter, status int, payload any) {
|
||||
|
||||
func writeAuthorError(w http.ResponseWriter, err error) {
|
||||
switch {
|
||||
case access.WriteIfMissingPermission(w, err):
|
||||
case errors.Is(err, domain.ErrAuthorNotFound):
|
||||
http.Error(w, apperr.AuthorNotFoundErrorMessage, http.StatusNotFound)
|
||||
case errors.Is(err, domain.ErrSlugTaken):
|
||||
@@ -226,18 +227,6 @@ func writeValidationError(w http.ResponseWriter, err error) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (h *Handler) currentUser(r *http.Request) (user.User, bool) {
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
return user.User{}, false
|
||||
}
|
||||
current, err := h.GetUserUseCase.GetUser(userId)
|
||||
if err != nil {
|
||||
return user.User{}, false
|
||||
}
|
||||
return current, true
|
||||
}
|
||||
|
||||
type listQuery struct {
|
||||
Page int `validate:"gte=1"`
|
||||
Size int `validate:"oneof=5 25 50 100"`
|
||||
@@ -277,8 +266,7 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
current, _ := h.currentUser(r)
|
||||
page, err := h.ListUseCase.List(useCases.ListAuthorsCommand{Q: query.Q, Page: query.Page, Size: query.Size, CallerRole: current.Role})
|
||||
page, err := h.ListUseCase.List(useCases.ListAuthorsCommand{Q: query.Q, Page: query.Page, Size: query.Size, Grants: access.Grants(r)})
|
||||
if err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -351,12 +339,12 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
current, ok := h.currentUser(r)
|
||||
callerID, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
updated, err := h.UpdateUseCase.Update(r.Context(), id, useCases.AuthorCaller{UserID: current.ID, Role: current.Role}, request.toPatch())
|
||||
updated, err := h.UpdateUseCase.Update(r.Context(), id, useCases.AuthorCaller{UserID: callerID, Grants: access.Grants(r)}, request.toPatch())
|
||||
if err != nil {
|
||||
writeAuthorError(w, err)
|
||||
return
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package update
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/access"
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/role"
|
||||
domain "RenewCMS/internal/domain/update"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -21,29 +22,23 @@ type Handler struct {
|
||||
CheckForUpdateUseCase *useCases.CheckForUpdateUseCase
|
||||
StartUpdateDownloadUseCase *useCases.StartUpdateDownloadUseCase
|
||||
RequestRestartUseCase *useCases.RequestRestartUseCase
|
||||
GetUserUseCase *useCases.GetUserUseCase
|
||||
}
|
||||
|
||||
type restartRequest struct {
|
||||
Kind *string `json:"kind" validate:"omitempty,oneof=update restart"`
|
||||
}
|
||||
|
||||
func NewHandler(getStatus *useCases.GetUpdateStatusUseCase, checkForUpdate *useCases.CheckForUpdateUseCase, startDownload *useCases.StartUpdateDownloadUseCase, requestRestart *useCases.RequestRestartUseCase, getUser *useCases.GetUserUseCase) *Handler {
|
||||
func NewHandler(getStatus *useCases.GetUpdateStatusUseCase, checkForUpdate *useCases.CheckForUpdateUseCase, startDownload *useCases.StartUpdateDownloadUseCase, requestRestart *useCases.RequestRestartUseCase) *Handler {
|
||||
return &Handler{
|
||||
GetUpdateStatusUseCase: getStatus,
|
||||
CheckForUpdateUseCase: checkForUpdate,
|
||||
StartUpdateDownloadUseCase: startDownload,
|
||||
RequestRestartUseCase: requestRestart,
|
||||
GetUserUseCase: getUser,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
isOwner, ok := h.isOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
writeStatus(w, http.StatusOK, h.GetUpdateStatusUseCase.GetStatus(isOwner))
|
||||
writeStatus(w, http.StatusOK, h.GetUpdateStatusUseCase.GetStatus(canApply(r)))
|
||||
}
|
||||
|
||||
func (h *Handler) Check(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -51,11 +46,7 @@ func (h *Handler) Check(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
isOwner, ok := h.isOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
writeStatus(w, http.StatusOK, h.GetUpdateStatusUseCase.GetStatus(isOwner))
|
||||
writeStatus(w, http.StatusOK, h.GetUpdateStatusUseCase.GetStatus(canApply(r)))
|
||||
}
|
||||
|
||||
func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -105,22 +96,8 @@ func decodeRestartKind(w http.ResponseWriter, r *http.Request) (string, bool) {
|
||||
return *request.Kind, true
|
||||
}
|
||||
|
||||
func (h *Handler) isOwner(w http.ResponseWriter, r *http.Request) (bool, bool) {
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return false, false
|
||||
}
|
||||
currentUser, err := h.GetUserUseCase.GetUser(userId)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return false, false
|
||||
}
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return false, false
|
||||
}
|
||||
return currentUser.Role == user.RoleOwner, true
|
||||
func canApply(r *http.Request) bool {
|
||||
return access.Grants(r).Holds(role.KeyApplyUpdates)
|
||||
}
|
||||
|
||||
func writeDownloadError(w http.ResponseWriter, err error) {
|
||||
|
||||
@@ -77,39 +77,3 @@ func RequireVerifiedUser(getUser *useCases.GetUserUseCase) func(http.Handler) ht
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func RequireOwner(getUser *useCases.GetUserUseCase) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser, ok := resolveCurrentUser(getUser, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if currentUser.Role != user.RoleOwner {
|
||||
http.Error(w, apperr.OwnerRequiredErrorMessage, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func RequireOwnerOrAdmin(getUser *useCases.GetUserUseCase) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser, ok := resolveCurrentUser(getUser, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if currentUser.Role != user.RoleOwner && currentUser.Role != user.RoleAdmin {
|
||||
http.Error(w, apperr.OwnerOrAdminRequiredErrorMessage, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/access"
|
||||
"RenewCMS/internal/domain/role"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type grantCheck func(account user.User, key string) (role.GrantSet, error)
|
||||
|
||||
func RequirePermission(getUser *useCases.GetUserUseCase, check *useCases.CheckPermissionUseCase, key string) func(http.Handler) http.Handler {
|
||||
return enforce(getUser, check.Require, key)
|
||||
}
|
||||
|
||||
func RequireFullScope(getUser *useCases.GetUserUseCase, check *useCases.CheckPermissionUseCase, key string) func(http.Handler) http.Handler {
|
||||
return enforce(getUser, check.RequireAll, key)
|
||||
}
|
||||
|
||||
func enforce(getUser *useCases.GetUserUseCase, verify grantCheck, key string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser, ok := resolveCurrentUser(getUser, w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
grants, err := verify(currentUser, key)
|
||||
if err != nil {
|
||||
if !access.WriteIfMissingPermission(w, err) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(access.NewContext(r.Context(), grants)))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/internal/domain/role"
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type grantsContextKey struct{}
|
||||
|
||||
func NewContext(ctx context.Context, grants role.GrantSet) context.Context {
|
||||
return context.WithValue(ctx, grantsContextKey{}, grants)
|
||||
}
|
||||
|
||||
func Grants(r *http.Request) role.GrantSet {
|
||||
grants, _ := r.Context().Value(grantsContextKey{}).(role.GrantSet)
|
||||
return grants
|
||||
}
|
||||
|
||||
func WriteMissingPermission(w http.ResponseWriter, key string) {
|
||||
w.Header().Set(apperr.MissingPermissionHeader, key)
|
||||
http.Error(w, apperr.MissingPermissionErrorMessage, http.StatusForbidden)
|
||||
}
|
||||
|
||||
func WriteIfMissingPermission(w http.ResponseWriter, err error) bool {
|
||||
var missing role.MissingPermissionError
|
||||
if !errors.As(err, &missing) {
|
||||
return false
|
||||
}
|
||||
WriteMissingPermission(w, missing.Key)
|
||||
return true
|
||||
}
|
||||
@@ -24,7 +24,6 @@ const InviteNotFoundErrorMessage = "Invite not found"
|
||||
const InviteAlreadyAcceptedErrorMessage = "This invite has already been accepted."
|
||||
const InviteInvalidErrorMessage = "This invite link doesn't work."
|
||||
const InviteExpiredErrorMessage = "This invite has expired."
|
||||
const OwnerRequiredErrorMessage = "Only the owner can do this."
|
||||
const UsernameTakenErrorMessage = "That username is taken. Try another."
|
||||
const PasswordTooLongErrorMessage = "The password must be 72 bytes or fewer."
|
||||
const MediaQueryErrorMessage = "The media query is invalid."
|
||||
@@ -39,8 +38,9 @@ const UpdateRestartMismatchErrorMessage = "The update state changed. Reload the
|
||||
const ArticleBodyFormatErrorMessage = "The format must be one of: markdown, html, json."
|
||||
const APIKeyNotFoundErrorMessage = "API key not found"
|
||||
const APIKeyExpiryErrorMessage = "The expiry must be a date in the future."
|
||||
const MissingPermissionErrorMessage = "missing_permission"
|
||||
const MissingPermissionHeader = "X-Missing-Permission"
|
||||
const SessionRequiredErrorMessage = "This action needs a real session."
|
||||
const OwnerOrAdminRequiredErrorMessage = "Only owners and admins can do this."
|
||||
const PurgeConflictErrorMessage = "The library changed, so nothing was purged. Check the new summary and try again."
|
||||
const PurgeBusyErrorMessage = "A purge is already running."
|
||||
const PurgeIntervalErrorMessage = "Enter a whole number of days between 1 and 365."
|
||||
|
||||
+61
-66
@@ -58,6 +58,14 @@ func (c *UseCases) apiKeyAuth() func(http.Handler) http.Handler {
|
||||
return c.apiKeyAuthMiddleware
|
||||
}
|
||||
|
||||
func (c *UseCases) require(key string) func(http.Handler) http.Handler {
|
||||
return middleware.RequirePermission(c.GetUserUseCase, c.CheckPermissionUseCase, key)
|
||||
}
|
||||
|
||||
func (c *UseCases) requireAll(key string) func(http.Handler) http.Handler {
|
||||
return middleware.RequireFullScope(c.GetUserUseCase, c.CheckPermissionUseCase, key)
|
||||
}
|
||||
|
||||
func NewArticleRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Use(container.apiKeyAuth())
|
||||
@@ -149,10 +157,10 @@ func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings,
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Patch("/me", userHandler.UpdateMe)
|
||||
r.Get("/", inviteHandler.ListUsers)
|
||||
r.With(container.require("users.view")).Get("/", inviteHandler.ListUsers)
|
||||
r.Get("/lookup", lookupHandler.Lookup)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Use(container.require("users.invite"))
|
||||
r.Use(middleware.TrustedProxyRealIP(trustedProxies))
|
||||
r.Use(middleware.NewRateLimiter(rateLimit.MaxAttempts, rateLimit.Window))
|
||||
r.Post("/invite", inviteHandler.CreateInvite)
|
||||
@@ -173,6 +181,7 @@ func NewSettingsRouter(container *UseCases, trustedProxies []string) http.Handle
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(container.require("settings.view"))
|
||||
r.Get("/", settingsHandler.GetSettings)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
@@ -183,7 +192,7 @@ func NewSettingsRouter(container *UseCases, trustedProxies []string) http.Handle
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Use(container.require("system.edit_retention"))
|
||||
r.Patch("/", settingsHandler.UpdateSettings)
|
||||
})
|
||||
return r
|
||||
@@ -213,11 +222,10 @@ func NewSiteRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Post("/", siteHandler.Create)
|
||||
r.Patch("/{id}", siteHandler.Update)
|
||||
r.Patch("/{id}/default", siteHandler.SetDefault)
|
||||
r.Delete("/{id}", siteHandler.Delete)
|
||||
r.With(container.require("settings.manage_sites")).Post("/", siteHandler.Create)
|
||||
r.With(container.require("settings.manage_sites")).Patch("/{id}", siteHandler.Update)
|
||||
r.With(container.require("settings.manage_sites")).Patch("/{id}/default", siteHandler.SetDefault)
|
||||
r.With(container.require("settings.delete_site")).Delete("/{id}", siteHandler.Delete)
|
||||
})
|
||||
return r
|
||||
}
|
||||
@@ -225,7 +233,7 @@ func NewSiteRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
func NewAuthorRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
|
||||
authorHandler := author.NewHandler(container.ListAuthorsUseCase, container.CreateAuthorUseCase, container.CreateMyAuthorUseCase, container.UpdateAuthorUseCase, container.DeleteAuthorUseCase, container.GetUserUseCase)
|
||||
authorHandler := author.NewHandler(container.ListAuthorsUseCase, container.CreateAuthorUseCase, container.CreateMyAuthorUseCase, container.UpdateAuthorUseCase, container.DeleteAuthorUseCase)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
@@ -233,6 +241,7 @@ func NewAuthorRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(container.require("authors.view"))
|
||||
r.Get("/", authorHandler.List)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
@@ -244,12 +253,9 @@ func NewAuthorRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Post("/me", authorHandler.CreateMine)
|
||||
r.Patch("/{id}", authorHandler.Update)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Post("/", authorHandler.Create)
|
||||
r.Delete("/{id}", authorHandler.Delete)
|
||||
})
|
||||
r.With(container.require("authors.edit")).Patch("/{id}", authorHandler.Update)
|
||||
r.With(container.require("authors.create")).Post("/", authorHandler.Create)
|
||||
r.With(container.require("authors.delete")).Delete("/{id}", authorHandler.Delete)
|
||||
})
|
||||
return r
|
||||
}
|
||||
@@ -268,14 +274,11 @@ func NewContentTypeRouter(container *UseCases, trustedProxies []string) http.Han
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", contentTypeHandler.List)
|
||||
r.Get("/{key}", contentTypeHandler.Get)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Post("/", contentTypeHandler.Create)
|
||||
r.Put("/{key}", contentTypeHandler.Update)
|
||||
r.Delete("/{key}", contentTypeHandler.Delete)
|
||||
})
|
||||
r.With(container.require("schema.view")).Get("/", contentTypeHandler.List)
|
||||
r.With(container.require("schema.view")).Get("/{key}", contentTypeHandler.Get)
|
||||
r.With(container.require("schema.edit")).Post("/", contentTypeHandler.Create)
|
||||
r.With(container.require("schema.edit")).Put("/{key}", contentTypeHandler.Update)
|
||||
r.With(container.require("schema.delete")).Delete("/{key}", contentTypeHandler.Delete)
|
||||
r.Get("/{key}/entry", entryHandler.List)
|
||||
r.Post("/{key}/entry", entryHandler.Create)
|
||||
r.Get("/{key}/entry/{id}", entryHandler.Get)
|
||||
@@ -296,7 +299,7 @@ func NewBuiltinTypeRouter(container *UseCases, trustedProxies []string) http.Han
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Use(container.require("schema.view"))
|
||||
r.Get("/", builtinTypeHandler.List)
|
||||
r.Get("/{key}", builtinTypeHandler.Get)
|
||||
return r
|
||||
@@ -314,12 +317,9 @@ func NewActivityRouter(container *UseCases, trustedProxies []string) http.Handle
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", activityHandler.List)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Get("/summary", activityHandler.Summary)
|
||||
r.Get("/export", activityHandler.Export)
|
||||
})
|
||||
r.With(container.require("activity.view")).Get("/", activityHandler.List)
|
||||
r.With(container.requireAll("activity.view")).Get("/summary", activityHandler.Summary)
|
||||
r.With(container.require("activity.export"), container.require("activity.view")).Get("/export", activityHandler.Export)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ const (
|
||||
func NewSystemRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
|
||||
updateHandler := update.NewHandler(container.GetUpdateStatusUseCase, container.CheckForUpdateUseCase, container.StartUpdateDownloadUseCase, container.RequestRestartUseCase, container.GetUserUseCase)
|
||||
updateHandler := update.NewHandler(container.GetUpdateStatusUseCase, container.CheckForUpdateUseCase, container.StartUpdateDownloadUseCase, container.RequestRestartUseCase)
|
||||
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
@@ -341,19 +341,19 @@ func NewSystemRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/update", updateHandler.GetStatus)
|
||||
r.With(container.require("settings.view")).Get("/update", updateHandler.GetStatus)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Use(container.require("system.apply_updates"))
|
||||
r.Use(middleware.NewUserRateLimiter(updateCheckMaxAttempts, updateCheckWindow))
|
||||
r.Post("/update/check", updateHandler.Check)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Use(container.require("system.apply_updates"))
|
||||
r.Use(middleware.NewUserRateLimiter(updateDownloadMaxAttempts, updateDownloadWindow))
|
||||
r.Post("/update/download", updateHandler.Download)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
r.Use(container.require("system.apply_updates"))
|
||||
r.Post("/update/restart", updateHandler.Restart)
|
||||
})
|
||||
return r
|
||||
@@ -370,9 +370,9 @@ func NewAPIKeyRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Post("/", apiKeyHandler.Create)
|
||||
r.Get("/", apiKeyHandler.List)
|
||||
r.Delete("/{id}", apiKeyHandler.Revoke)
|
||||
r.With(container.require("apikeys.create")).Post("/", apiKeyHandler.Create)
|
||||
r.With(container.require("apikeys.view")).Get("/", apiKeyHandler.List)
|
||||
r.With(container.require("apikeys.revoke")).Delete("/{id}", apiKeyHandler.Revoke)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -402,28 +402,24 @@ func NewMediaRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Get("/purge-settings", mediaHandler.GetPurgeSettings)
|
||||
r.Patch("/purge-settings", mediaHandler.UpdatePurgeSettings)
|
||||
r.Get("/purge-preview", mediaHandler.PreviewPurge)
|
||||
r.Post("/purge", mediaHandler.RunPurge)
|
||||
})
|
||||
r.With(container.require("settings.view")).Get("/purge-settings", mediaHandler.GetPurgeSettings)
|
||||
r.With(container.require("settings.edit_general")).Patch("/purge-settings", mediaHandler.UpdatePurgeSettings)
|
||||
r.With(container.requireAll("media.delete")).Get("/purge-preview", mediaHandler.PreviewPurge)
|
||||
r.With(container.requireAll("media.delete")).Post("/purge", mediaHandler.RunPurge)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RejectAPIKey)
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Get("/{id}/variants", mediaHandler.ListVariants)
|
||||
r.Post("/{id}/variants", mediaHandler.GenerateAllVariants)
|
||||
r.Post("/{id}/variants/{name}", mediaHandler.GenerateVariant)
|
||||
r.Delete("/{id}/variants/{name}", mediaHandler.PurgeVariant)
|
||||
r.With(container.require("media.view")).Get("/{id}/variants", mediaHandler.ListVariants)
|
||||
r.With(container.require("settings.edit_general")).Post("/{id}/variants", mediaHandler.GenerateAllVariants)
|
||||
r.With(container.require("settings.edit_general")).Post("/{id}/variants/{name}", mediaHandler.GenerateVariant)
|
||||
r.With(container.require("settings.edit_general")).Delete("/{id}/variants/{name}", mediaHandler.PurgeVariant)
|
||||
})
|
||||
r.Get("/", mediaHandler.List)
|
||||
r.Post("/", mediaHandler.Upload)
|
||||
r.Post("/merge", mediaHandler.Merge)
|
||||
r.Patch("/{id}", mediaHandler.Rename)
|
||||
r.Delete("/{id}", mediaHandler.Delete)
|
||||
r.Get("/{id}", mediaHandler.Get)
|
||||
r.Get("/{id}/usage", mediaHandler.Usage)
|
||||
r.With(container.require("media.view")).Get("/", mediaHandler.List)
|
||||
r.With(container.require("media.upload")).Post("/", mediaHandler.Upload)
|
||||
r.With(container.requireAll("media.delete")).Post("/merge", mediaHandler.Merge)
|
||||
r.With(container.require("media.upload")).Patch("/{id}", mediaHandler.Rename)
|
||||
r.With(container.requireAll("media.delete")).Delete("/{id}", mediaHandler.Delete)
|
||||
r.With(container.require("media.view")).Get("/{id}", mediaHandler.Get)
|
||||
r.With(container.require("media.view")).Get("/{id}/usage", mediaHandler.Usage)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -438,15 +434,14 @@ func NewImageVariantRouter(container *UseCases, trustedProxies []string) http.Ha
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
r.Get("/", variantHandler.List)
|
||||
r.Post("/", variantHandler.Create)
|
||||
r.Get("/cache", variantHandler.CacheStats)
|
||||
r.Post("/cache/purge", variantHandler.PurgeCache)
|
||||
r.Put("/{name}", variantHandler.Update)
|
||||
r.Patch("/{name}", variantHandler.SetEnabled)
|
||||
r.Delete("/{name}", variantHandler.Delete)
|
||||
r.Post("/{name}/purge", variantHandler.Purge)
|
||||
r.With(container.require("media.view")).Get("/", variantHandler.List)
|
||||
r.With(container.require("settings.edit_general")).Post("/", variantHandler.Create)
|
||||
r.With(container.require("settings.view")).Get("/cache", variantHandler.CacheStats)
|
||||
r.With(container.require("settings.edit_general")).Post("/cache/purge", variantHandler.PurgeCache)
|
||||
r.With(container.require("settings.edit_general")).Put("/{name}", variantHandler.Update)
|
||||
r.With(container.require("settings.edit_general")).Patch("/{name}", variantHandler.SetEnabled)
|
||||
r.With(container.require("settings.edit_general")).Delete("/{name}", variantHandler.Delete)
|
||||
r.With(container.require("settings.edit_general")).Post("/{name}/purge", variantHandler.Purge)
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user