diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 1a07a39..dd938bd 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -57,7 +57,7 @@ func initRouter(db *gorm.DB, appConfigService *service.AppConfigService) { apiGroup := r.Group("/api") controller.NewWebauthnController(apiGroup, jwtAuthMiddleware, middleware.NewRateLimitMiddleware(), webauthnService) controller.NewOidcController(apiGroup, jwtAuthMiddleware, fileSizeLimitMiddleware, oidcService, jwtService) - controller.NewUserController(apiGroup, jwtAuthMiddleware, middleware.NewRateLimitMiddleware(), userService) + controller.NewUserController(apiGroup, jwtAuthMiddleware, middleware.NewRateLimitMiddleware(), userService, appConfigService) controller.NewAppConfigController(apiGroup, jwtAuthMiddleware, appConfigService) controller.NewAuditLogController(apiGroup, auditLogService, jwtAuthMiddleware) controller.NewUserGroupController(apiGroup, jwtAuthMiddleware, userGroupService) diff --git a/backend/internal/common/errors.go b/backend/internal/common/errors.go index 0056ce7..4aad22f 100644 --- a/backend/internal/common/errors.go +++ b/backend/internal/common/errors.go @@ -104,7 +104,6 @@ type ClientIdOrSecretNotProvidedError struct{} func (e *ClientIdOrSecretNotProvidedError) Error() string { return "Client id and secret not provided" } - func (e *ClientIdOrSecretNotProvidedError) HttpStatusCode() int { return http.StatusBadRequest } type WrongFileTypeError struct { @@ -114,7 +113,6 @@ type WrongFileTypeError struct { func (e *WrongFileTypeError) Error() string { return fmt.Sprintf("File must be of type %s", e.ExpectedFileType) } - func (e *WrongFileTypeError) HttpStatusCode() int { return http.StatusBadRequest } type MissingSessionIdError struct{} @@ -122,7 +120,6 @@ type MissingSessionIdError struct{} func (e *MissingSessionIdError) Error() string { return "Missing session id" } - func (e *MissingSessionIdError) HttpStatusCode() int { return http.StatusBadRequest } type ReservedClaimError struct { @@ -132,7 +129,6 @@ type ReservedClaimError struct { func (e *ReservedClaimError) Error() string { return fmt.Sprintf("Claim %s is reserved and can't be used", e.Key) } - func (e *ReservedClaimError) HttpStatusCode() int { return http.StatusBadRequest } type DuplicateClaimError struct { @@ -142,5 +138,11 @@ type DuplicateClaimError struct { func (e *DuplicateClaimError) Error() string { return fmt.Sprintf("Claim %s is already defined", e.Key) } - func (e *DuplicateClaimError) HttpStatusCode() int { return http.StatusBadRequest } + +type AccountEditNotAllowedError struct{} + +func (e *AccountEditNotAllowedError) Error() string { + return "You are not allowed to edit your account" +} +func (e *AccountEditNotAllowedError) HttpStatusCode() int { return http.StatusForbidden } diff --git a/backend/internal/controller/user_controller.go b/backend/internal/controller/user_controller.go index d68daf4..2990e1f 100644 --- a/backend/internal/controller/user_controller.go +++ b/backend/internal/controller/user_controller.go @@ -2,6 +2,7 @@ package controller import ( "github.com/gin-gonic/gin" + "github.com/stonith404/pocket-id/backend/internal/common" "github.com/stonith404/pocket-id/backend/internal/dto" "github.com/stonith404/pocket-id/backend/internal/middleware" "github.com/stonith404/pocket-id/backend/internal/service" @@ -11,9 +12,10 @@ import ( "time" ) -func NewUserController(group *gin.RouterGroup, jwtAuthMiddleware *middleware.JwtAuthMiddleware, rateLimitMiddleware *middleware.RateLimitMiddleware, userService *service.UserService) { +func NewUserController(group *gin.RouterGroup, jwtAuthMiddleware *middleware.JwtAuthMiddleware, rateLimitMiddleware *middleware.RateLimitMiddleware, userService *service.UserService, appConfigService *service.AppConfigService) { uc := UserController{ - UserService: userService, + UserService: userService, + AppConfigService: appConfigService, } group.GET("/users", jwtAuthMiddleware.Add(true), uc.listUsersHandler) @@ -30,7 +32,8 @@ func NewUserController(group *gin.RouterGroup, jwtAuthMiddleware *middleware.Jwt } type UserController struct { - UserService *service.UserService + UserService *service.UserService + AppConfigService *service.AppConfigService } func (uc *UserController) listUsersHandler(c *gin.Context) { @@ -124,6 +127,10 @@ func (uc *UserController) updateUserHandler(c *gin.Context) { } func (uc *UserController) updateCurrentUserHandler(c *gin.Context) { + if uc.AppConfigService.DbConfig.AllowOwnAccountEdit.Value != "true" { + c.Error(&common.AccountEditNotAllowedError{}) + return + } uc.updateUser(c, true) } diff --git a/backend/internal/dto/app_config_dto.go b/backend/internal/dto/app_config_dto.go index 90c5611..1242679 100644 --- a/backend/internal/dto/app_config_dto.go +++ b/backend/internal/dto/app_config_dto.go @@ -12,13 +12,14 @@ type AppConfigVariableDto struct { } type AppConfigUpdateDto struct { - AppName string `json:"appName" binding:"required,min=1,max=30"` - SessionDuration string `json:"sessionDuration" binding:"required"` - EmailsVerified string `json:"emailsVerified" binding:"required"` - EmailEnabled string `json:"emailEnabled" binding:"required"` - SmtHost string `json:"smtpHost"` - SmtpPort string `json:"smtpPort"` - SmtpFrom string `json:"smtpFrom" binding:"omitempty,email"` - SmtpUser string `json:"smtpUser"` - SmtpPassword string `json:"smtpPassword"` + AppName string `json:"appName" binding:"required,min=1,max=30"` + SessionDuration string `json:"sessionDuration" binding:"required"` + EmailsVerified string `json:"emailsVerified" binding:"required"` + AllowOwnAccountEdit string `json:"allowOwnAccountEdit" binding:"required"` + EmailEnabled string `json:"emailEnabled" binding:"required"` + SmtHost string `json:"smtpHost"` + SmtpPort string `json:"smtpPort"` + SmtpFrom string `json:"smtpFrom" binding:"omitempty,email"` + SmtpUser string `json:"smtpUser"` + SmtpPassword string `json:"smtpPassword"` } diff --git a/backend/internal/model/app_config.go b/backend/internal/model/app_config.go index 34c5f6f..f340bb9 100644 --- a/backend/internal/model/app_config.go +++ b/backend/internal/model/app_config.go @@ -11,11 +11,13 @@ type AppConfigVariable struct { type AppConfig struct { AppName AppConfigVariable + SessionDuration AppConfigVariable + EmailsVerified AppConfigVariable + AllowOwnAccountEdit AppConfigVariable + BackgroundImageType AppConfigVariable LogoLightImageType AppConfigVariable LogoDarkImageType AppConfigVariable - SessionDuration AppConfigVariable - EmailsVerified AppConfigVariable EmailEnabled AppConfigVariable SmtpHost AppConfigVariable diff --git a/backend/internal/service/app_config_service.go b/backend/internal/service/app_config_service.go index 08f07fd..feb1db8 100644 --- a/backend/internal/service/app_config_service.go +++ b/backend/internal/service/app_config_service.go @@ -46,6 +46,12 @@ var defaultDbConfig = model.AppConfig{ Type: "bool", DefaultValue: "false", }, + AllowOwnAccountEdit: model.AppConfigVariable{ + Key: "allowOwnAccountEdit", + Type: "bool", + IsPublic: true, + DefaultValue: "true", + }, BackgroundImageType: model.AppConfigVariable{ Key: "backgroundImageType", Type: "string", diff --git a/frontend/src/lib/types/application-configuration.ts b/frontend/src/lib/types/application-configuration.ts index b4d9796..0d17c35 100644 --- a/frontend/src/lib/types/application-configuration.ts +++ b/frontend/src/lib/types/application-configuration.ts @@ -1,5 +1,6 @@ export type AppConfig = { appName: string; + allowOwnAccountEdit: boolean; }; export type AllAppConfig = AppConfig & { diff --git a/frontend/src/routes/settings/account/+page.svelte b/frontend/src/routes/settings/account/+page.svelte index 3eeaa1e..15abd2b 100644 --- a/frontend/src/routes/settings/account/+page.svelte +++ b/frontend/src/routes/settings/account/+page.svelte @@ -3,6 +3,7 @@ import * as Card from '$lib/components/ui/card'; import UserService from '$lib/services/user-service'; import WebAuthnService from '$lib/services/webauthn-service'; + import appConfigStore from '$lib/stores/application-configuration-store'; import type { Passkey } from '$lib/types/passkey.type'; import type { UserCreate } from '$lib/types/user.type'; import { axiosErrorToast, getWebauthnErrorMessage } from '$lib/utils/error-util'; @@ -51,14 +52,16 @@
+ Whether the user should be able to edit their own account details. +
+