From 90bb00b073245f0949e3a5e7464cc1dd26a9ef36 Mon Sep 17 00:00:00 2001 From: Omer Zuarets Date: Thu, 7 Sep 2023 20:06:45 +0300 Subject: [PATCH] add all-tenants check authorization query --- pkg/enforcement/all_tenants.go | 108 +++++++++++++++++++++++++++++++++ pkg/enforcement/consts.go | 14 +++-- pkg/permit/permit.go | 5 ++ pkg/tests/integration_test.go | 15 +++++ 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 pkg/enforcement/all_tenants.go diff --git a/pkg/enforcement/all_tenants.go b/pkg/enforcement/all_tenants.go new file mode 100644 index 0000000..ba24b39 --- /dev/null +++ b/pkg/enforcement/all_tenants.go @@ -0,0 +1,108 @@ +package enforcement + +import ( + "bytes" + "encoding/json" + "github.com/permitio/permit-golang/pkg/errors" + "go.uber.org/zap" + "io" + "net/http" +) + +type TenantDetails struct { + Key string `json:"key"` + Attributes map[string]interface{} `json:"attributes"` +} + +type AllTenantsCheckResponse struct { + CheckResponse + Tenant TenantDetails `json:"tenant"` +} + +type opaAllTenantsResponse struct { + Result *allowedTenantsResponse `json:"result"` +} + +type allowedTenantsResponse struct { + AllowedTenants *[]AllTenantsCheckResponse `json:"allowed_tenants"` +} + +func (e *PermitEnforcer) getAllTenantsCheckEndpoint() string { + return e.getEndpointByPolicyPackage(allTenantsPolicyPackage) +} + +func (e *PermitEnforcer) parseAllTenantsResponse(res *http.Response) ([]AllTenantsCheckResponse, error) { + var result []AllTenantsCheckResponse + bodyBytes, err := io.ReadAll(res.Body) + if err != nil { + permitError := errors.NewPermitUnexpectedError(err, res) + e.logger.Error("error reading Permit.Bulk() response from PDP", zap.Error(permitError)) + return nil, permitError + } + err = errors.HttpErrorHandle(err, res) + if err != nil { + e.logger.Error(string(bodyBytes), zap.Error(err)) + return nil, err + } + if e.config.GetOpaUrl() != "" { + opaStruct := &opaAllTenantsResponse{ + Result: &allowedTenantsResponse{ + &result, + }, + } + + if err := json.Unmarshal(bodyBytes, opaStruct); err != nil { + permitError := errors.NewPermitUnexpectedError(err, res) + e.logger.Error("error unmarshalling Permit.BulkCheck() response from OPA", zap.Error(permitError)) + return nil, err + } + } else { + pdpStruct := &allowedTenantsResponse{&result} + if err := json.Unmarshal(bodyBytes, &pdpStruct); err != nil { + permitError := errors.NewPermitUnexpectedError(err, res) + e.logger.Error("error unmarshalling Permit.BulkCheck response from PDP", zap.Error(permitError)) + return nil, permitError + } + } + + return result, nil +} + +func (e *PermitEnforcer) AllTenantsCheck(user User, action Action, resource Resource, additionalContext ...map[string]string) ([]TenantDetails, error) { + reqAuthValue := "Bearer " + e.config.GetToken() + + if additionalContext == nil { + additionalContext = make([]map[string]string, 0) + additionalContext = append(additionalContext, make(map[string]string)) + } + jsonCheckReq, err := newJsonCheckRequest(e.config.GetOpaUrl(), user, action, resource, additionalContext[0]) + if err != nil { + permitError := errors.NewPermitUnexpectedError(err, nil) + e.logger.Error("error marshalling Permit.AllTenantsCheck() request", zap.Error(permitError)) + return nil, permitError + } + reqBody := bytes.NewBuffer(jsonCheckReq) + httpRequest, err := http.NewRequest(reqMethod, e.getAllTenantsCheckEndpoint(), reqBody) + if err != nil { + permitError := errors.NewPermitUnexpectedError(err, nil) + e.logger.Error("error creating Permit.AllTenantsCheck() request", zap.Error(permitError)) + return nil, permitError + } + httpRequest.Header.Set(reqContentTypeKey, reqContentTypeValue) + httpRequest.Header.Set(reqAuthKey, reqAuthValue) + res, err := client.Do(httpRequest) + if err != nil { + permitError := errors.NewPermitUnexpectedError(err, res) + e.logger.Error("error sending Permit.AllTenantsCheck() request to PDP", zap.Error(permitError)) + return nil, permitError + } + results, err := e.parseAllTenantsResponse(res) + if err != nil { + return nil, err + } + allowResults := make([]TenantDetails, len(results)) + for result := range results { + allowResults[result] = results[result].Tenant + } + return allowResults, nil +} diff --git a/pkg/enforcement/consts.go b/pkg/enforcement/consts.go index 041f6db..62e91cb 100644 --- a/pkg/enforcement/consts.go +++ b/pkg/enforcement/consts.go @@ -19,13 +19,15 @@ type packageName string type sidecarPath string const ( - mainPolicyPackage packageName = "permit.root" - bulkPolicyPackage packageName = "permit.bulk" + mainPolicyPackage packageName = "permit.root" + bulkPolicyPackage packageName = "permit.bulk" + allTenantsPolicyPackage packageName = "permit.all_tenants" ) const ( - mainPolicy sidecarPath = "/allowed" - bulkPolicy sidecarPath = "/allowed/bulk" + mainPolicy sidecarPath = "/allowed" + bulkPolicy sidecarPath = "/allowed/bulk" + allTenantsPolicy sidecarPath = "/allowed/all-tenants" ) type checkOperationConfig struct { @@ -42,4 +44,8 @@ var policyMap = map[packageName]checkOperationConfig{ sidecarPath: bulkPolicy, opaPath: strings.Replace(string(bulkPolicyPackage), ".", "/", -1), }, + allTenantsPolicyPackage: { + sidecarPath: allTenantsPolicy, + opaPath: strings.Replace(string(allTenantsPolicyPackage), ".", "/", -1), + }, } diff --git a/pkg/permit/permit.go b/pkg/permit/permit.go index d6b59c9..4a39e23 100644 --- a/pkg/permit/permit.go +++ b/pkg/permit/permit.go @@ -40,8 +40,13 @@ func (c *Client) BulkCheck(requests ...enforcement.CheckRequest) ([]bool, error) return c.enforcement.BulkCheck(requests...) } +func (c *Client) AllTenantsCheck(user enforcement.User, action enforcement.Action, resource enforcement.Resource) ([]enforcement.TenantDetails, error) { + return c.enforcement.AllTenantsCheck(user, action, resource) +} + type PermitInterface interface { Check(user enforcement.User, action enforcement.Action, resource enforcement.Resource) (bool, error) BulkCheck(requests ...enforcement.CheckRequest) ([]bool, error) + AllTenantsCheck(request enforcement.CheckRequest) ([]enforcement.TenantDetails, error) SyncUser(ctx context.Context, user models.UserCreate) (*models.UserRead, error) } diff --git a/pkg/tests/integration_test.go b/pkg/tests/integration_test.go index 9fd7499..88510a6 100644 --- a/pkg/tests/integration_test.go +++ b/pkg/tests/integration_test.go @@ -100,6 +100,7 @@ func TestIntegration(t *testing.T) { actionKey := randKey("action") actionGroupKey := randKey("actiongroup") tenantKey := randKey("tenant") + secondTenantKey := randKey("tenant") token := os.Getenv("PDP_API_KEY") if token == "" { @@ -218,6 +219,11 @@ func TestIntegration(t *testing.T) { _, err = permitClient.Api.Tenants.Create(ctx, *tenantCreate) assert.NoError(t, err) + secondTenantCreate := models.NewTenantCreate(secondTenantKey, secondTenantKey) + secondTenantCreate.SetAttributes(map[string]interface{}{"isSecond": true}) + _, err = permitClient.Api.Tenants.Create(ctx, *secondTenantCreate) + assert.NoError(t, err) + tenants, err := permitClient.Api.Tenants.ListByAttributes(ctx, map[string]interface{}{ "marker": marker, }, 1, 100) @@ -240,4 +246,13 @@ func TestIntegration(t *testing.T) { allowed, err := permitClient.Check(userCheck, "read", resourceCheck) assert.NoError(t, err) assert.True(t, allowed) + + allowedTenants, err := permitClient.AllTenantsCheck( + userCheck, + "read", + resourceCheck.WithTenant("").Build(), + ) + assert.Len(t, allowedTenants, 1) + assert.Equal(t, tenantKey, allowedTenants[0].Key) + assert.InDeltaMapValues(t, allowedTenants[0].Attributes, tenantCreate.Attributes, 0.0) }