Skip to content

Commit 9c216f5

Browse files
committed
feat: ACLRule.exclude_subfolder
1 parent 8d45da1 commit 9c216f5

3 files changed

Lines changed: 28 additions & 35 deletions

File tree

internal/model/acl.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import "time"
44

55
// ACLRule represents an access control rule for a specific role and path
66
type ACLRule struct {
7-
ID uint `json:"id" gorm:"primaryKey"`
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
13-
CreatedAt time.Time `json:"created_at"`
14-
UpdatedAt time.Time `json:"updated_at"`
7+
ID uint `json:"id" gorm:"primaryKey"`
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+
ExcludeSubfolder bool `json:"exclude_subfolder" gorm:"default:false"` // whether to exclude all subfolders (default: false)
12+
Permissions int32 `json:"permissions"` // bitwise permissions
13+
Priority int `json:"priority" gorm:"default:0"` // higher priority rules override lower ones
14+
CreatedAt time.Time `json:"created_at"`
15+
UpdatedAt time.Time `json:"updated_at"`
1516
}
1617

1718
// ACL Permission bits

internal/op/acl.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ func CheckACLPermission(ctx context.Context, path string, requiredPerm int32) (*
9696
continue
9797
}
9898

99-
// Check if path matches the rule
100-
if pathMatches(normalizedPath, rule.Path) {
99+
// Check if path matches the rule, considering ExcludeSubfolder
100+
if pathMatchesWithSubfolder(normalizedPath, rule.Path, !rule.ExcludeSubfolder) {
101101
if matchedRule == nil || rule.Priority > matchedRule.Priority {
102102
matchedRule = &rule
103103
}
@@ -185,7 +185,7 @@ func GetMatchedACLRule(ctx context.Context, path string) (*model.ACLMatchedRule,
185185
continue
186186
}
187187

188-
if pathMatches(normalizedPath, rule.Path) {
188+
if pathMatchesWithSubfolder(normalizedPath, rule.Path, !rule.ExcludeSubfolder) {
189189
if matchedRule == nil || rule.Priority > matchedRule.Priority {
190190
matchedRule = &rule
191191
}
@@ -257,28 +257,18 @@ func normalizePath(path string) string {
257257
return path
258258
}
259259

260-
func pathMatches(path, pattern string) bool {
261-
// Normalize both paths
260+
// pathMatchesWithSubfolder checks if path matches pattern, with subfolder option
261+
func pathMatchesWithSubfolder(path, pattern string, includeSubfolder bool) bool {
262262
path = normalizePath(path)
263263
pattern = normalizePath(pattern)
264264

265-
// Exact match
266265
if path == pattern {
267266
return true
268267
}
269-
270-
// Pattern with wildcard
271-
if strings.HasSuffix(pattern, "/*") {
272-
prefix := strings.TrimSuffix(pattern, "/*")
268+
if includeSubfolder {
273269
// Check if path is under this directory
274-
return strings.HasPrefix(path, prefix+"/") || path == prefix
270+
return strings.HasPrefix(path, pattern+"/") || path == pattern
275271
}
276-
277-
// Pattern is a parent directory
278-
if strings.HasPrefix(path, pattern+"/") {
279-
return true
280-
}
281-
282272
return false
283273
}
284274

server/handles/acl.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import (
99
)
1010

1111
type ACLRuleReq struct {
12-
Role string `json:"role" binding:"required"`
13-
IsRegex bool `json:"is_regex"`
14-
Path string `json:"path" binding:"required"`
15-
Permissions int32 `json:"permissions"`
16-
Priority int `json:"priority"`
12+
Role string `json:"role" binding:"required"`
13+
IsRegex bool `json:"is_regex"`
14+
Path string `json:"path" binding:"required"`
15+
Permissions int32 `json:"permissions"`
16+
Priority int `json:"priority"`
17+
ExcludeSubfolder bool `json:"exclude_subfolder"` // false means include subfolders (default)
1718
}
1819

1920
// ListACLRules lists all ACL rules
@@ -64,11 +65,12 @@ func CreateACLRule(c *gin.Context) {
6465
return
6566
}
6667
rule := &model.ACLRule{
67-
Role: req.Role,
68-
IsRegex: req.IsRegex,
69-
Path: req.Path,
70-
Permissions: req.Permissions,
71-
Priority: req.Priority,
68+
Role: req.Role,
69+
IsRegex: req.IsRegex,
70+
Path: req.Path,
71+
Permissions: req.Permissions,
72+
Priority: req.Priority,
73+
ExcludeSubfolder: req.ExcludeSubfolder,
7274
}
7375
if err := op.CreateACLRule(rule); err != nil {
7476
common.ErrorResp(c, err, 500)

0 commit comments

Comments
 (0)