Add users and network settings tabs with admin protections.
List and delete accounts with last-admin and Administrators-group guards, and expose editable network settings via the configuration UI.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"codeberg.org/SquidSE/ClusterCanvas/service/internal/auth"
|
||||
"codeberg.org/SquidSE/ClusterCanvas/service/internal/settings"
|
||||
)
|
||||
|
||||
type networkResponse struct {
|
||||
Network settings.NetworkSettings `json:"network"`
|
||||
}
|
||||
|
||||
type putNetworkRequest struct {
|
||||
Network settings.NetworkSettings `json:"network"`
|
||||
}
|
||||
|
||||
func networkGetHandler(configDir string) http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
settingsPayload, err := loadSettingsOrDefault(configDir)
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(writer, http.StatusOK, networkResponse{
|
||||
Network: settings.EffectiveNetwork(settingsPayload.Network),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func networkPutHandler(configDir string) http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
if err := authorizeNetworkWrite(request); err != nil {
|
||||
writeJSON(writer, http.StatusForbidden, apiErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var payload putNetworkRequest
|
||||
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
|
||||
writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: "invalid JSON body"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateNetworkSettings(payload.Network); err != nil {
|
||||
writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
network := settings.EffectiveNetwork(payload.Network)
|
||||
clientIP := auth.ClientIP(request)
|
||||
if wouldLockOut, reason := auth.WouldLockOut(clientIP, network); wouldLockOut {
|
||||
message := "these settings would lock out your current IP"
|
||||
if reason != "" {
|
||||
message = message + ": " + reason
|
||||
}
|
||||
writeJSON(writer, http.StatusBadRequest, apiErrorResponse{Error: message})
|
||||
return
|
||||
}
|
||||
|
||||
settingsPayload, err := loadSettingsOrDefault(configDir)
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
settingsPayload.Network = network
|
||||
if err := settings.SaveSettings(configDir, settingsPayload); err != nil {
|
||||
writeJSON(writer, http.StatusInternalServerError, apiErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(writer, http.StatusOK, networkResponse{Network: network})
|
||||
}
|
||||
}
|
||||
|
||||
func authorizeNetworkWrite(_ *http.Request) error {
|
||||
// TODO: integrate authz middleware.
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNetworkSettings(network settings.NetworkSettings) error {
|
||||
if err := auth.ValidateListenAddress(network.ListenAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := auth.ValidateHostname(network.PublicHostname); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := auth.ValidateAccessRules(network.Rules); err != nil {
|
||||
return err
|
||||
}
|
||||
switch network.AccessMode {
|
||||
case settings.AccessModeOpen, settings.AccessModeWhitelist, settings.AccessModeBlacklist:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("network.access_mode must be open, whitelist, or blacklist")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user