Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: batch check relations #1521

Merged
merged 16 commits into from
Jul 18, 2024
Merged

feat: batch check relations #1521

merged 16 commits into from
Jul 18, 2024

Conversation

patrickduffy95
Copy link
Contributor

@patrickduffy95 patrickduffy95 commented Apr 15, 2024

Related issue(s)

#812

Checklist

  • I have read the contributing guidelines.
  • I have referenced an issue containing the design document if my change
    introduces a new feature.
  • I am following the
    contributing code guidelines.
  • I have read the security policy.
  • I confirm that this pull request does not address a security
    vulnerability. If this pull request addresses a security vulnerability, I
    confirm that I got the approval (please contact
    [email protected]) from the maintainers to push
    the changes.
  • I have added tests that prove my fix is effective or that my feature
    works.
  • I have added or changed the documentation.

Further Comments

This change adds REST and gRPC endpoints for batch checking relations. The endpoint accepts a list of relation tuples to check, iterates through them (with concurrency), and returns a list of allowed responses.

REST API
New endpoint:
POST /relation-tuples/batch/check?max-depth=<depth>&parallelization-factor=<max-concurrent-requests>
Request body:

{
    "tuples": [
        {
            "namespace": <namespace>,
            "object": <object>,
            "relation": <relation>,
            "subject_id": <subject_id>,
            "subject_set": <subject_set>
        }
    ]
}

Response:

{
    "results": [
        {
            "allowed": true,
            "error": "an optional error message if the individual check fails"
        }
    ]
}

gRPC
New RPC:
CheckService/BatchCheck
Request:

// The request for a CheckService.BatchCheck RPC.
// Checks a batch of relations.
message BatchCheckRequest {
  repeated RelationTuple tuples = 1;

  // This field is not implemented yet and has no effect.
  bool latest = 2;
  // This field is not implemented yet and has no effect.
  string snaptoken = 3;
  // The maximum depth to search for a relation.
  //
  // If the value is less than 1 or greater than the global
  // max-depth then the global max-depth will be used instead.
  int32 max_depth = 4;
  // The number of check requests to perform in parallel.
  //
  // Will default to 5 if not provided. If provided, it must be
  // a positive integer
  optional int32 parallelization_factor = 5;
}

Response

// The response for a CheckService.BatchCheck rpc.
message BatchCheckResponse {
  // The results of the batch check. The order of these
  // results will match the order of the input.
  repeated CheckResponseWithError results = 1;
}

// The response for an individual check in the CheckService.BatchCheck rpc.
message CheckResponseWithError {
  // Whether the specified subject (id)
  // is related to the requested object.
  //
  // It is false by default if no ACL matches.
  bool allowed = 1;
  // If there was an error checking the tuple,
  // this will contain the error message.
  //
  // If the check was performed successfully, this will be empty.
  string error = 2;
  // This field is not implemented yet and has no effect.
  string snaptoken = 3;
}

Notes:

  • The maximum batch size is configurable based on a new limit.max_batch_check_size size with a default value of 10
  • The checks are executed individually. If any single check errors, the API will continue with the other checks. For the failed check, it returns not allowed along with an error message
  • The checks are executed in parallel. The max number of checks that can be executed in parallel is controlled by a "parallelization factor" that can optionally be included with each request. It defaults to 5.
  • I did not implement this for the CLI

@CLAassistant
Copy link

CLAassistant commented Apr 15, 2024

CLA assistant check
All committers have signed the CLA.

@patrickduffy95 patrickduffy95 changed the title Batch check relations feat: Batch check relations Apr 15, 2024
@patrickduffy95 patrickduffy95 changed the title feat: Batch check relations feat: batch check relations Apr 15, 2024
@patrickduffy95
Copy link
Contributor Author

@alnr would you be able to let me know if this PR is on the right track?

@aeneasr
Copy link
Member

aeneasr commented May 13, 2024

Hello, sorry for kot responding here. I think this feature is grand! @zepatrik and @hperl are finishing up some work on our end and can probably check out this PR in a week or two.

@PrimeDominus
Copy link

Thanks for implementing this. We've hit a roadblock and without this feature we have to write a bunch of custom code in our server which is getting cumbersome to maintain. Really looking forward to trying this.

