From 4e73db34e4dbd2e0f26657be6ec3de4e9dc6e0d5 Mon Sep 17 00:00:00 2001 From: Karan Kajla Date: Sat, 7 Oct 2023 12:19:32 -0700 Subject: [PATCH] Add `isImplicit` boolean flag to check result (#233) --- pkg/authz/check/service.go | 24 ++++++--- pkg/authz/check/spec.go | 1 + tests/authz-policy.json | 27 ++++++---- tests/authz-wildcard.json | 72 ++++++++++++++++++--------- tests/authz-wildcard2.json | 9 ++-- tests/authz.json | 20 +++++--- tests/pricing-tiers-and-features.json | 24 ++++++--- 7 files changed, 119 insertions(+), 58 deletions(-) diff --git a/pkg/authz/check/service.go b/pkg/authz/check/service.go index a1d61e93..736f9770 100644 --- a/pkg/authz/check/service.go +++ b/pkg/authz/check/service.go @@ -208,8 +208,9 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf if warrantCheck.Op == objecttype.InheritIfAllOf { var processingTime int64 + var isImplicit bool for _, warrantSpec := range warrantCheck.Warrants { - match, decisionPath, err := svc.Check(ctx, authInfo, CheckSpec{ + match, decisionPath, implicit, err := svc.Check(ctx, authInfo, CheckSpec{ CheckWarrantSpec: warrantSpec, Debug: warrantCheck.Debug, }) @@ -217,6 +218,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf return nil, err } + isImplicit = isImplicit || implicit if warrantCheck.Debug { checkResult.ProcessingTime = processingTime + time.Since(start).Milliseconds() if len(decisionPath) > 0 { @@ -238,6 +240,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusForbidden checkResult.Result = NotAuthorized + checkResult.IsImplicit = false return &checkResult, nil } @@ -249,13 +252,14 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusOK checkResult.Result = Authorized + checkResult.IsImplicit = isImplicit return &checkResult, nil } if warrantCheck.Op == objecttype.InheritIfAnyOf { var processingTime int64 for _, warrantSpec := range warrantCheck.Warrants { - match, decisionPath, err := svc.Check(ctx, authInfo, CheckSpec{ + match, decisionPath, isImplicit, err := svc.Check(ctx, authInfo, CheckSpec{ CheckWarrantSpec: warrantSpec, Debug: warrantCheck.Debug, }) @@ -284,6 +288,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusOK checkResult.Result = Authorized + checkResult.IsImplicit = isImplicit return &checkResult, nil } @@ -297,6 +302,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusForbidden checkResult.Result = NotAuthorized + checkResult.IsImplicit = false return &checkResult, nil } @@ -305,7 +311,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf } warrantSpec := warrantCheck.Warrants[0] - match, decisionPath, err := svc.Check(ctx, authInfo, CheckSpec{ + match, decisionPath, isImplicit, err := svc.Check(ctx, authInfo, CheckSpec{ CheckWarrantSpec: warrantSpec, Debug: warrantCheck.Debug, }) @@ -334,6 +340,7 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusOK checkResult.Result = Authorized + checkResult.IsImplicit = isImplicit return &checkResult, nil } @@ -344,11 +351,12 @@ func (svc CheckService) CheckMany(ctx context.Context, authInfo *service.AuthInf checkResult.Code = http.StatusForbidden checkResult.Result = NotAuthorized + checkResult.IsImplicit = false return &checkResult, nil } // Check returns true if the subject has a warrant (explicitly or implicitly) for given objectType:objectId#relation and context. -func (svc CheckService) Check(ctx context.Context, authInfo *service.AuthInfo, warrantCheck CheckSpec) (bool, []warrant.WarrantSpec, error) { +func (svc CheckService) Check(ctx context.Context, authInfo *service.AuthInfo, warrantCheck CheckSpec) (bool, []warrant.WarrantSpec, bool, error) { // Used to automatically append tenant context for session token w/ tenantId checks if authInfo != nil && authInfo.TenantId != "" { if warrantCheck.CheckWarrantSpec.Context == nil { @@ -365,7 +373,7 @@ func (svc CheckService) Check(ctx context.Context, authInfo *service.AuthInfo, w checkCtx, err := svc.CreateCheckContext(ctx) if err != nil { - return false, nil, err + return false, nil, false, err } childCtx, cancelFunc := context.WithTimeout(checkCtx, svc.CheckConfig.Timeout) defer cancelFunc() @@ -377,14 +385,14 @@ func (svc CheckService) Check(ctx context.Context, authInfo *service.AuthInfo, w result := <-resultsC if result.Err != nil { - return false, nil, result.Err + return false, nil, false, result.Err } if result.Matched { - return true, result.DecisionPath, nil + return true, result.DecisionPath, len(result.DecisionPath) != 1 || result.DecisionPath[0].Relation != warrantCheck.Relation, nil } - return false, nil, nil + return false, nil, false, nil } type result struct { diff --git a/pkg/authz/check/spec.go b/pkg/authz/check/spec.go index 57dbd5dd..36bfa1af 100644 --- a/pkg/authz/check/spec.go +++ b/pkg/authz/check/spec.go @@ -128,6 +128,7 @@ func (spec SessionCheckManySpec) ToMap() map[string]interface{} { type CheckResultSpec struct { Code int64 `json:"code,omitempty"` Result string `json:"result"` + IsImplicit bool `json:"isImplicit"` ProcessingTime int64 `json:"processingTime,omitempty"` DecisionPath map[string][]warrant.WarrantSpec `json:"decisionPath,omitempty"` } diff --git a/tests/authz-policy.json b/tests/authz-policy.json index 00b4f8f6..15396d6a 100644 --- a/tests/authz-policy.json +++ b/tests/authz-policy.json @@ -202,7 +202,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -232,7 +233,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -366,7 +368,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -396,7 +399,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -479,7 +483,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -512,7 +517,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -637,7 +643,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -680,7 +687,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -707,7 +715,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, diff --git a/tests/authz-wildcard.json b/tests/authz-wildcard.json index f21fe29a..b61aa96f 100644 --- a/tests/authz-wildcard.json +++ b/tests/authz-wildcard.json @@ -506,7 +506,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -537,7 +538,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -568,7 +570,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -599,7 +602,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -630,7 +634,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, @@ -661,7 +666,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -692,7 +698,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -723,7 +730,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -754,7 +762,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -785,7 +794,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -816,7 +826,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -847,7 +858,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -878,7 +890,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -909,7 +922,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -940,7 +954,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -971,7 +986,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -1002,7 +1018,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -1033,7 +1050,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1064,7 +1082,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1095,7 +1114,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1126,7 +1146,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1157,7 +1178,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1188,7 +1210,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -1219,7 +1242,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, diff --git a/tests/authz-wildcard2.json b/tests/authz-wildcard2.json index 277a3a39..e01064da 100644 --- a/tests/authz-wildcard2.json +++ b/tests/authz-wildcard2.json @@ -495,7 +495,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -523,7 +524,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -551,7 +553,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": false } } }, diff --git a/tests/authz.json b/tests/authz.json index ead46a55..c40777b6 100644 --- a/tests/authz.json +++ b/tests/authz.json @@ -77,7 +77,7 @@ } }, { - "name": "createUserUsera", + "name": "createUserUserA", "request": { "method": "POST", "url": "/v1/users", @@ -95,7 +95,7 @@ } }, { - "name": "createUserUserb", + "name": "createUserUserB", "request": { "method": "POST", "url": "/v1/users", @@ -425,6 +425,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": true, "decisionPath": { "report:report-a#editor@user:user-b[tenant=tenant-b]": [ { @@ -481,6 +482,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": true, "decisionPath": { "report:report-a#editor@user:user-b[tenant=tenant-b]": [ { @@ -537,6 +539,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": true, "decisionPath": { "permission:edit-balance-sheet#member@user:user-a[tenant=tenant-a]": [ { @@ -591,7 +594,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -621,6 +625,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": false, "decisionPath": { "report:report-a#editor@user:user-a": [ { @@ -663,6 +668,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": true, "decisionPath": { "report:report-a#viewer@user:user-a": [ { @@ -705,6 +711,7 @@ "body": { "code": 200, "result": "Authorized", + "isImplicit": true, "decisionPath": { "report:report-a#editor-viewer@user:user-a": [ { @@ -745,7 +752,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -900,7 +908,7 @@ } }, { - "name": "deleteUserUserb", + "name": "deleteUserUserB", "request": { "method": "DELETE", "url": "/v1/users/user-b" @@ -910,7 +918,7 @@ } }, { - "name": "deleteUserUsera", + "name": "deleteUserUserA", "request": { "method": "DELETE", "url": "/v1/users/user-a" diff --git a/tests/pricing-tiers-and-features.json b/tests/pricing-tiers-and-features.json index 960b0d3a..66a67895 100644 --- a/tests/pricing-tiers-and-features.json +++ b/tests/pricing-tiers-and-features.json @@ -367,7 +367,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -395,7 +396,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -423,7 +425,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -451,7 +454,8 @@ "statusCode": 200, "body": { "code": 403, - "result": "Not Authorized" + "result": "Not Authorized", + "isImplicit": false } } }, @@ -479,7 +483,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -507,7 +512,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -535,7 +541,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } }, @@ -563,7 +570,8 @@ "statusCode": 200, "body": { "code": 200, - "result": "Authorized" + "result": "Authorized", + "isImplicit": true } } },