feat(custom-roles): align chatwoot permissions

This commit is contained in:
2026-06-05 11:14:38 +08:00
parent b5f2a47ef8
commit 8b3b532c65
20 changed files with 681 additions and 226 deletions
+15 -8
View File
@@ -27,7 +27,8 @@ import (
// For production, use AccountScopeWithService() which looks up AccountUser from DB.
//
// Usage:
// router.Use(AuthRequired(jwtSvc), AccountScope())
//
// router.Use(AuthRequired(jwtSvc), AccountScope())
func AccountScope() gin.HandlerFunc {
return func(c *gin.Context) {
// Step 1: Get user_id from JWT claims (set by AuthRequired)
@@ -94,8 +95,9 @@ func AccountScope() gin.HandlerFunc {
// permissions from the database. This is the production-grade version.
//
// Usage:
// rbacSvc := service.NewRBACService(db)
// router.Use(AuthRequired(jwtSvc), AccountScopeWithService(rbacSvc))
//
// rbacSvc := service.NewRBACService(db)
// router.Use(AuthRequired(jwtSvc), AccountScopeWithService(rbacSvc))
func AccountScopeWithService(lookup RBACLookup) gin.HandlerFunc {
return func(c *gin.Context) {
userID, exists := c.Get("user_id")
@@ -115,7 +117,7 @@ func AccountScopeWithService(lookup RBACLookup) gin.HandlerFunc {
c.Set("account_id", accountID)
// Look up AccountUser to get role and CustomRoleID
accountUser, err := lookup.GetAccountUser(userID.(uint), accountID)
accountUser, err := lookup.GetAccountUserRole(userID.(uint), accountID)
if err != nil {
response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden,
"User does not belong to this account")
@@ -124,7 +126,7 @@ func AccountScopeWithService(lookup RBACLookup) gin.HandlerFunc {
// Build permissions matrix based on role
permissions := auth.PermissionMatrixMap{}
if accountUser.Role == "custom_role" && accountUser.CustomRoleID > 0 {
if accountUser.CustomRoleID > 0 && accountUser.Role != "administrator" {
pm, err := lookup.GetCustomRolePermissions(accountUser.CustomRoleID)
if err != nil {
// Fallback to agent defaults if custom role not found
@@ -134,10 +136,15 @@ func AccountScopeWithService(lookup RBACLookup) gin.HandlerFunc {
}
}
effectiveRole := accountUser.Role
if accountUser.CustomRoleID > 0 && effectiveRole != "administrator" {
effectiveRole = "custom_role"
}
policyCtx := auth.NewPolicyContext(
userID.(uint),
accountID,
accountUser.Role,
effectiveRole,
accountUser.CustomRoleID,
permissions,
)
@@ -187,7 +194,7 @@ func getAccountID(c *gin.Context) uint {
// RBACLookup is the interface that the RBAC service must implement
// for use with AccountScopeWithService middleware.
type RBACLookup interface {
GetAccountUser(userID, accountID uint) (*AccountUserRole, error)
GetAccountUserRole(userID, accountID uint) (*AccountUserRole, error)
GetCustomRolePermissions(customRoleID uint) (auth.PermissionMatrixMap, error)
}
@@ -199,4 +206,4 @@ type AccountUserRole struct {
Role string
CustomRoleID uint
Availability string
}
}