105 lines
3.0 KiB
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)
|
|
}
|
|
}
|