Skip to content
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

Support AuthZ WhatCanTargetAccessWithRelation API #423

Merged
merged 5 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions descope/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ var (
authzREResource: "mgmt/authz/re/resource",
authzRETargets: "mgmt/authz/re/targets",
authzRETargetAll: "mgmt/authz/re/targetall",
authzRETargetWithRelation: "mgmt/authz/re/targetwithrelation",
authzGetModified: "mgmt/authz/getmodified",
},
logout: "auth/logout",
Expand Down Expand Up @@ -369,22 +370,23 @@ type mgmtEndpoints struct {

auditSearch string

authzSchemaSave string
authzSchemaDelete string
authzSchemaLoad string
authzNSSave string
authzNSDelete string
authzRDSave string
authzRDDelete string
authzRECreate string
authzREDelete string
authzREDeleteResources string
authzREHasRelations string
authzREWho string
authzREResource string
authzRETargets string
authzRETargetAll string
authzGetModified string
authzSchemaSave string
authzSchemaDelete string
authzSchemaLoad string
authzNSSave string
authzNSDelete string
authzRDSave string
authzRDDelete string
authzRECreate string
authzREDelete string
authzREDeleteResources string
authzREHasRelations string
authzREWho string
authzREResource string
authzRETargets string
authzRETargetAll string
authzRETargetWithRelation string
authzGetModified string
}

