Skip to content

Commit 375316f

Browse files
committed
Seperated template management and creation from Admin only. Created new
panel for "Creators"
1 parent 570a5d7 commit 375316f

File tree

9 files changed

+164
-37
lines changed

9 files changed

+164
-37
lines changed

internal/api/auth/auth_service.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@ func (s *AuthService) IsAdmin(username string) (bool, error) {
7070
return false, nil
7171
}
7272

73+
func (s *AuthService) IsCreator(username string) (bool, error) {
74+
// Get user's groups from Proxmox
75+
userGroups, err := s.proxmoxService.GetUserGroups(username)
76+
if err != nil {
77+
return false, fmt.Errorf("failed to get user groups: %w", err)
78+
}
79+
80+
// Get the creator group name from config
81+
creatorGroupName := s.proxmoxService.Config.CreatorGroupName
82+
83+
// Check if user is in the creator group
84+
if slices.Contains(userGroups, creatorGroupName) {
85+
return true, nil
86+
}
87+
88+
return false, nil
89+
}
90+
7391
func (s *AuthService) HealthCheck() error {
7492
return s.ldapService.HealthCheck()
7593
}

internal/api/auth/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Service interface {
1313
// Authentication
1414
Authenticate(username, password string) (bool, error)
1515
IsAdmin(username string) (bool, error)
16+
IsCreator(username string) (bool, error)
1617

1718
// Health and Connection
1819
HealthCheck() error

internal/api/handlers/auth_handler.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ func NewAuthHandler() (*AuthHandler, error) {
4848
}, nil
4949
}
5050

51+
// GetAuthService returns the auth service for use in middleware
52+
func (h *AuthHandler) GetAuthService() auth.Service {
53+
return h.authService
54+
}
55+
5156
// LoginHandler handles the login POST request
5257
func (h *AuthHandler) LoginHandler(c *gin.Context) {
5358
var req UsernamePasswordRequest
@@ -80,15 +85,24 @@ func (h *AuthHandler) LoginHandler(c *gin.Context) {
8085
}
8186
session.Set("isAdmin", isAdmin)
8287

88+
// Check if user is creator
89+
isCreator, err := h.authService.IsCreator(req.Username)
90+
if err != nil {
91+
log.Printf("Error checking creator status for user %s: %v", req.Username, err)
92+
isCreator = false
93+
}
94+
session.Set("isCreator", isCreator)
95+
8396
if err := session.Save(); err != nil {
8497
log.Printf("Failed to save session for user %s: %v", req.Username, err)
8598
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save session"})
8699
return
87100
}
88101

89102
c.JSON(http.StatusOK, gin.H{
90-
"message": "Login successful",
91-
"isAdmin": isAdmin,
103+
"message": "Login successful",
104+
"isAdmin": isAdmin,
105+
"isCreator": isCreator,
92106
})
93107
}
94108

@@ -113,17 +127,24 @@ func (h *AuthHandler) SessionHandler(c *gin.Context) {
113127
// Since this is under private routes, AuthRequired middleware ensures session exists
114128
id := session.Get("id")
115129
isAdmin := session.Get("isAdmin")
130+
isCreator := session.Get("isCreator")
116131

117-
// Convert isAdmin to bool, defaulting to false if not set
132+
// Convert to bool, defaulting to false if not set
118133
adminStatus := false
119134
if isAdmin != nil {
120135
adminStatus = isAdmin.(bool)
121136
}
122137

138+
creatorStatus := false
139+
if isCreator != nil {
140+
creatorStatus = isCreator.(bool)
141+
}
142+
123143
c.JSON(http.StatusOK, gin.H{
124144
"authenticated": true,
125145
"username": id.(string),
126146
"isAdmin": adminStatus,
147+
"isCreator": creatorStatus,
127148
})
128149
}
129150

internal/api/middleware/authorization.go

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package middleware
22

33
import (
4+
"log"
45
"net/http"
56

7+
"github.com/cpp-cyber/proclone/internal/api/auth"
68
"github.com/gin-contrib/sessions"
79
"github.com/gin-gonic/gin"
810
)
@@ -19,23 +21,75 @@ func AuthRequired(c *gin.Context) {
1921
c.Next()
2022
}
2123

