259 lines
6.9 KiB
Go
259 lines
6.9 KiB
Go
package http
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/example/storage-appliance/internal/auth"
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// UsersHandler shows the users management page
|
|
func (a *App) UsersHandler(w http.ResponseWriter, r *http.Request) {
|
|
data := templateData(r, map[string]interface{}{
|
|
"Title": "User Management",
|
|
})
|
|
if err := templates.ExecuteTemplate(w, "users", data); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// HXUsersHandler returns HTMX partial for users list
|
|
func (a *App) HXUsersHandler(w http.ResponseWriter, r *http.Request) {
|
|
rbacStore := auth.NewRBACStore(a.DB)
|
|
|
|
// Get all users (simplified - in production, you'd want pagination)
|
|
rows, err := a.DB.QueryContext(r.Context(), `SELECT id, username, created_at FROM users ORDER BY username`)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
type UserWithRoles struct {
|
|
ID string
|
|
Username string
|
|
CreatedAt string
|
|
Roles []auth.Role
|
|
}
|
|
|
|
var users []UserWithRoles
|
|
for rows.Next() {
|
|
var u UserWithRoles
|
|
if err := rows.Scan(&u.ID, &u.Username, &u.CreatedAt); err != nil {
|
|
continue
|
|
}
|
|
roles, _ := rbacStore.GetUserRoles(r.Context(), u.ID)
|
|
u.Roles = roles
|
|
users = append(users, u)
|
|
}
|
|
|
|
data := map[string]interface{}{
|
|
"Users": users,
|
|
}
|
|
if err := templates.ExecuteTemplate(w, "hx_users", data); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// CreateUserHandler creates a new user
|
|
func (a *App) CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
username := r.FormValue("username")
|
|
password := r.FormValue("password")
|
|
|
|
if username == "" || password == "" {
|
|
http.Error(w, "username and password required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
userStore := auth.NewUserStore(a.DB)
|
|
_, err := userStore.CreateUser(r.Context(), username, password)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Return HTMX partial or redirect
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/users", http.StatusFound)
|
|
}
|
|
}
|
|
|
|
// DeleteUserHandler deletes a user
|
|
func (a *App) DeleteUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
userID := chi.URLParam(r, "id")
|
|
|
|
_, err := a.DB.ExecContext(r.Context(), `DELETE FROM users WHERE id = ?`, userID)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/users", http.StatusFound)
|
|
}
|
|
}
|
|
|
|
// UpdateUserRolesHandler updates roles for a user
|
|
func (a *App) UpdateUserRolesHandler(w http.ResponseWriter, r *http.Request) {
|
|
userID := chi.URLParam(r, "id")
|
|
|
|
var req struct {
|
|
RoleIDs []string `json:"role_ids"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
rbacStore := auth.NewRBACStore(a.DB)
|
|
|
|
// Get current roles
|
|
currentRoles, _ := rbacStore.GetUserRoles(r.Context(), userID)
|
|
|
|
// Remove all current roles
|
|
for _, role := range currentRoles {
|
|
rbacStore.RemoveRoleFromUser(r.Context(), userID, role.ID)
|
|
}
|
|
|
|
// Add new roles
|
|
for _, roleID := range req.RoleIDs {
|
|
rbacStore.AssignRoleToUser(r.Context(), userID, roleID)
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/users", http.StatusFound)
|
|
}
|
|
}
|
|
|
|
// RolesHandler shows the roles management page
|
|
func (a *App) RolesHandler(w http.ResponseWriter, r *http.Request) {
|
|
data := templateData(r, map[string]interface{}{
|
|
"Title": "Role Management",
|
|
})
|
|
if err := templates.ExecuteTemplate(w, "roles", data); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// HXRolesHandler returns HTMX partial for roles list
|
|
func (a *App) HXRolesHandler(w http.ResponseWriter, r *http.Request) {
|
|
rbacStore := auth.NewRBACStore(a.DB)
|
|
|
|
roles, err := rbacStore.GetAllRoles(r.Context())
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
type RoleWithPermissions struct {
|
|
auth.Role
|
|
Permissions []auth.Permission
|
|
}
|
|
|
|
var rolesWithPerms []RoleWithPermissions
|
|
for _, role := range roles {
|
|
rwp := RoleWithPermissions{Role: role}
|
|
perms, _ := rbacStore.GetRolePermissions(r.Context(), role.ID)
|
|
rwp.Permissions = perms
|
|
rolesWithPerms = append(rolesWithPerms, rwp)
|
|
}
|
|
|
|
data := map[string]interface{}{
|
|
"Roles": rolesWithPerms,
|
|
}
|
|
if err := templates.ExecuteTemplate(w, "hx_roles", data); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// CreateRoleHandler creates a new role
|
|
func (a *App) CreateRoleHandler(w http.ResponseWriter, r *http.Request) {
|
|
name := r.FormValue("name")
|
|
description := r.FormValue("description")
|
|
|
|
if name == "" {
|
|
http.Error(w, "name required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
roleID := name // Using name as ID for simplicity
|
|
_, err := a.DB.ExecContext(r.Context(),
|
|
`INSERT INTO roles (id, name, description) VALUES (?, ?, ?)`,
|
|
roleID, name, description)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/roles", http.StatusFound)
|
|
}
|
|
}
|
|
|
|
// DeleteRoleHandler deletes a role
|
|
func (a *App) DeleteRoleHandler(w http.ResponseWriter, r *http.Request) {
|
|
roleID := chi.URLParam(r, "id")
|
|
|
|
_, err := a.DB.ExecContext(r.Context(), `DELETE FROM roles WHERE id = ?`, roleID)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/roles", http.StatusFound)
|
|
}
|
|
}
|
|
|
|
// UpdateRolePermissionsHandler updates permissions for a role
|
|
func (a *App) UpdateRolePermissionsHandler(w http.ResponseWriter, r *http.Request) {
|
|
roleID := chi.URLParam(r, "id")
|
|
|
|
var req struct {
|
|
PermissionIDs []string `json:"permission_ids"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
rbacStore := auth.NewRBACStore(a.DB)
|
|
|
|
// Get current permissions
|
|
currentPerms, _ := rbacStore.GetRolePermissions(r.Context(), roleID)
|
|
|
|
// Remove all current permissions
|
|
for _, perm := range currentPerms {
|
|
rbacStore.RemovePermissionFromRole(r.Context(), roleID, perm.ID)
|
|
}
|
|
|
|
// Add new permissions
|
|
for _, permID := range req.PermissionIDs {
|
|
rbacStore.AssignPermissionToRole(r.Context(), roleID, permID)
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
w.Header().Set("HX-Refresh", "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Redirect(w, r, "/admin/roles", http.StatusFound)
|
|
}
|
|
}
|
|
|