-
Notifications
You must be signed in to change notification settings - Fork 2
[LFXV2-920] Policy Abstraction for Fine-Grained Authorization #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
mauriciozanettisalomao
wants to merge
8
commits into
linuxfoundation:main
from
mauriciozanettisalomao:feature/lfxv2--920-committee-policies-member-visibility
Closed
Changes from 2 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0f0c2f4
feat: implement fine-grained authorization policies and relationship …
mauriciozanettisalomao a6ba574
feat: enhance policy handling with logging and validation tests
mauriciozanettisalomao 625f846
refactor: improve code readability by formatting function signatures
mauriciozanettisalomao 4db32b4
feat: update EvaluatePolicy to include user-object relation for commi…
mauriciozanettisalomao c85922a
feat: add logging for policy evaluation errors and tuple checks
mauriciozanettisalomao 5d1aeff
refactor: improve logging format for policy evaluation errors in comm…
mauriciozanettisalomao 08879b8
refactor: format EvaluatePolicy function signature for improved reada…
mauriciozanettisalomao e6f00be
chore: update chart version to 0.3.0
mauriciozanettisalomao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright The Linux Foundation and each contributor to LFX. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package domain | ||
|
|
||
| import "fmt" | ||
|
|
||
| // Policy represents a fine-grained authorization policy. | ||
| type Policy struct { | ||
| Name string `json:"name"` | ||
| Relation string `json:"relation"` | ||
| Value string `json:"value"` | ||
| } | ||
|
|
||
| // Validate checks if the policy has all required fields. | ||
| // Returns an error if any field is empty. | ||
| func (p Policy) Validate() error { | ||
| if p.Name == "" { | ||
| return fmt.Errorf("policy name cannot be empty") | ||
| } | ||
| if p.Value == "" { | ||
| return fmt.Errorf("policy value cannot be empty") | ||
| } | ||
| if p.Relation == "" { | ||
| return fmt.Errorf("policy relation cannot be empty") | ||
| } | ||
| return nil | ||
mauriciozanettisalomao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // ObjectID returns the OpenFGA object identifier for this policy. | ||
| // Format: policy.Name:policy.Value | ||
| // Example: "visibility_policy:basic_profile" | ||
| func (p Policy) ObjectID() string { | ||
| return fmt.Sprintf("%s:%s", p.Name, p.Value) | ||
| } | ||
|
|
||
| // UserRelation returns the user relation string for a given object. | ||
| // Format: objectID#relation | ||
| // Example: "committee:123#member" | ||
| func (p Policy) UserRelation(objectID, relation string) string { | ||
| return fmt.Sprintf("%s#%s", objectID, relation) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| // Copyright The Linux Foundation and each contributor to LFX. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package domain | ||
|
|
||
| import ( | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestPolicy_Validate(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| policy Policy | ||
| wantErr bool | ||
| errMsg string | ||
| }{ | ||
| { | ||
| name: "valid policy", | ||
| policy: Policy{ | ||
| Name: "visibility_policy", | ||
| Value: "basic_profile", | ||
| Relation: "allows_basic_profile", | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty name", | ||
| policy: Policy{ | ||
| Name: "", | ||
| Value: "basic_profile", | ||
| Relation: "allows_basic_profile", | ||
| }, | ||
| wantErr: true, | ||
| errMsg: "policy name cannot be empty", | ||
| }, | ||
| { | ||
| name: "empty value", | ||
| policy: Policy{ | ||
| Name: "visibility_policy", | ||
| Value: "", | ||
| Relation: "allows_basic_profile", | ||
| }, | ||
| wantErr: true, | ||
| errMsg: "policy value cannot be empty", | ||
| }, | ||
| { | ||
| name: "empty relation", | ||
| policy: Policy{ | ||
| Name: "visibility_policy", | ||
| Value: "basic_profile", | ||
| Relation: "", | ||
| }, | ||
| wantErr: true, | ||
| errMsg: "policy relation cannot be empty", | ||
| }, | ||
| { | ||
| name: "all fields empty", | ||
| policy: Policy{ | ||
| Name: "", | ||
| Value: "", | ||
| Relation: "", | ||
| }, | ||
| wantErr: true, | ||
| errMsg: "policy name cannot be empty", | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| err := tt.policy.Validate() | ||
| if (err != nil) != tt.wantErr { | ||
| t.Errorf("Policy.Validate() error = %v, wantErr %v", err, tt.wantErr) | ||
| return | ||
| } | ||
| if err != nil && err.Error() != tt.errMsg { | ||
| t.Errorf("Policy.Validate() error message = %v, want %v", err.Error(), tt.errMsg) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestPolicy_ObjectID(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| policy Policy | ||
| want string | ||
| }{ | ||
| { | ||
| name: "visibility policy", | ||
| policy: Policy{ | ||
| Name: "visibility_policy", | ||
| Value: "basic_profile", | ||
| }, | ||
| want: "visibility_policy:basic_profile", | ||
| }, | ||
| { | ||
| name: "access policy", | ||
| policy: Policy{ | ||
| Name: "access_policy", | ||
| Value: "admin", | ||
| }, | ||
| want: "access_policy:admin", | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| policy: Policy{ | ||
| Name: "", | ||
| Value: "", | ||
| }, | ||
| want: ":", | ||
| }, | ||
| { | ||
| name: "special characters", | ||
| policy: Policy{ | ||
| Name: "policy-name", | ||
| Value: "value_123", | ||
| }, | ||
| want: "policy-name:value_123", | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if got := tt.policy.ObjectID(); got != tt.want { | ||
| t.Errorf("Policy.ObjectID() = %v, want %v", got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestPolicy_UserRelation(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| policy Policy | ||
| objectID string | ||
| relation string | ||
| want string | ||
| }{ | ||
| { | ||
| name: "committee member relation", | ||
| policy: Policy{}, | ||
| objectID: "committee:123", | ||
| relation: "member", | ||
| want: "committee:123#member", | ||
| }, | ||
| { | ||
| name: "project viewer relation", | ||
| policy: Policy{}, | ||
| objectID: "project:abc-def-ghi", | ||
| relation: "viewer", | ||
| want: "project:abc-def-ghi#viewer", | ||
| }, | ||
| { | ||
| name: "team owner relation", | ||
| policy: Policy{}, | ||
| objectID: "team:xyz", | ||
| relation: "owner", | ||
| want: "team:xyz#owner", | ||
| }, | ||
| { | ||
| name: "empty objectID", | ||
| policy: Policy{}, | ||
| objectID: "", | ||
| relation: "member", | ||
| want: "#member", | ||
| }, | ||
| { | ||
| name: "empty relation", | ||
| policy: Policy{}, | ||
| objectID: "committee:123", | ||
| relation: "", | ||
| want: "committee:123#", | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if got := tt.policy.UserRelation(tt.objectID, tt.relation); got != tt.want { | ||
| t.Errorf("Policy.UserRelation() = %v, want %v", got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestPolicy_Integration(t *testing.T) { | ||
| // Test that all methods work together correctly | ||
| policy := Policy{ | ||
| Name: "visibility_policy", | ||
| Value: "basic_profile", | ||
| Relation: "allows_basic_profile", | ||
| } | ||
|
|
||
| // Validate the policy | ||
| if err := policy.Validate(); err != nil { | ||
| t.Errorf("Policy.Validate() failed for valid policy: %v", err) | ||
| } | ||
|
|
||
| // Get object ID | ||
| objectID := policy.ObjectID() | ||
| expectedObjectID := "visibility_policy:basic_profile" | ||
| if objectID != expectedObjectID { | ||
| t.Errorf("Policy.ObjectID() = %v, want %v", objectID, expectedObjectID) | ||
| } | ||
|
|
||
| // Get user relation | ||
| committeeID := "committee:f01dec3e-2611-482e-bffc-b4a6d9cd0afd" | ||
| userRelation := policy.UserRelation(committeeID, "member") | ||
| expectedUserRelation := "committee:f01dec3e-2611-482e-bffc-b4a6d9cd0afd#member" | ||
| if userRelation != expectedUserRelation { | ||
| t.Errorf("Policy.UserRelation() = %v, want %v", userRelation, expectedUserRelation) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.