func (e *endpoints) SignInOTP() string {
Expand Down Expand Up @@ -1017,6 +1019,10 @@ func (e *endpoints) ManagementAuthzRETargetAll() string {
return path.Join(e.version, e.mgmt.authzRETargetAll)
}

func (e *endpoints) ManagementAuthzRETargetWithRelation() string {
return path.Join(e.version, e.mgmt.authzRETargetWithRelation)
}

func (e *endpoints) ManagementAuthzGetModified() string {
return path.Join(e.version, e.mgmt.authzGetModified)
}
Expand Down
43 changes: 43 additions & 0 deletions descope/internal/mgmt/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ type relationsResponse struct {
Relations []*descope.AuthzRelation `json:"relations"`
}

type resourcesResponse struct {
Resources []string `json:"resources"`
}

func (a *authz) ResourceRelations(ctx context.Context, resource string) ([]*descope.AuthzRelation, error) {
if resource == "" {
return nil, utils.NewInvalidArgumentError("resource")
Expand Down Expand Up @@ -277,6 +281,45 @@ func (a *authz) WhatCanTargetAccess(ctx context.Context, target string) ([]*desc
return response.Relations, nil
}

func (a *authz) WhatCanTargetAccessWithRelation(ctx context.Context, target, relationDefinition, namespace string) ([]*descope.AuthzRelation, error) {
if target == "" {
return nil, utils.NewInvalidArgumentError("target")
}
if relationDefinition == "" {
return nil, utils.NewInvalidArgumentError("relationDefinition")
}
if namespace == "" {
return nil, utils.NewInvalidArgumentError("namespace")
}
body := map[string]any{
"target": target,
"relationDefinition": relationDefinition,
"namespace": namespace,
}
res, err := a.client.DoPostRequest(ctx, api.Routes.ManagementAuthzRETargetWithRelation(), body, nil, a.conf.ManagementKey)
if err != nil {
// notest
return nil, err
}
var response *resourcesResponse
err = utils.Unmarshal([]byte(res.BodyStr), &response)
if err != nil {
// notest
return nil, err
}

var resp []*descope.AuthzRelation
for _, resource := range response.Resources {
resp = append(resp, &descope.AuthzRelation{
Resource: resource,
Target: target,
RelationDefinition: relationDefinition,
Namespace: namespace,
})
}
return resp, nil
}

func (a *authz) GetModified(ctx context.Context, since time.Time) (*descope.AuthzModified, error) {
body := map[string]any{}
if !since.IsZero() {
Expand Down
40 changes: 40 additions & 0 deletions descope/internal/mgmt/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,46 @@ func TestWhatCanTargetAccessMissingArgument(t *testing.T) {
require.ErrorContains(t, err, utils.NewInvalidArgumentError("target").Message)
}

func TestWhatCanTargetAccessWithRelationSuccess(t *testing.T) {
response := []*descope.AuthzRelation{
{
Resource: "r1",
RelationDefinition: "rd",
Namespace: "n",
Target: "u1",
},
{
Resource: "r2",
RelationDefinition: "rd",
Namespace: "n",
Target: "u1",
},
}
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "u1", req["target"])
require.Equal(t, "rd", req["relationDefinition"])
require.Equal(t, "n", req["namespace"])
}, map[string]any{"resources": []string{"r1", "r2"}}))
res, err := mgmt.Authz().WhatCanTargetAccessWithRelation(context.Background(), "u1", "rd", "n")
require.NoError(t, err)
assert.EqualValues(t, response, res)
}

func TestWhatCanTargetAccessWithRelationMissingArgument(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
_, err := mgmt.Authz().WhatCanTargetAccessWithRelation(context.Background(), "", "", "")
require.ErrorContains(t, err, utils.NewInvalidArgumentError("target").Message)

_, err = mgmt.Authz().WhatCanTargetAccessWithRelation(context.Background(), "tar", "", "")
require.ErrorContains(t, err, utils.NewInvalidArgumentError("relationDefinition").Message)

_, err = mgmt.Authz().WhatCanTargetAccessWithRelation(context.Background(), "tar", "def", "")
require.ErrorContains(t, err, utils.NewInvalidArgumentError("namespace").Message)
}

func TestGetModifiedWrongArgument(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
_, err := mgmt.Authz().GetModified(context.Background(), time.Now().Add(10*time.Second))
Expand Down
3 changes: 3 additions & 0 deletions descope/sdk/mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,9 @@ type Authz interface {
// WhatCanTargetAccess returns the list of all relations for the given target including derived relations from the schema tree.
WhatCanTargetAccess(ctx context.Context, target string) ([]*descope.AuthzRelation, error)

// WhatCanTargetAccessWithRelation returns the list of all resources that the target has the given relation to including all derived relations
WhatCanTargetAccessWithRelation(ctx context.Context, target, relationDefinition, namespace string) ([]*descope.AuthzRelation, error)

// GetModified list of targets and resources changed since the given date
// Should be used to invalidate local caches
GetModified(ctx context.Context, since time.Time) (*descope.AuthzModified, error)
Expand Down
11 changes: 11 additions & 0 deletions descope/tests/mocks/mgmt/managementmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,10 @@ type MockAuthz struct {
WhatCanTargetAccessResponse []*descope.AuthzRelation
WhatCanTargetAccessError error

WhatCanTargetAccessWithRelationAssert func(target, relationDefinition, namespace string)
WhatCanTargetAccessWithRelationResponse []*descope.AuthzRelation
WhatCanTargetAccessWithRelationError error

GetModifiedAssert func(since time.Time)
GetModifiedResponse *descope.AuthzModified
GetModifiedError error
Expand Down Expand Up @@ -1384,6 +1388,13 @@ func (m *MockAuthz) WhatCanTargetAccess(_ context.Context, target string) ([]*de
return m.WhatCanTargetAccessResponse, m.WhatCanTargetAccessError
}

func (m *MockAuthz) WhatCanTargetAccessWithRelation(_ context.Context, target, relationDefinition, namespace string) ([]*descope.AuthzRelation, error) {
if m.WhatCanTargetAccessWithRelationAssert != nil {
m.WhatCanTargetAccessWithRelationAssert(target, relationDefinition, namespace)
}
return m.WhatCanTargetAccessWithRelationResponse, m.WhatCanTargetAccessWithRelationError
}

func (m *MockAuthz) GetModified(_ context.Context, since time.Time) (*descope.AuthzModified, error) {
if m.GetModifiedAssert != nil {
m.GetModifiedAssert(since)
Expand Down
Loading