Files
member-console/internal/server/operator.go

105 lines
3.0 KiB
Go

package server
import (
"html/template"
"io/fs"
"log/slog"
"net/http"
"git.coopcloud.tech/wiki-cafe/member-console/internal/auth"
"git.coopcloud.tech/wiki-cafe/member-console/internal/embeds"
"git.coopcloud.tech/wiki-cafe/member-console/internal/middleware"
"github.com/spf13/viper"
)
// OperatorHandler handles the main operator page
type OperatorHandler struct {
AuthConfig *auth.Config
Logger *slog.Logger
Templates *template.Template
}
// OperatorHandlerConfig holds configuration for the operator handler
type OperatorHandlerConfig struct {
AuthConfig *auth.Config
Logger *slog.Logger
}
// NewOperatorHandler creates a new OperatorHandler
func NewOperatorHandler(cfg OperatorHandlerConfig) (*OperatorHandler, error) {
// Parse operator template
templateSubFS, err := fs.Sub(embeds.Templates, "templates")
if err != nil {
return nil, err
}
tmpl, err := template.ParseFS(templateSubFS, "operator.html")
if err != nil {
return nil, err
}
return &OperatorHandler{
AuthConfig: cfg.AuthConfig,
Logger: cfg.Logger,
Templates: tmpl,
}, nil
}
// RegisterRoutes registers operator routes
func (h *OperatorHandler) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /operator", h.requireOperatorRole(h.GetOperatorPage))
}
// requireOperatorRole is middleware that checks for the operator role
func (h *OperatorHandler) requireOperatorRole(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !h.AuthConfig.HasRole(r, OperatorRole) {
h.Logger.Warn("operator access denied",
slog.String("path", r.URL.Path),
slog.String("reason", "missing operator-member role"))
http.Error(w, "Forbidden: operator-member role required", http.StatusForbidden)
return
}
next(w, r)
}
}
// OperatorPageData holds data for the operator page template
type OperatorPageData struct {
Name string
Username string
Email string
KeycloakAccountURL string
CSRFToken string
}
// GetOperatorPage handles GET /operator
func (h *OperatorHandler) GetOperatorPage(w http.ResponseWriter, r *http.Request) {
session, err := h.AuthConfig.Store.Get(r, h.AuthConfig.SessionName)
if err != nil {
http.Redirect(w, r, "/login", http.StatusFound)
return
}
name, _ := session.Values["name"].(string)
username, _ := session.Values["username"].(string)
email, _ := session.Values["email"].(string)
keycloakAccountURL := viper.GetString("oidc-idp-issuer-url") + "/account"
csrfToken := middleware.CSRFToken(r)
data := OperatorPageData{
Name: name,
Username: username,
Email: email,
KeycloakAccountURL: keycloakAccountURL,
CSRFToken: csrfToken,
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := h.Templates.ExecuteTemplate(w, "operator.html", data); err != nil {
h.Logger.Error("template execute error", slog.Any("error", err))
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}