Skip to content

Commit c94646b

Browse files
committed
refactor: use checkDirectCall for recursive invokes
1 parent d7c7030 commit c94646b

3 files changed

Lines changed: 80 additions & 67 deletions

File tree

‎internal/engines/check.go‎

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,7 @@ func (engine *CheckEngine) check(
123123
var fn CheckFunction
124124

125125
// Determine the type of the reference by name in the given entity definition.
126-
tor, err := schema.GetTypeOfReferenceByNameInEntityDefinition(en, request.GetPermission())
127-
if err != nil {
128-
// If an error is encountered while determining the type, a CheckFunction is returned that always fails with this error.
129-
return checkFail(err)
130-
}
126+
tor, _ := schema.GetTypeOfReferenceByNameInEntityDefinition(en, request.GetPermission())
131127

132128
// Based on the type of the reference, define the CheckFunction in different ways.
133129
switch tor {
@@ -155,11 +151,7 @@ func (engine *CheckEngine) check(
155151
// If the reference is a relation, check the direct relation.
156152
fn = engine.checkDirectRelation(request)
157153
default:
158-
// If the reference is not a permission, attribute or relation, check the call.
159-
fn = engine.checkCall(request, &base.Call{
160-
RuleName: request.GetPermission(),
161-
Arguments: request.GetArguments(),
162-
})
154+
fn = engine.checkDirectCall(request)
163155
}
164156

165157
// If the CheckFunction is still undefined after the switch, return a CheckFunction that always fails with an error indicating an undefined child kind.
@@ -412,7 +404,7 @@ func (engine *CheckEngine) checkTupleToUserSet(
412404
// ComputedUserSet data structure. It returns a CheckFunction closure that performs the check.
413405
func (engine *CheckEngine) checkComputedUserSet(
414406
request *base.PermissionCheckRequest, // The request containing details about the permission to be checked
415-
cu *base.ComputedUserSet, // The computed user set containing user set information
407+
cu *base.ComputedUserSet, // The computed user set containing user set information
416408
) CheckFunction {
417409
// The returned CheckFunction invokes a permission check with a new request that is almost the same
418410
// as the incoming request, but changes the Permission to be the relation defined in the computed user set.
@@ -496,7 +488,7 @@ func (engine *CheckEngine) checkDirectAttribute(
496488
}
497489

498490
// Unmarshal the attribute value into a BoolValue message.
499-
var msg base.BoolValue
491+
var msg base.BooleanValue
500492
if err := val.GetValue().UnmarshalTo(&msg); err != nil {
501493
// If there was an error unmarshaling, return a denied response and the error.
502494
return denied(&base.PermissionCheckResponseMetadata{}), err
@@ -512,13 +504,30 @@ func (engine *CheckEngine) checkDirectAttribute(
512504
}
513505
}
514506

515-
// checkCall is a function that validates a call using the CheckEngine.
516-
// It returns a function (CheckFunction) that when called, performs the permission check.
507+
// checkCall creates and returns a CheckFunction based on the provided request and call details.
508+
// It essentially constructs a new PermissionCheckRequest based on the call details and then invokes
509+
// the permission check using the engine's invoke method.
517510
func (engine *CheckEngine) checkCall(
518-
request *base.PermissionCheckRequest, // The request containing the details for the permission check
519-
call *base.Call, // The specific call to be checked
511+
request *base.PermissionCheckRequest,
512+
call *base.Call,
513+
) CheckFunction {
514+
// Construct a new permission check request based on the input request and call details.
515+
return engine.invoke(&base.PermissionCheckRequest{
516+
TenantId: request.GetTenantId(),
517+
Entity: request.GetEntity(),
518+
Permission: call.GetRuleName(),
519+
Subject: request.GetSubject(),
520+
Metadata: request.GetMetadata(),
521+
Context: request.GetContext(),
522+
Arguments: call.GetArguments(),
523+
})
524+
}
525+
526+
// checkDirectCall creates and returns a CheckFunction that performs direct permission checking.
527+
// The function evaluates permissions based on rule definitions, arguments, and attributes.
528+
func (engine *CheckEngine) checkDirectCall(
529+
request *base.PermissionCheckRequest,
520530
) CheckFunction {
521-
// The function returned by checkCall
522531
return func(ctx context.Context) (*base.PermissionCheckResponse, error) {
523532
var err error
524533

@@ -529,52 +538,44 @@ func (engine *CheckEngine) checkCall(
529538

530539
// Read the rule definition from the schema. If an error occurs, return the default denied response.
531540
var ru *base.RuleDefinition
532-
ru, _, err = engine.schemaReader.ReadRuleDefinition(ctx, request.GetTenantId(), call.GetRuleName(), request.GetMetadata().GetSchemaVersion())
541+
ru, _, err = engine.schemaReader.ReadRuleDefinition(ctx, request.GetTenantId(), request.GetPermission(), request.GetMetadata().GetSchemaVersion())
533542
if err != nil {
534543
return emptyResp, err
535544
}
536545

537-
// Prepare the arguments map to be used in the CEL evaluation
546+
// Initialize an arguments map to hold argument values.
538547
arguments := make(map[string]interface{})
539548

540-
// Prepare a slice for attributes
549+
// List to store computed attributes.
541550
attributes := make([]string, 0)
542551

543-
// Populate the arguments map based on the type of argument in the call
544-
for _, arg := range call.GetArguments() {
552+
// Iterate over request arguments to classify and process them.
553+
for _, arg := range request.GetArguments() {
545554
switch actualArg := arg.Type.(type) {
546555
case *base.Argument_ComputedAttribute:
547-
// Get the name of the computed attribute
556+
// Handle computed attributes: Set them to a default empty value.
548557
attrName := actualArg.ComputedAttribute.GetName()
549-
550-
// Get the empty value for this attribute type
551558
emptyValue := getEmptyValueForType(ru.GetArguments()[attrName])
552-
553-
// Add the attribute with its empty value to the arguments map
554559
arguments[attrName] = emptyValue
555-
556-
// Append the attribute to the attributes slice
557560
attributes = append(attributes, attrName)
561+
558562
case *base.Argument_ContextAttribute:
559-
// Get the name of the context attribute
563+
// Handle context attributes: Use the value from context or default to an empty value.
560564
attrName := actualArg.ContextAttribute.GetName()
561-
562-
// Get the value of the context attribute if exists, else get an empty value
563565
value, exists := request.GetContext().GetData().AsMap()[attrName]
564566
if !exists {
565567
value = getEmptyValueForType(ru.GetArguments()[attrName])
566568
}
567-
568-
// Add the attribute with its value to the arguments map
569569
arguments[attrName] = value
570+
570571
default:
571-
// Return an error for unhandled types
572+
// Return an error for any unsupported argument types.
572573
return denied(&base.PermissionCheckResponseMetadata{}), fmt.Errorf(base.ErrorCode_ERROR_CODE_INTERNAL.String())
573574
}
574575
}
575576

577+
// If there are computed attributes, fetch them from the data source.
576578
if len(attributes) > 0 {
577-
// Prepare the filter for querying attributes from the database
578579
filter := &base.AttributeFilter{
579580
Entity: &base.EntityFilter{
580581
Type: request.GetEntity().GetType(),
@@ -583,7 +584,6 @@ func (engine *CheckEngine) checkCall(
583584
Attributes: attributes,
584585
}
585586

586-
// Query the database for the attributes and add them to the arguments map
587587
ait, err := engine.dataReader.QueryAttributes(ctx, request.GetTenantId(), filter, request.GetMetadata().GetSnapToken())
588588
if err != nil {
589589
return denied(&base.PermissionCheckResponseMetadata{}), err
@@ -594,38 +594,37 @@ func (engine *CheckEngine) checkCall(
594594
return denied(&base.PermissionCheckResponseMetadata{}), err
595595
}
596596

597+
// Combine attributes from different sources ensuring uniqueness.
597598
it := database.NewUniqueAttributeIterator(ait, cta)
598-
599599
for it.HasNext() {
600600
next, ok := it.GetNext()
601601
if !ok {
602602
break
603603
}
604-
605604
arguments[next.GetAttribute()] = utils.ConvertProtoAnyToInterface(next.GetValue())
606605
}
607606
}
608607

609-
// Prepare the CEL environment using the arguments in the rule definition
608+
// Prepare the CEL environment with the argument values.
610609
env, err := utils.ArgumentsAsCelEnv(ru.Arguments)
611610
if err != nil {
612611
return nil, err
613612
}
614613

615-
// Prepare the CEL program using the rule's expression
614+
// Compile the rule expression into an executable form.
616615
exp := cel.CheckedExprToAst(ru.Expression)
617616
prg, err := env.Program(exp)
618617
if err != nil {
619618
return nil, err
620619
}
621620

622-
// Evaluate the CEL program with the arguments. If an error occurs, return a "denied" response.
621+
// Evaluate the rule expression with the provided arguments.
623622
out, _, err := prg.Eval(arguments)
624623
if err != nil {
625624
return denied(&base.PermissionCheckResponseMetadata{}), fmt.Errorf("failed to evaluate expression: %w", err)
626625
}
627626

628-
// Check if the result of the CEL evaluation is a boolean
627+
// Ensure the result of evaluation is boolean and decide on permission.
629628
result, ok := out.Value().(bool)
630629
if !ok {
631630
return denied(&base.PermissionCheckResponseMetadata{}), fmt.Errorf("expected boolean result, but got %T", out.Value())

‎internal/engines/expand.go‎

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,7 @@ func (engine *ExpandEngine) expand(ctx context.Context, request *base.Permission
8181

8282
var tor base.EntityDefinition_Reference
8383
// Get the type of reference by name in the entity definition.
84-
tor, err = schema.GetTypeOfReferenceByNameInEntityDefinition(en, request.GetPermission())
85-
if err != nil {
86-
// If an error occurred while getting the type of reference, return an ExpandResponse with the error.
87-
return ExpandResponse{Err: err}
88-
}
84+
tor, _ = schema.GetTypeOfReferenceByNameInEntityDefinition(en, request.GetPermission())
8985

9086
// Depending on the type of reference, execute different branches of code.
9187
switch tor {
@@ -114,10 +110,7 @@ func (engine *ExpandEngine) expand(ctx context.Context, request *base.Permission
114110
fn = engine.expandDirectRelation(request)
115111
default:
116112
// If the reference is neither permission, attribute, nor relation, use the 'expandCall' method.
117-
fn = engine.expandCall(request, &base.Call{
118-
RuleName: request.GetPermission(),
119-
Arguments: request.GetArguments(),
120-
})
113+
fn = engine.expandDirectCall(request)
121114
}
122115

123116
if fn == nil {
@@ -519,7 +512,7 @@ func (engine *ExpandEngine) expandDirectAttribute(
519512
},
520513
Attribute: request.GetPermission(),
521514
}
522-
val.Value, err = anypb.New(&base.BoolValue{Data: false})
515+
val.Value, err = anypb.New(&base.BooleanValue{Data: false})
523516
if err != nil {
524517
expandChan <- expandFailResponse(err)
525518
return
@@ -546,11 +539,32 @@ func (engine *ExpandEngine) expandDirectAttribute(
546539
}
547540
}
548541

542+
// expandCall returns an ExpandFunction for the given request and call.
543+
// The returned function, when executed, sends the expanded permission result
544+
// to the provided result channel.
545+
func (engine *ExpandEngine) expandCall(
546+
request *base.PermissionExpandRequest,
547+
call *base.Call,
548+
) ExpandFunction {
549+
return func(ctx context.Context, resultChan chan<- ExpandResponse) {
550+
resultChan <- engine.expand(ctx, &base.PermissionExpandRequest{
551+
TenantId: request.GetTenantId(),
552+
Entity: &base.Entity{
553+
Type: request.GetEntity().GetType(),
554+
Id: request.GetEntity().GetId(),
555+
},
556+
Permission: call.GetRuleName(),
557+
Metadata: request.GetMetadata(),
558+
Context: request.GetContext(),
559+
Arguments: call.GetArguments(),
560+
})
561+
}
562+
}
563+
549564
// The function 'expandCall' is a method on the ExpandEngine struct.
550565
// It takes a PermissionExpandRequest and a Call as parameters and returns an ExpandFunction.
551-
func (engine *ExpandEngine) expandCall(
566+
func (engine *ExpandEngine) expandDirectCall(
552567
request *base.PermissionExpandRequest, // The request object containing information necessary for the expansion.
553-
call *base.Call, // The call object that defines the rule name and its arguments.
554568
) ExpandFunction { // The function returns an ExpandFunction.
555569
return func(ctx context.Context, expandChan chan<- ExpandResponse) { // defining the returned function.
556570

@@ -559,7 +573,7 @@ func (engine *ExpandEngine) expandCall(
559573
var ru *base.RuleDefinition // variable to hold the rule definition.
560574

561575
// Read the rule definition based on the rule name in the call.
562-
ru, _, err = engine.schemaReader.ReadRuleDefinition(ctx, request.GetTenantId(), call.GetRuleName(), request.GetMetadata().GetSchemaVersion())
576+
ru, _, err = engine.schemaReader.ReadRuleDefinition(ctx, request.GetTenantId(), request.GetPermission(), request.GetMetadata().GetSchemaVersion())
563577
if err != nil {
564578
// If there's an error in reading the rule definition, send a failure response through the channel and return from the function.
565579
expandChan <- expandFailResponse(err)
@@ -573,7 +587,7 @@ func (engine *ExpandEngine) expandCall(
573587
attributes := make([]string, 0)
574588

575589
// For each argument in the call...
576-
for _, arg := range call.GetArguments() {
590+
for _, arg := range request.GetArguments() {
577591
switch actualArg := arg.Type.(type) { // Switch on the type of the argument.
578592
case *base.Argument_ComputedAttribute: // If the argument is a ComputedAttribute...
579593
attrName := actualArg.ComputedAttribute.GetName() // get the name of the attribute.
@@ -667,8 +681,8 @@ func (engine *ExpandEngine) expandCall(
667681
Response: &base.PermissionExpandResponse{
668682
Tree: &base.Expand{
669683
Entity: request.GetEntity(),
670-
Permission: call.GetRuleName(),
671-
Arguments: call.GetArguments(),
684+
Permission: request.GetPermission(),
685+
Arguments: request.GetArguments(),
672686
Node: &base.Expand_Leaf{
673687
Leaf: &base.ExpandLeaf{
674688
Type: &base.ExpandLeaf_Values{
@@ -688,7 +702,7 @@ func (engine *ExpandEngine) expandCall(
688702
// It takes a PermissionExpandRequest and a ComputedAttribute as parameters and returns an ExpandFunction.
689703
func (engine *ExpandEngine) expandComputedAttribute(
690704
request *base.PermissionExpandRequest, // The request object containing necessary information for the expansion.
691-
ca *base.ComputedAttribute, // The computed attribute object that has the name of the attribute to be computed.
705+
ca *base.ComputedAttribute, // The computed attribute object that has the name of the attribute to be computed.
692706
) ExpandFunction { // The function returns an ExpandFunction.
693707
return func(ctx context.Context, resultChan chan<- ExpandResponse) { // defining the returned function.
694708

@@ -712,11 +726,11 @@ func (engine *ExpandEngine) expandComputedAttribute(
712726
// a slice of arguments, slice of ExpandFunctions, and an operation of type base.ExpandTreeNode_Operation.
713727
// It returns an ExpandResponse.
714728
func expandOperation(
715-
ctx context.Context, // The context of this operation, which may carry deadlines, cancellation signals, etc.
716-
entity *base.Entity, // The entity on which the operation will be performed.
717-
permission string, // The permission string required for the operation.
718-
arguments []*base.Argument, // A slice of arguments required for the operation.
719-
functions []ExpandFunction, // A slice of functions that will be used to expand the operation.
729+
ctx context.Context, // The context of this operation, which may carry deadlines, cancellation signals, etc.
730+
entity *base.Entity, // The entity on which the operation will be performed.
731+
permission string, // The permission string required for the operation.
732+
arguments []*base.Argument, // A slice of arguments required for the operation.
733+
functions []ExpandFunction, // A slice of functions that will be used to expand the operation.
720734
op base.ExpandTreeNode_Operation, // The operation to be performed.
721735
) ExpandResponse { // The function returns an ExpandResponse.
722736

‎internal/engines/expand_test.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,8 +1320,8 @@ var _ = Describe("expand-engine", func() {
13201320
assertions map[string]*base.Expand
13211321
}
13221322

1323-
anyVal, _ := anypb.New(&base.Boolean{Value: true})
1324-
dow, _ := anypb.New(&base.String{Value: "monday"})
1323+
anyVal, _ := anypb.New(&base.BooleanValue{Data: true})
1324+
dow, _ := anypb.New(&base.StringValue{Data: "monday"})
13251325

13261326
tests := struct {
13271327
relationships []string

0 commit comments

Comments
 (0)