guard_test
API
guard_test
packageAPI reference for the guard_test
package.
Imports
(5)
S
struct
Resource
pkg/guard/guard_bench_test.go:12-15
type Resource struct
Fields
| Name | Type | Description |
|---|---|---|
| Title | string | guard:"role:admin; can:read,write" |
| Data | string | guard:"role:user; can:read" |
S
User
pkg/guard/guard_bench_test.go:17-20
type User struct
Methods
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | |
| Roles | []string |
F
function
CanNaive
Naive implementation simulating old behavior (parsing every time)
Parameters
Returns
error
pkg/guard/guard_bench_test.go:26-94
func CanNaive(user guard.Identity, resource any, action string) error
{
val := reflect.ValueOf(resource)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
userRoles := make(map[string]bool)
for _, r := range user.GetRoles() {
userRoles[r] = true
}
found := false
allowed := false
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
tag := field.Tag.Get("guard")
if tag == "" {
continue
}
// Simulate parsing overhead (string splitting + allocation)
parts := strings.Split(tag, ";")
for _, part := range parts {
kv := strings.Split(part, ":")
if len(kv) != 2 {
continue
}
key := strings.TrimSpace(kv[0])
// Simulating "can" check
// "can:read,write"
if key == "can" {
perms := strings.Split(kv[1], ",")
for _, p := range perms {
if strings.TrimSpace(p) == action {
found = true
// Simplified check for roles in same tag
// In real naive impl, we would have parsed roles too.
// Let's parse roles to be fair.
}
}
}
}
// Re-scan for roles if found
if found {
for _, part := range parts {
kv := strings.Split(part, ":")
if strings.TrimSpace(kv[0]) == "role" {
roles := strings.Split(kv[1], ",")
for _, r := range roles {
if userRoles[strings.TrimSpace(r)] {
allowed = true
}
}
}
}
}
}
if !found {
return fmt.Errorf("no rule")
}
if !allowed {
return fmt.Errorf("denied")
}
return nil
}
F
function
BenchmarkCan_Cached
Parameters
b
pkg/guard/guard_bench_test.go:96-108
func BenchmarkCan_Cached(b *testing.B)
{
g := guard.NewGuard()
u := &User{ID: "1", Roles: []string{"admin"}}
res := &Resource{Title: "Test"}
// Warmup cache
_ = g.Can(u, res, "read")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = g.Can(u, res, "read")
}
}
F
function
BenchmarkCan_Naive
Parameters
b
pkg/guard/guard_bench_test.go:110-118
func BenchmarkCan_Naive(b *testing.B)
{
u := &User{ID: "1", Roles: []string{"admin"}}
res := &Resource{Title: "Test"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = CanNaive(u, res, "read")
}
}
S
struct
Document
pkg/guard/guard_test.go:9-14
type Document struct
Fields
| Name | Type | Description |
|---|---|---|
| Title | string | guard:"role:admin; can:read,write" |
| Content | string | guard:"role:editor; can:edit" |
| Public | string | guard:"role:*; can:view" |
| OwnerID | string | guard:"role:*; can:delete" |
S
struct
DynamicDoc
pkg/guard/guard_test.go:16-18
type DynamicDoc struct
Fields
| Name | Type | Description |
|---|---|---|
| Meta | map[string]string | guard:"role:*; can:manage" |
S
MockUser
pkg/guard/guard_test.go:20-23
type MockUser struct
Methods
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | |
| Roles | []string |
F
function
TestGuard_Can
Parameters
t
pkg/guard/guard_test.go:28-108
func TestGuard_Can(t *testing.T)
{
g := guard.NewGuard()
admin := &MockUser{ID: "1", Roles: []string{"admin"}}
editor := &MockUser{ID: "2", Roles: []string{"editor"}}
// owner := &MockUser{ID: "3", Roles: []string{"user"}} // OwnerID will be "3" in doc
stranger := &MockUser{ID: "4", Roles: []string{"guest"}}
doc := &Document{
Title: "Secret",
Content: "Stuff",
Public: "guest", // value "guest" treated as role? "role:*" on "Public"
OwnerID: "3", // value "3" treated as role? Or is it "role of user with ID 3"?
// Wait, `role:*` semantic in my implementation:
// If Map: checks keys against UserID. If match, values are roles.
// If String: String value IS the role.
// If Int: Int value stringified IS the role.
// So here: Public="guest". User stranger has role "guest". Should match?
// OwnerID="3". User owner has ID "3". Does he have role "3"? No.
// BUT `checker` logic: `IsMatch(val, userID)`?
// Let's re-read `policy.go` logic for Dynamic Rule:
// `extractRoles` extracts strings from Valid Map/Slice/String.
// The `CompiledPolicy` logic:
// `dynamicRoles := extractRoles(fieldVal, user.GetID())`
// `extractRoles` logic:
// If Map: `if checker.IsMatch(key, userID) { append val }`
// If String: `append val.String()`
// If Slice: iterate and append strings.
// So for `Public string`: it appends "guest".
// Then we checks inner loop: `if dr == ur`.
// User stranger has role "guest". So "guest" == "guest". ALLOWED.
// For `OwnerID string`: it appends "3".
// User owner has role "user". "3" != "user".
// Unless OwnerID held "user"?
// If `role:*` mechanism is used for "Ownership", usually it means "User ID must match this field".
// But here `role:*` means "The value of this field IS the required role".
// If I want "Owner check", the tag should probably be different or value should be the Role Name (e.g. Owner has dynamic role "3"? No).
// Wait, usually `guard` library might support `owner` strategy?
// But in this implementation, `role:*` interprets the field value as the Required Role Name.
// So if OwnerID="3", the required role is "3".
// If my user has role "3", allowed.
// If I want ownership based access, I probably use `role:owner` and assign "owner" role to user dynamically?
// Or maybe the field value IS the user ID?
// If the logic intended was "Allow if Field Value == User ID", then current implementation is WRONG.
// But checking `policy.go`:
// `extractRoles` just gets strings.
// It doesn't compare field value to UserID (except for Map keys).
// So `role:*` on string field = "Use value as required role".
// Correct.
}
tests := []struct {
name string
user guard.Identity
resource *Document
action string
wantErr bool
}{
{"Admin Read Title", admin, doc, "read", false},
{"Admin Write Title", admin, doc, "write", false},
{"Admin Edit Content (Fail)", admin, doc, "edit", true}, // Admin doesn't have "editor"
{"Editor Read Title (Fail)", editor, doc, "read", true},
{"Editor Edit Content", editor, doc, "edit", false},
{"Guest View Public", stranger, doc, "view", false}, // field Public="guest", user role="guest"
// {"Owner Delete (Fail)", owner, doc, "delete", true}, // OwnerID="3", user role != "3"
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := g.Can(tt.user, tt.resource, tt.action)
if (err != nil) != tt.wantErr {
t.Errorf("Can() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}