22-
func AdminRequired(c *gin.Context) {
23-
session := sessions.Default(c)
24-
id := session.Get("id")
25-
if id == nil {
26-
c.String(http.StatusUnauthorized, "Unauthorized")
27-
c.Abort()
28-
return
29-
}
24+
func AdminRequired(authService auth.Service) gin.HandlerFunc {
25+
return func(c *gin.Context) {
26+
session := sessions.Default(c)
27+
id := session.Get("id")
28+
if id == nil {
29+
c.String(http.StatusUnauthorized, "Unauthorized")
30+
c.Abort()
31+
return
32+
}
3033

31-
isAdmin := session.Get("isAdmin")
32-
if isAdmin == nil || !isAdmin.(bool) {
33-
c.String(http.StatusForbidden, "Admin access required")
34-
c.Abort()
35-
return
34+
username := id.(string)
35+
36+
// Verify with Proxmox
37+
isAdmin, err := authService.IsAdmin(username)
38+
if err != nil {
39+
log.Printf("Error verifying admin status for user %s: %v", username, err)
40+
c.String(http.StatusInternalServerError, "Failed to verify permissions")
41+
c.Abort()
42+
return
43+
}
44+
45+
if !isAdmin {
46+
c.String(http.StatusForbidden, "Admin access required")
47+
c.Abort()
48+
return
49+
}
50+
51+
c.Next()
3652
}
53+
}
3754

38-
c.Next()
55+
// CreatorOrAdminRequired provides authorization middleware for template operations
56+
func CreatorOrAdminRequired(authService auth.Service) gin.HandlerFunc {
57+
return func(c *gin.Context) {
58+
session := sessions.Default(c)
59+
id := session.Get("id")
60+
if id == nil {
61+
c.String(http.StatusUnauthorized, "Unauthorized")
62+
c.Abort()
63+
return
64+
}
65+
66+
username := id.(string)
67+
68+
// Verify with Proxmox for template operations
69+
isAdmin, err := authService.IsAdmin(username)
70+
if err != nil {
71+
log.Printf("Error verifying admin status for user %s: %v", username, err)
72+
c.String(http.StatusInternalServerError, "Failed to verify permissions")
73+
c.Abort()
74+
return
75+
}
76+
77+
isCreator, err := authService.IsCreator(username)
78+
if err != nil {
79+
log.Printf("Error verifying creator status for user %s: %v", username, err)
80+
c.String(http.StatusInternalServerError, "Failed to verify permissions")
81+
c.Abort()
82+
return
83+
}
84+
85+
if !isAdmin && !isCreator {
86+
c.String(http.StatusForbidden, "Creator or Admin access required")
87+
c.Abort()
88+
return
89+
}
90+
91+
c.Next()
92+
}
3993
}
4094

4195
func GetUser(c *gin.Context) string {

internal/api/routes/admin_routes.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,38 @@ import (
55
"github.com/gin-gonic/gin"
66
)
77

8-
// registerAdminRoutes defines all routes accessible to admin users
8+
// registerAdminRoutes defines all routes accessible ONLY to admin users
9+
// Template operations have been moved to creator routes (accessible by both admins and creators)
910
func registerAdminRoutes(g *gin.RouterGroup, authHandler *handlers.AuthHandler, proxmoxHandler *handlers.ProxmoxHandler, cloningHandler *handlers.CloningHandler, dashboardHandler *handlers.DashboardHandler) {
10-
// GET Requests
11+
// Admin dashboard and cluster management
1112
g.GET("/dashboard", dashboardHandler.GetAdminDashboardStatsHandler)
1213
g.GET("/cluster", proxmoxHandler.GetClusterResourceUsageHandler)
13-
g.GET("/users", proxmoxHandler.GetUsersHandler)
14-
g.GET("/groups", proxmoxHandler.GetGroupsHandler)
15-
g.GET("/vms", proxmoxHandler.GetVMsHandler)
1614
g.GET("/vnets", proxmoxHandler.GetUsedVNetsHandler)
15+
g.GET("/vms", proxmoxHandler.GetVMsHandler)
1716
g.GET("/pods", cloningHandler.AdminGetPodsHandler)
18-
g.GET("/templates", cloningHandler.AdminGetTemplatesHandler)
19-
g.GET("/templates/unpublished", cloningHandler.GetUnpublishedTemplatesHandler)
20-
g.GET("/templates/vms", proxmoxHandler.GetVMTemplatesHandler)
21-
g.GET("/templates/proxmox", proxmoxHandler.GetProxmoxTemplatePoolsHandler)
2217

23-
// POST Requests
18+
// User management (admin only)
19+
g.GET("/users", proxmoxHandler.GetUsersHandler)
2420
g.POST("/users/create", authHandler.CreateUsersHandler)
2521
g.POST("/users/delete", authHandler.DeleteUsersHandler)
2622
g.POST("/user/groups", proxmoxHandler.SetUserGroupsHandler)
23+
24+
// Group management (admin only)
25+
g.GET("/groups", proxmoxHandler.GetGroupsHandler)
2726
g.POST("/groups/create", proxmoxHandler.CreateGroupsHandler)
2827
g.POST("/group/members/add", proxmoxHandler.AddUsersHandler)
2928
g.POST("/group/members/remove", proxmoxHandler.RemoveUsersHandler)
3029
g.POST("/group/edit", proxmoxHandler.EditGroupHandler)
3130
g.POST("/groups/delete", proxmoxHandler.DeleteGroupsHandler)
31+
32+
// VM management (admin only)
3233
g.POST("/vm/start", proxmoxHandler.StartVMHandler)
3334
g.POST("/vm/shutdown", proxmoxHandler.ShutdownVMHandler)
3435
g.POST("/vm/reboot", proxmoxHandler.RebootVMHandler)
36+
37+
// Pod management (admin only)
3538
g.POST("/pods/delete", cloningHandler.AdminDeletePodHandler)
36-
g.POST("/template/publish", cloningHandler.PublishTemplateHandler)
37-
g.POST("/template/create", proxmoxHandler.CreateTemplateHandler)
38-
g.POST("/template/edit", cloningHandler.EditTemplateHandler)
39-
g.POST("/template/delete", cloningHandler.DeleteTemplateHandler)
40-
g.POST("/template/visibility", cloningHandler.ToggleTemplateVisibilityHandler)
41-
g.POST("/template/image/upload", cloningHandler.UploadTemplateImageHandler)
39+
40+
// Bulk template deployment (admin only)
4241
g.POST("/templates/clone", cloningHandler.AdminCloneTemplateHandler)
4342
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package routes
2+
3+
import (
4+
"github.com/cpp-cyber/proclone/internal/api/handlers"
5+
"github.com/gin-gonic/gin"
6+
)
7+
8+
// registerCreatorRoutes defines all routes accessible to both creators and admins
9+
func registerCreatorRoutes(g *gin.RouterGroup, proxmoxHandler *handlers.ProxmoxHandler, cloningHandler *handlers.CloningHandler) {
10+
// Template management operations (create, publish, edit, delete)
11+
g.POST("/template/publish", cloningHandler.PublishTemplateHandler)
12+
g.POST("/template/create", proxmoxHandler.CreateTemplateHandler)
13+
g.POST("/template/edit", cloningHandler.EditTemplateHandler)
14+
g.POST("/template/delete", cloningHandler.DeleteTemplateHandler)
15+
g.POST("/template/visibility", cloningHandler.ToggleTemplateVisibilityHandler)
16+
g.POST("/template/image/upload", cloningHandler.UploadTemplateImageHandler)
17+
18+
// Template viewing operations
19+
g.GET("/templates", cloningHandler.AdminGetTemplatesHandler)
20+
g.GET("/templates/unpublished", cloningHandler.GetUnpublishedTemplatesHandler)
21+
g.GET("/templates/vms", proxmoxHandler.GetVMTemplatesHandler)
22+
g.GET("/templates/proxmox", proxmoxHandler.GetProxmoxTemplatePoolsHandler)
23+
}

internal/api/routes/routes.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ func RegisterRoutes(r *gin.Engine, authHandler *handlers.AuthHandler, proxmoxHan
1111
// Create centralized dashboard handler
1212
dashboardHandler := handlers.NewDashboardHandler(authHandler, proxmoxHandler, cloningHandler)
1313

14+
// Get auth service from handler for middleware
15+
authService := authHandler.GetAuthService()
16+
1417
// Public routes (no authentication required)
1518
public := r.Group("/api/v1")
1619
registerPublicRoutes(public, authHandler, cloningHandler)
@@ -20,8 +23,15 @@ func RegisterRoutes(r *gin.Engine, authHandler *handlers.AuthHandler, proxmoxHan
2023
private.Use(middleware.AuthRequired)
2124
registerPrivateRoutes(private, authHandler, cloningHandler, dashboardHandler)
2225

26+
// Creator routes (authentication + creator OR admin privileges required)
27+
// Template management operations accessible to both creators and admins
28+
creator := r.Group("/api/v1/creator")
29+
creator.Use(middleware.CreatorOrAdminRequired(authService))
30+
registerCreatorRoutes(creator, proxmoxHandler, cloningHandler)
31+
2332
// Admin routes (authentication + admin privileges required)
33+
// User/group management and system operations
2434
admin := r.Group("/api/v1/admin")
25-
admin.Use(middleware.AdminRequired)
35+
admin.Use(middleware.AdminRequired(authService))
2636
registerAdminRoutes(admin, authHandler, proxmoxHandler, cloningHandler, dashboardHandler)
2737
}

internal/ldap/users.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ func extractDomainFromDN(dn string) string {
150150

151151
for _, part := range parts {
152152
part = strings.TrimSpace(part)
153-
if strings.HasPrefix(part, "dc=") {
154-
domainParts = append(domainParts, strings.TrimPrefix(part, "dc="))
153+
if domain, found := strings.CutPrefix(part, "dc="); found {
154+
domainParts = append(domainParts, domain)
155155
}
156156
}
157157

internal/proxmox/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ type ProxmoxConfig struct {
1818
Realm string `envconfig:"PROXMOX_REALM"`
1919
NodesStr string `envconfig:"PROXMOX_NODES"`
2020
StorageID string `envconfig:"PROXMOX_STORAGE_ID" default:"local-lvm"`
21-
AdminGroupName string `envconfig:"PROXMOX_ADMIN_GROUP_NAME" default:"admin"`
21+
AdminGroupName string `envconfig:"PROXMOX_ADMIN_GROUP_NAME" default:"Admin"`
22+
CreatorGroupName string `envconfig:"PROXMOX_CREATOR_GROUP_NAME" default:"Creator"`
2223
VMTemplatePool string `envconfig:"PROXMOX_VM_TEMPLATE_POOL" default:"Templates"`
2324
RouterName string `envconfig:"PROXMOX_ROUTER_NAME" default:"1-1NAT-vyos"`
2425
RouterNode string `envconfig:"PROXMOX_ROUTER_NODE"`

0 commit comments

Comments
 (0)