Skip to content

Commit

Permalink
Merge pull request #16 from permitio/omer/per-7848-go-sdk-new-check-a…
Browse files Browse the repository at this point in the history
…uthorization-queries

Add more authorization query options
  • Loading branch information
omer9564 authored Sep 10, 2023
2 parents 50ab270 + 435fb5d commit a5939c4
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 50 deletions.
108 changes: 108 additions & 0 deletions pkg/enforcement/all_tenants.go
Original file line number Diff line number Diff line change
@@ -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.AllTenantsCheck() 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.AllTenantsCheck() 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.AllTenantsCheck() 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
}
133 changes: 133 additions & 0 deletions pkg/enforcement/bulk_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package enforcement

import (
"bytes"
"encoding/json"
"fmt"
"github.com/permitio/permit-golang/pkg/errors"
"go.uber.org/zap"
"io"
"net/http"
)

type opaBulkResponse struct {
Result *allowBulkResponse `json:"result"`
}

type allowBulkResponse struct {
Allow *[]CheckResponse `json:"allow"`
}

func NewBulkCheckRequest(requests ...CheckRequest) []CheckRequest {
return requests
}

func NewBulkCheckRequestParameterized(user []User, action []Action, resource []Resource, context []map[string]string) ([]CheckRequest, error) {
if len(user) != len(action) || len(user) != len(resource) || len(user) != len(context) {
return nil, fmt.Errorf("user, action, resource and context must have the same length")
}
requests := make([]CheckRequest, len(user))
for i := range user {
requests[i] = CheckRequest{
User: user[i],
Action: action[i],
Resource: resource[i],
Context: context[i],
}
}
return requests, nil
}

func newJsonBulkCheckRequest(opaUrl string, requests ...CheckRequest) ([]byte, error) {
checkReq := NewBulkCheckRequest(requests...)
var genericCheckReq interface{} = checkReq
if opaUrl != "" {
genericCheckReq = &struct {
Input []CheckRequest `json:"input"`
}{checkReq}
}
jsonCheckReq, err := json.Marshal(genericCheckReq)
if err != nil {
return nil, err
}
return jsonCheckReq, nil
}

func (e *PermitEnforcer) getBulkCheckEndpoint() string {
return e.getEndpointByPolicyPackage(bulkPolicyPackage)
}

func (e *PermitEnforcer) parseBulkResponse(res *http.Response) ([]CheckResponse, error) {
var result []CheckResponse
bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
permitError := errors.NewPermitUnexpectedError(err, res)
e.logger.Error("error reading Permit.BulkCheck() 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 := &opaBulkResponse{
&allowBulkResponse{
&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 := &allowBulkResponse{&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) BulkCheck(requests ...CheckRequest) ([]bool, error) {
reqAuthValue := "Bearer " + e.config.GetToken()

jsonCheckReq, err := newJsonBulkCheckRequest(e.config.GetOpaUrl(), requests...)
if err != nil {
permitError := errors.NewPermitUnexpectedError(err, nil)
e.logger.Error("error marshalling Permit.BulkCheck() request", zap.Error(permitError))
return nil, permitError
}
reqBody := bytes.NewBuffer(jsonCheckReq)
httpRequest, err := http.NewRequest(reqMethod, e.getBulkCheckEndpoint(), reqBody)
if err != nil {
permitError := errors.NewPermitUnexpectedError(err, nil)
e.logger.Error("error creating Permit.BulkCheck() 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.BulkCheck() request to PDP", zap.Error(permitError))
return nil, permitError
}
results, err := e.parseBulkResponse(res)
if err != nil {
return nil, err
}
if len(results) != len(requests) {
return nil, errors.NewPermitUnexpectedError(fmt.Errorf("unexpected number of results"), res)
}
allowResults := make([]bool, len(results))
for result := range results {
allowResults[result] = results[result].Allow
}
return allowResults, nil
}
12 changes: 1 addition & 11 deletions pkg/enforcement/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ func newJsonCheckRequest(opaUrl string, user User, action Action, resource Resou
}

func (e *PermitEnforcer) getCheckEndpoint() string {
if opaUrl := e.config.GetOpaUrl(); opaUrl != "" {
return opaUrl + "/v1/data/" + mainPolicyPath
} else {
return e.config.GetPdpUrl() + "/allowed"
}
return e.getEndpointByPolicyPackage(mainPolicyPackage)
}

func (e *PermitEnforcer) parseResponse(res *http.Response) (*CheckResponse, error) {
Expand Down Expand Up @@ -100,12 +96,6 @@ func (e *PermitEnforcer) parseResponse(res *http.Response) (*CheckResponse, erro
}

func (e *PermitEnforcer) Check(user User, action Action, resource Resource, additionalContext ...map[string]string) (bool, error) {
const (
reqMethod = "POST"
reqContentTypeKey = "Content-Type"
reqContentTypeValue = "application/json"
reqAuthKey = "Authorization"
)
reqAuthValue := "Bearer " + e.config.GetToken()

if additionalContext == nil {
Expand Down
49 changes: 44 additions & 5 deletions pkg/enforcement/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,49 @@ package enforcement
import "strings"

const (
DefaultTenant = "default"
DefaultTimeout = 30
AllowKey = "allow"
mainPolicyPackage = "permit.root"
DefaultTenant = "default"
DefaultTimeout = 30
AllowKey = "allow"
)

var mainPolicyPath = strings.Replace(mainPolicyPackage, ".", "/", -1)
const (
reqMethod = "POST"
reqContentTypeKey = "Content-Type"
reqContentTypeValue = "application/json"
reqAuthKey = "Authorization"
)

type packageName string
type sidecarPath string

const (
mainPolicyPackage packageName = "permit.root"
bulkPolicyPackage packageName = "permit.bulk"
allTenantsPolicyPackage packageName = "permit.all_tenants"
)

const (
mainPolicy sidecarPath = "/allowed"
bulkPolicy sidecarPath = "/allowed/bulk"
allTenantsPolicy sidecarPath = "/allowed/all-tenants"
)

type checkOperationConfig struct {
sidecarPath sidecarPath
opaPath string
}

var policyMap = map[packageName]checkOperationConfig{
mainPolicyPackage: {
sidecarPath: mainPolicy,
opaPath: strings.Replace(string(mainPolicyPackage), ".", "/", -1),
},
bulkPolicyPackage: {
sidecarPath: bulkPolicy,
opaPath: strings.Replace(string(bulkPolicyPackage), ".", "/", -1),
},
allTenantsPolicyPackage: {
sidecarPath: allTenantsPolicy,
opaPath: strings.Replace(string(allTenantsPolicyPackage), ".", "/", -1),
},
}
9 changes: 9 additions & 0 deletions pkg/enforcement/enforcement.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ func NewPermitEnforcerClient(config *config.PermitConfig) *PermitEnforcer {
},
}
}

func (e *PermitEnforcer) getEndpointByPolicyPackage(name packageName) string {
operationConfig := policyMap[name]
if e.config.GetOpaUrl() != "" {
return e.config.GetOpaUrl() + "/v1/data/" + operationConfig.opaPath
} else {
return e.config.GetPdpUrl() + string(operationConfig.sidecarPath)
}
}
10 changes: 10 additions & 0 deletions pkg/permit/permit.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,17 @@ func (c *Client) Check(user enforcement.User, action enforcement.Action, resourc
return c.enforcement.Check(user, action, resource)
}

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)
}
Loading

0 comments on commit a5939c4

Please sign in to comment.