From 6e1b1888c1f56f7051293bfbdee9775c029b19fb Mon Sep 17 00:00:00 2001 From: talaharoni Date: Thu, 4 Apr 2024 12:44:50 +0300 Subject: [PATCH 1/4] Support AuthZ WhatCanTargetAccessWithRelation API --- descope/api/client.go | 38 +++++++++++++++++------------- descope/internal/mgmt/authz.go | 43 ++++++++++++++++++++++++++++++++++ descope/sdk/mgmt.go | 3 +++ 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/descope/api/client.go b/descope/api/client.go index 6d771f31..b3dc7038 100644 --- a/descope/api/client.go +++ b/descope/api/client.go @@ -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", @@ -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 { @@ -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) } diff --git a/descope/internal/mgmt/authz.go b/descope/internal/mgmt/authz.go index 0294a07c..f240c6a0 100644 --- a/descope/internal/mgmt/authz.go +++ b/descope/internal/mgmt/authz.go @@ -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") @@ -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() { diff --git a/descope/sdk/mgmt.go b/descope/sdk/mgmt.go index bbdd5767..4c50c5a8 100644 --- a/descope/sdk/mgmt.go +++ b/descope/sdk/mgmt.go @@ -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) From 02a9ce12fdd5e7903ae6976d37d41ef2f3672db8 Mon Sep 17 00:00:00 2001 From: talaharoni Date: Thu, 4 Apr 2024 13:05:35 +0300 Subject: [PATCH 2/4] add test --- descope/internal/mgmt/authz_test.go | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/descope/internal/mgmt/authz_test.go b/descope/internal/mgmt/authz_test.go index 9b2f0705..7468cacc 100644 --- a/descope/internal/mgmt/authz_test.go +++ b/descope/internal/mgmt/authz_test.go @@ -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)) From b104e082613fd77c5f8bf3b99f31d2f52f479535 Mon Sep 17 00:00:00 2001 From: talaharoni Date: Thu, 4 Apr 2024 13:16:53 +0300 Subject: [PATCH 3/4] add mock --- descope/tests/mocks/mgmt/managementmock.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/descope/tests/mocks/mgmt/managementmock.go b/descope/tests/mocks/mgmt/managementmock.go index 6ee25b2a..c3b5988a 100644 --- a/descope/tests/mocks/mgmt/managementmock.go +++ b/descope/tests/mocks/mgmt/managementmock.go @@ -1280,6 +1280,10 @@ type MockAuthz struct { WhatCanTargetAccessResponse []*descope.AuthzRelation WhatCanTargetAccessError error + WhatCanTargetAccessWithRelationAssert func(target string) + WhatCanTargetAccessWithRelationResponse []*descope.AuthzRelation + WhatCanTargetAccessWithRelationError error + GetModifiedAssert func(since time.Time) GetModifiedResponse *descope.AuthzModified GetModifiedError error @@ -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) + } + return m.WhatCanTargetAccessWithRelationResponse, m.WhatCanTargetAccessWithRelationError +} + func (m *MockAuthz) GetModified(_ context.Context, since time.Time) (*descope.AuthzModified, error) { if m.GetModifiedAssert != nil { m.GetModifiedAssert(since) From 0ba6f3311835db15d02fba5e7654de852c0c70e1 Mon Sep 17 00:00:00 2001 From: talaharoni Date: Thu, 4 Apr 2024 13:21:08 +0300 Subject: [PATCH 4/4] update mock assert func --- descope/tests/mocks/mgmt/managementmock.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/descope/tests/mocks/mgmt/managementmock.go b/descope/tests/mocks/mgmt/managementmock.go index c3b5988a..0b852bd8 100644 --- a/descope/tests/mocks/mgmt/managementmock.go +++ b/descope/tests/mocks/mgmt/managementmock.go @@ -1280,7 +1280,7 @@ type MockAuthz struct { WhatCanTargetAccessResponse []*descope.AuthzRelation WhatCanTargetAccessError error - WhatCanTargetAccessWithRelationAssert func(target string) + WhatCanTargetAccessWithRelationAssert func(target, relationDefinition, namespace string) WhatCanTargetAccessWithRelationResponse []*descope.AuthzRelation WhatCanTargetAccessWithRelationError error @@ -1390,7 +1390,7 @@ func (m *MockAuthz) WhatCanTargetAccess(_ context.Context, target string) ([]*de func (m *MockAuthz) WhatCanTargetAccessWithRelation(_ context.Context, target, relationDefinition, namespace string) ([]*descope.AuthzRelation, error) { if m.WhatCanTargetAccessWithRelationAssert != nil { - m.WhatCanTargetAccessWithRelationAssert(target) + m.WhatCanTargetAccessWithRelationAssert(target, relationDefinition, namespace) } return m.WhatCanTargetAccessWithRelationResponse, m.WhatCanTargetAccessWithRelationError }