@patrickduffy95
Copy link
Contributor Author

Hello, sorry for kot responding here. I think this feature is grand! @zepatrik and @hperl are finishing up some work on our end and can probably check out this PR in a week or two.

Thanks @aeneasr! Do you have a better idea now of when they might be able to take a look at it?

Copy link
Collaborator

@hperl hperl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for your contribution! This looks very good already.

Besides the comments below, the main thing that is missing are some tests. I'd like to see tests for the happy path, going over the batch limit (bad request), graceful handling of on errornous tuple in the batch (should not fail the whole batch, but just that check), empty batch, and whatever edge case you can think of.

I left some comments below. As for your questions, here's my take on it:

Should we limit the number of tuples this batch API accepts?

Yes, please make it configurable with a conservative default (10?)

Should we parallelize the check requests?

Yes, I'd make the number of concurrent checks another parameter.

Is implementing only the POST okay for the REST API?

Yes, that will suffice.

@@ -49,13 +49,15 @@ func NewHandler(d handlerDependencies) *Handler {
const (
RouteBase = "/relation-tuples/check"
OpenAPIRouteBase = RouteBase + "/openapi"
BatchRoute = "/relation-tuples/batchCheck"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about /relation-tuples/batch/check? Then we can have other batch ops in the future.

}
allowed, err := h.d.PermissionEngine().CheckIsMember(ctx, t[0], maxDepth)
if err != nil {
return nil, err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we annotate here which tuple caused the error? It might be frustrating to submit a batch of checks and get an error and then having to binary-search which tuple caused the error.

Alternatively we could just add the error to the results list and then commence with the next tuple. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding more context to the error is a good idea. The question then is whether to return errors at the tuple level vs for the API as a whole. If there are situations where clients might expect an error and would want to treat an error the same as receiving a false allowed value, I could see a benefit in continuing on error and returning for each tuple if an error was generated. As far as I know though, that isn't a normal case, so I will just add tuple details to the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed my mind and went for proceeding with checking the whole batch and adding the error to the response for each tuple


func (h *Handler) BatchCheck(ctx context.Context, req *rts.BatchCheckRequest) (*rts.BatchCheckResponse, error) {
responses := make([]*rts.CheckResponse, len(req.Tuples))
for i, reqTuple := range req.Tuples {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic is duplicated from the REST handler here. Ideally we'd implement this once and have gRPC and REST call that function.

@patrickduffy95
Copy link
Contributor Author

Thanks a lot for the review @hperl. I will let you know once this PR is updated.

@patrickduffy95 patrickduffy95 marked this pull request as ready for review June 13, 2024 18:48
@patrickduffy95
Copy link
Contributor Author

@hperl I have update the PR based on your feedback and it is now ready for review.

@PrimeDominus
Copy link

PrimeDominus commented Jul 8, 2024

I'm really looking forward to this functionality becoming available on Ory Network. @hperl is there anything else that needs to be done before this can be merged?

Copy link
Collaborator

@hperl hperl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for your changes! This looks very good already. I just added some minor comments to imrpove this further.

internal/check/handler.go Outdated Show resolved Hide resolved
internal/check/engine.go Outdated Show resolved Hide resolved
internal/check/handler.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@hperl hperl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks perfect now! Thanks for all the good work! 🎉

@hperl hperl added this pull request to the merge queue Jul 18, 2024
Merged via the queue into ory:master with commit d670d50 Jul 18, 2024
22 checks passed
@rbnbr
Copy link

rbnbr commented Nov 3, 2024

Hey, thank you for the great feature!

Now with this being merged in the master branch, could we also extend the Rest HTTP API (documentation) accordingly (https://www.ory.sh/docs/keto/reference/rest-api) and also make it available to the keto-client(s), especially for go (https://github.com/ory/keto-client-go)?

Thank you!

@alnr alnr mentioned this pull request Nov 18, 2024
6 tasks
@ryukinix
Copy link
Contributor

Very interesting! We were waiting for this since 2022. Awesome work!

@jcottongit
Copy link

@hperl Cheers for getting this in, can we get a docker image released with this feature included?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants