diff --git a/controllers/server.go b/controllers/server.go index eec9ed06..27780be4 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -377,15 +377,6 @@ func identifySettingsUpdateAction(old, new models.ServerSettings) models.Action return models.UpdateMonitoringAndDebuggingSettings } - if old.Theme != new.Theme { - return models.UpdateDisplaySettings - } - - if old.TextSize != new.TextSize || - old.ReducedMotion != new.ReducedMotion { - return models.UpdateAccessibilitySettings - } - if old.EmailSenderAddr != new.EmailSenderAddr || old.EmailSenderUser != new.EmailSenderUser || old.EmailSenderPassword != new.EmailSenderPassword || diff --git a/controllers/user.go b/controllers/user.go index cf15eedf..a2650847 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -49,6 +49,8 @@ func userHandlers(r *mux.Router) { r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet) r.HandleFunc("/api/users/{username}/enable", logic.SecurityCheck(true, http.HandlerFunc(enableUserAccount))).Methods(http.MethodPost) r.HandleFunc("/api/users/{username}/disable", logic.SecurityCheck(true, http.HandlerFunc(disableUserAccount))).Methods(http.MethodPost) + r.HandleFunc("/api/users/{username}/settings", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserSettings)))).Methods(http.MethodGet) + r.HandleFunc("/api/users/{username}/settings", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(updateUserSettings)))).Methods(http.MethodPut) r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet) r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet) r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet) @@ -817,6 +819,46 @@ func disableUserAccount(w http.ResponseWriter, r *http.Request) { logic.ReturnSuccessResponse(w, r, "user account disabled") } +// @Summary Get a user's preferences and settings +// @Router /api/users/{username}/settings [get] +// @Tags Users +// @Param username path string true "Username of the user" +// @Success 200 {object} models.SuccessResponse +func getUserSettings(w http.ResponseWriter, r *http.Request) { + userID := r.Header.Get("user") + userSettings := logic.GetUserSettings(userID) + logic.ReturnSuccessResponseWithJson(w, r, userSettings, "fetched user settings") +} + +// @Summary Update a user's preferences and settings +// @Router /api/users/{username}/settings [put] +// @Tags Users +// @Param username path string true "Username of the user" +// @Success 200 {object} models.SuccessResponse +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +func updateUserSettings(w http.ResponseWriter, r *http.Request) { + userID := r.Header.Get("user") + var req models.UserSettings + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + logger.Log(0, "failed to decode request body: ", err.Error()) + err = fmt.Errorf("invalid request body: %v", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + + err = logic.UpsertUserSettings(userID, req) + if err != nil { + err = fmt.Errorf("failed to update user settings: %v", err.Error()) + logger.Log(0, err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + + logic.ReturnSuccessResponseWithJson(w, r, req, "updated user settings") +} + // swagger:route GET /api/v1/users user getUserV1 // // Get an individual user with role info. @@ -1383,6 +1425,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) { } } } + _ = logic.DeleteUserInvite(user.UserName) mq.PublishPeerUpdate(false) if servercfg.IsDNSMode() { logic.SetDNS() diff --git a/logic/settings.go b/logic/settings.go index 8428be21..9a5df12a 100644 --- a/logic/settings.go +++ b/logic/settings.go @@ -15,11 +15,17 @@ import ( "github.com/gravitl/netmaker/servercfg" ) -var serverSettingsDBKey = "server_cfg" +var ServerSettingsDBKey = "server_cfg" var SettingsMutex = &sync.RWMutex{} +var defaultUserSettings = models.UserSettings{ + TextSize: "16", + Theme: models.Dark, + ReducedMotion: false, +} + func GetServerSettings() (s models.ServerSettings) { - data, err := database.FetchRecord(database.SERVER_SETTINGS, serverSettingsDBKey) + data, err := database.FetchRecord(database.SERVER_SETTINGS, ServerSettingsDBKey) if err != nil { return } @@ -42,13 +48,47 @@ func UpsertServerSettings(s models.ServerSettings) error { if err != nil { return err } - err = database.Insert(serverSettingsDBKey, string(data), database.SERVER_SETTINGS) + err = database.Insert(ServerSettingsDBKey, string(data), database.SERVER_SETTINGS) if err != nil { return err } return nil } +func GetUserSettings(userID string) models.UserSettings { + data, err := database.FetchRecord(database.SERVER_SETTINGS, userID) + if err != nil { + return defaultUserSettings + } + var userSettings models.UserSettings + err = json.Unmarshal([]byte(data), &userSettings) + if err != nil { + return defaultUserSettings + } + + return userSettings +} + +func UpsertUserSettings(userID string, userSettings models.UserSettings) error { + if userSettings.TextSize == "" { + userSettings.TextSize = "16" + } + + if userSettings.Theme == "" { + userSettings.Theme = models.Dark + } + + data, err := json.Marshal(userSettings) + if err != nil { + return err + } + return database.Insert(userID, string(data), database.SERVER_SETTINGS) +} + +func DeleteUserSettings(userID string) error { + return database.DeleteRecord(database.SERVER_SETTINGS, userID) +} + func ValidateNewSettings(req models.ServerSettings) bool { // TODO: add checks for different fields if req.JwtValidityDuration > 525600 || req.JwtValidityDuration < 5 { @@ -87,9 +127,6 @@ func GetServerSettingsFromEnv() (s models.ServerSettings) { DefaultDomain: servercfg.GetDefaultDomain(), Stun: servercfg.IsStunEnabled(), StunServers: servercfg.GetStunServers(), - TextSize: "16", - Theme: models.Dark, - ReducedMotion: false, } return diff --git a/migrate/migrate.go b/migrate/migrate.go index 22869ccd..d21052c7 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -25,7 +25,7 @@ import ( // Run - runs all migrations func Run() { - settings() + migrateSettings() updateEnrollmentKeys() assignSuperAdmin() createDefaultTagsAndPolicies() @@ -635,8 +635,8 @@ func migrateToEgressV1() { } } -func settings() { - _, err := database.FetchRecords(database.SERVER_SETTINGS) +func migrateSettings() { + _, err := database.FetchRecord(database.SERVER_SETTINGS, logic.ServerSettingsDBKey) if database.IsEmptyRecord(err) { logic.UpsertServerSettings(logic.GetServerSettingsFromEnv()) } diff --git a/models/events.go b/models/events.go index 4a6e1603..d6529b99 100644 --- a/models/events.go +++ b/models/events.go @@ -29,8 +29,6 @@ const ( UpdateClientSettings Action = "UPDATE_CLIENT_SETTINGS" UpdateAuthenticationSecuritySettings Action = "UPDATE_AUTHENTICATION_SECURITY_SETTINGS" UpdateMonitoringAndDebuggingSettings Action = "UPDATE_MONITORING_AND_DEBUGGING_SETTINGS" - UpdateDisplaySettings Action = "UPDATE_DISPLAY_SETTINGS" - UpdateAccessibilitySettings Action = "UPDATE_ACCESSIBILITY_SETTINGS" UpdateSMTPSettings Action = "UPDATE_EMAIL_SETTINGS" UpdateIDPSettings Action = "UPDATE_IDP_SETTINGS" ) diff --git a/models/settings.go b/models/settings.go index 0328886a..b96219ee 100644 --- a/models/settings.go +++ b/models/settings.go @@ -45,8 +45,11 @@ type ServerSettings struct { DefaultDomain string `json:"default_domain"` Stun bool `json:"stun"` StunServers string `json:"stun_servers"` - Theme Theme `json:"theme"` - TextSize string `json:"text_size"` - ReducedMotion bool `json:"reduced_motion"` AuditLogsRetentionPeriodInDays int `json:"audit_logs_retention_period"` } + +type UserSettings struct { + Theme Theme `json:"theme"` + TextSize string `json:"text_size"` + ReducedMotion bool `json:"reduced_motion"` +}