Skip to content

Commit

Permalink
feat: Inspect policies in the store (#47)
Browse files Browse the repository at this point in the history
To prevent name conflicts between function names of the `type
InspectPoliciesOption func(*requestv1.InspectPoliciesRequest)` and `type
ListPoliciesOption func(*requestv1.ListPoliciesRequest)`, I prefixed the
formers' function names with `InspectPoliciesWith`. I am not sure if
this is a good idea or not.

---------

Signed-off-by: Oğuzhan Durgun <oguzhandurgun95@gmail.com>
  • Loading branch information
oguzhand95 authored Apr 8, 2024
1 parent 17291e4 commit 00afe19
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 25 deletions.
4 changes: 3 additions & 1 deletion cerbos/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"

policyv1 "github.com/cerbos/cerbos/api/genpb/cerbos/policy/v1"
responsev1 "github.com/cerbos/cerbos/api/genpb/cerbos/response/v1"
schemav1 "github.com/cerbos/cerbos/api/genpb/cerbos/schema/v1"
)

Expand Down Expand Up @@ -43,7 +44,8 @@ type PrincipalContext interface {
type AdminClient interface {
AddOrUpdatePolicy(ctx context.Context, policies *PolicySet) error
AuditLogs(ctx context.Context, opts AuditLogOptions) (<-chan *AuditLogEntry, error)
ListPolicies(ctx context.Context, opts ...ListPoliciesOption) ([]string, error)
ListPolicies(ctx context.Context, opts ...FilterOption) ([]string, error)
InspectPolicies(ctx context.Context, opts ...FilterOption) (*responsev1.InspectPoliciesResponse, error)
GetPolicy(ctx context.Context, ids ...string) ([]*policyv1.Policy, error)
DisablePolicy(ctx context.Context, ids ...string) (uint32, error)
EnablePolicy(ctx context.Context, ids ...string) (uint32, error)
Expand Down
35 changes: 32 additions & 3 deletions cerbos/grpc_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,16 @@ func (c *GRPCAdminClient) auditLogs(ctx context.Context, opts AuditLogOptions) (
return resp, nil
}

func (c *GRPCAdminClient) ListPolicies(ctx context.Context, opts ...ListPoliciesOption) ([]string, error) {
req := &requestv1.ListPoliciesRequest{}
func (c *GRPCAdminClient) ListPolicies(ctx context.Context, opts ...FilterOption) ([]string, error) {
options := &FilterOptions{}
for _, opt := range opts {
opt(req)
opt(options)
}
req := &requestv1.ListPoliciesRequest{
IncludeDisabled: options.IncludeDisabled,
NameRegexp: options.NameRegexp,
ScopeRegexp: options.ScopeRegexp,
VersionRegexp: options.VersionRegexp,
}
if err := internal.Validate(req); err != nil {
return nil, fmt.Errorf("could not validate list policies request: %w", err)
Expand All @@ -187,6 +193,29 @@ func (c *GRPCAdminClient) ListPolicies(ctx context.Context, opts ...ListPolicies
return p.PolicyIds, nil
}

func (c *GRPCAdminClient) InspectPolicies(ctx context.Context, opts ...FilterOption) (*responsev1.InspectPoliciesResponse, error) {
options := &FilterOptions{}
for _, opt := range opts {
opt(options)
}
req := &requestv1.InspectPoliciesRequest{
IncludeDisabled: options.IncludeDisabled,
NameRegexp: options.NameRegexp,
ScopeRegexp: options.ScopeRegexp,
VersionRegexp: options.VersionRegexp,
}
if err := internal.Validate(req); err != nil {
return nil, fmt.Errorf("could not validate get inspect policies request: %w", err)
}

resp, err := c.client.InspectPolicies(metadata.AppendToOutgoingContext(ctx, c.headers...), req, grpc.PerRPCCredentials(c.creds))
if err != nil {
return nil, fmt.Errorf("could not inspect policies: %w", err)
}

return resp, nil
}

func (c *GRPCAdminClient) GetPolicy(ctx context.Context, ids ...string) ([]*policyv1.Policy, error) {
req := &requestv1.GetPolicyRequest{
Id: ids,
Expand Down
91 changes: 86 additions & 5 deletions cerbos/grpc_admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func TestAdminClient(t *testing.T) {
t.Run("ListPolicies", func(t *testing.T) {
testCases := []struct {
name string
options []ListPoliciesOption
options []FilterOption
want map[string]string
}{
{
Expand All @@ -170,7 +170,7 @@ func TestAdminClient(t *testing.T) {
},
{
name: "NameRegexp",
options: []ListPoliciesOption{WithNameRegexp("leave_req")},
options: []FilterOption{WithNameRegexp("leave_req")},
want: map[string]string{
"resource.leave_request.v20210210": "",
"resource.leave_request.vdefault": "",
Expand All @@ -181,7 +181,7 @@ func TestAdminClient(t *testing.T) {
},
{
name: "ScopeRegexp",
options: []ListPoliciesOption{WithScopeRegexp("acme")},
options: []FilterOption{WithScopeRegexp("acme")},
want: map[string]string{
"principal.donald_duck.vdefault/acme": "",
"principal.donald_duck.vdefault/acme.hr": "",
Expand All @@ -192,14 +192,14 @@ func TestAdminClient(t *testing.T) {
},
{
name: "VersionRegexp",
options: []ListPoliciesOption{WithVersionRegexp(`\d+`)},
options: []FilterOption{WithVersionRegexp(`\d+`)},
want: map[string]string{
"resource.leave_request.v20210210": "",
},
},
{
name: "AllRegexp",
options: []ListPoliciesOption{WithNameRegexp(`.*`), WithScopeRegexp(`.*`), WithVersionRegexp("def")},
options: []FilterOption{WithNameRegexp(`.*`), WithScopeRegexp(`.*`), WithVersionRegexp("def")},
want: map[string]string{
"principal.donald_duck.vdefault": "",
"principal.donald_duck.vdefault/acme": "",
Expand All @@ -225,6 +225,87 @@ func TestAdminClient(t *testing.T) {
}
})

t.Run("InspectPolicies", func(t *testing.T) {
testCases := []struct {
name string
options []FilterOption
want map[string][]string
}{
{
name: "NoFilter",
want: map[string][]string{
"principal.donald_duck.vdefault": {"*"},
"principal.donald_duck.vdefault/acme": {"*"},
"principal.donald_duck.vdefault/acme.hr": {"view:*"},
"resource.leave_request.v20210210": {"*", "approve", "create", "defer", "delete", "remind", "view", "view:*", "view:public"},
"resource.leave_request.vdefault": {"*"},
"resource.leave_request.vdefault/acme": {"create", "view:public"},
"resource.leave_request.vdefault/acme.hr": {"approve", "defer", "delete", "view:*"},
"resource.leave_request.vdefault/acme.hr.uk": {"defer", "delete"},
},
},
{
name: "NameRegexp",
options: []FilterOption{WithNameRegexp("leave_req")},
want: map[string][]string{
"resource.leave_request.v20210210": {"*", "approve", "create", "defer", "delete", "remind", "view", "view:*", "view:public"},
"resource.leave_request.vdefault": {"*"},
"resource.leave_request.vdefault/acme": {"create", "view:public"},
"resource.leave_request.vdefault/acme.hr": {"approve", "defer", "delete", "view:*"},
"resource.leave_request.vdefault/acme.hr.uk": {"defer", "delete"},
},
},
{
name: "ScopeRegexp",
options: []FilterOption{WithScopeRegexp("acme")},
want: map[string][]string{
"principal.donald_duck.vdefault/acme": {"*"},
"principal.donald_duck.vdefault/acme.hr": {"view:*"},
"resource.leave_request.vdefault/acme": {"create", "view:public"},
"resource.leave_request.vdefault/acme.hr": {"approve", "defer", "delete", "view:*"},
"resource.leave_request.vdefault/acme.hr.uk": {"defer", "delete"},
},
},
{
name: "VersionRegexp",
options: []FilterOption{WithVersionRegexp(`\d+`)},
want: map[string][]string{
"resource.leave_request.v20210210": {"*", "approve", "create", "defer", "delete", "remind", "view", "view:*", "view:public"},
},
},
{
name: "AllRegexp",
options: []FilterOption{WithNameRegexp(`.*`), WithScopeRegexp(`.*`), WithVersionRegexp("def")},
want: map[string][]string{
"principal.donald_duck.vdefault": {"*"},
"principal.donald_duck.vdefault/acme": {"*"},
"principal.donald_duck.vdefault/acme.hr": {"view:*"},
"resource.leave_request.vdefault": {"*"},
"resource.leave_request.vdefault/acme": {"create", "view:public"},
"resource.leave_request.vdefault/acme.hr": {"approve", "defer", "delete", "view:*"},
"resource.leave_request.vdefault/acme.hr.uk": {"defer", "delete"},
},
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
have, err := ac.InspectPolicies(context.Background(), tc.options...)
require.NoError(t, err)
require.NotNil(t, have)
require.NotNil(t, have.Results)
for fqn, actions := range tc.want {
t.Run(fqn, func(t *testing.T) {
require.NotNil(t, have.Results[fqn])
require.Len(t, have.Results[fqn].Actions, len(actions))
require.ElementsMatch(t, have.Results[fqn].Actions, actions)
})
}
})
}
})

t.Run("AddOrUpdateSchema", func(t *testing.T) {
ss := NewSchemaSet()
for k, s := range schemas {
Expand Down
36 changes: 23 additions & 13 deletions cerbos/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -1212,29 +1212,39 @@ type PlanResourcesResponse struct {
}

type (
ListPoliciesOption func(*requestv1.ListPoliciesRequest)
FilterOptions struct {
NameRegexp string
ScopeRegexp string
VersionRegexp string
IncludeDisabled bool
}
// FilterOption allows filtering policies while calling InspectPolicies and ListPolicies.
FilterOption func(*FilterOptions)
// ListPoliciesOption allows filtering policies while calling ListPolicies
// Deprecated: ListPoliciesOption is deprecated, use FilterOption instead.
ListPoliciesOption = FilterOption
)

func WithIncludeDisabled() ListPoliciesOption {
return func(request *requestv1.ListPoliciesRequest) {
request.IncludeDisabled = true
func WithIncludeDisabled() FilterOption {
return func(fo *FilterOptions) {
fo.IncludeDisabled = true
}
}

func WithNameRegexp(re string) ListPoliciesOption {
return func(request *requestv1.ListPoliciesRequest) {
request.NameRegexp = re
func WithNameRegexp(re string) FilterOption {
return func(fo *FilterOptions) {
fo.NameRegexp = re
}
}

func WithScopeRegexp(re string) ListPoliciesOption {
return func(request *requestv1.ListPoliciesRequest) {
request.ScopeRegexp = re
func WithScopeRegexp(re string) FilterOption {
return func(fo *FilterOptions) {
fo.ScopeRegexp = re
}
}

func WithVersionRegexp(v string) ListPoliciesOption {
return func(request *requestv1.ListPoliciesRequest) {
request.VersionRegexp = v
func WithVersionRegexp(v string) FilterOption {
return func(fo *FilterOptions) {
fo.VersionRegexp = v
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.20
require (
github.com/bufbuild/protovalidate-go v0.6.1
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cerbos/cerbos/api/genpb v0.34.0
github.com/cerbos/cerbos/api/genpb v0.34.1-0.20240404120519-19d38a48998f
github.com/ghodss/yaml v1.0.0
github.com/google/go-cmp v0.6.0
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ github.com/bufbuild/protovalidate-go v0.6.1 h1:uzW8r0CDvqApUChNj87VzZVoQSKhcVdw5
github.com/bufbuild/protovalidate-go v0.6.1/go.mod h1:4BR3rKEJiUiTy+sqsusFn2ladOf0kYmA2Reo6BHSBgQ=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cerbos/cerbos/api/genpb v0.34.0 h1:HY8k9HVHv000EKU61KrhAia5TmjW4sX2AXNee4I+DZg=
github.com/cerbos/cerbos/api/genpb v0.34.0/go.mod h1:KEUMaRkMsCvEcOI8aptMFscKh6H2bfWgjjjSmRWKg8g=
github.com/cerbos/cerbos/api/genpb v0.34.1-0.20240404120519-19d38a48998f h1:2QY32KPwny2zJCE7fWBdarci+r6hwYeVeHp7xK/BYq8=
github.com/cerbos/cerbos/api/genpb v0.34.1-0.20240404120519-19d38a48998f/go.mod h1:DcozdAIUztxXwtVs88gGgdyCITru7WCTF9vGA6j+H8k=
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
Expand Down

0 comments on commit 00afe19

Please sign in to comment.