Skip to content

Commit 8d45da1

Browse files
committed
feat: ACLRuleInfo.IsRegex
1 parent a4557c4 commit 8d45da1

File tree

5 files changed

+41
-15
lines changed

5 files changed

+41
-15
lines changed

internal/errs/operate.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type ACLPermissionDeniedError struct {
2222
type ACLRuleInfo struct {
2323
RulePath string
2424
Role string
25+
IsRegex bool
2526
Permissions []string
2627
Priority int
2728
}

internal/model/acl.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@ import "time"
55
// ACLRule represents an access control rule for a specific role and path
66
type ACLRule struct {
77
ID uint `json:"id" gorm:"primaryKey"`
8-
Role string `json:"role" gorm:"index;not null"` // OIDC role name
9-
Path string `json:"path" gorm:"not null"` // path pattern (e.g., "/", "/folder/*")
10-
Permissions int32 `json:"permissions"` // bitwise permissions
11-
Priority int `json:"priority" gorm:"default:0"` // higher priority rules override lower ones
8+
Role string `json:"role" gorm:"index;not null"` // OIDC role name
9+
IsRegex bool `json:"is_regex" gorm:"default:false"` // whether Role is a regex pattern
10+
Path string `json:"path" gorm:"not null"` // path pattern (e.g., "/", "/folder/*")
11+
Permissions int32 `json:"permissions"` // bitwise permissions
12+
Priority int `json:"priority" gorm:"default:0"` // higher priority rules override lower ones
1213
CreatedAt time.Time `json:"created_at"`
1314
UpdatedAt time.Time `json:"updated_at"`
1415
}
1516

1617
// ACL Permission bits
1718
const (
18-
ACLPermRead int32 = 1 << iota // Read/List files
19-
ACLPermWrite // Upload/Create files
20-
ACLPermDelete // Delete files
21-
ACLPermManage // Manage (rename, move, copy)
22-
ACLPermShare // Create shares
23-
ACLPermDownload // Download files
19+
ACLPermRead int32 = 1 << iota // Read/List files
20+
ACLPermWrite // Upload/Create files
21+
ACLPermDelete // Delete files
22+
ACLPermManage // Manage (rename, move, copy)
23+
ACLPermShare // Create shares
24+
ACLPermDownload // Download files
2425
)
2526

2627
// HasPermission checks if the rule has a specific permission

internal/op/acl.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func CheckACLPermission(ctx context.Context, path string, requiredPerm int32) (*
9292

9393
for _, rule := range allRules {
9494
// Check if rule applies to any of the user's roles
95-
if !containsRole(userRoles, rule.Role) {
95+
if !containsRole(userRoles, rule.Role, rule.IsRegex) {
9696
continue
9797
}
9898

@@ -125,6 +125,7 @@ func CheckACLPermission(ctx context.Context, path string, requiredPerm int32) (*
125125
ruleInfo := &errs.ACLRuleInfo{
126126
RulePath: matchedRule.Path,
127127
Role: matchedRule.Role,
128+
IsRegex: matchedRule.IsRegex,
128129
Permissions: getPermissionNames(matchedRule.Permissions),
129130
Priority: matchedRule.Priority,
130131
}
@@ -180,7 +181,7 @@ func GetMatchedACLRule(ctx context.Context, path string) (*model.ACLMatchedRule,
180181
normalizedPath := normalizePath(path)
181182

182183
for _, rule := range allRules {
183-
if !containsRole(userRoles, rule.Role) {
184+
if !containsRole(userRoles, rule.Role, rule.IsRegex) {
184185
continue
185186
}
186187

@@ -227,10 +228,19 @@ func getUserRoles(user *model.User) []string {
227228
return roles
228229
}
229230

230-
func containsRole(roles []string, role string) bool {
231+
func containsRole(roles []string, role string, isRegex bool) bool {
231232
if role == "*" {
232233
return true
233234
}
235+
if isRegex {
236+
for _, r := range roles {
237+
matched, err := utils.RegexMatch(role, r)
238+
if err == nil && matched {
239+
return true
240+
}
241+
}
242+
return false
243+
}
234244
return slices.Contains(roles, role)
235245
}
236246

pkg/utils/regex.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package utils
2+
3+
import "regexp"
4+
5+
// RegexMatch returns true if str matches the given regex pattern.
6+
func RegexMatch(pattern, str string) (bool, error) {
7+
reg, err := regexp.Compile(pattern)
8+
if err != nil {
9+
return false, err
10+
}
11+
return reg.MatchString(str), nil
12+
}

server/handles/acl.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
type ACLRuleReq struct {
1212
Role string `json:"role" binding:"required"`
13+
IsRegex bool `json:"is_regex"`
1314
Path string `json:"path" binding:"required"`
1415
Permissions int32 `json:"permissions"`
1516
Priority int `json:"priority"`
@@ -64,6 +65,7 @@ func CreateACLRule(c *gin.Context) {
6465
}
6566
rule := &model.ACLRule{
6667
Role: req.Role,
68+
IsRegex: req.IsRegex,
6769
Path: req.Path,
6870
Permissions: req.Permissions,
6971
Priority: req.Priority,
@@ -122,12 +124,12 @@ func GetPathACLInfo(c *gin.Context) {
122124
common.ErrorResp(c, err, 400)
123125
return
124126
}
125-
127+
126128
matched, err := op.GetMatchedACLRule(c.Request.Context(), req.Path)
127129
if err != nil {
128130
common.ErrorResp(c, err, 500)
129131
return
130132
}
131-
133+
132134
common.SuccessResp(c, matched)
133135
}

0 commit comments

Comments
 (0)