From f37591f956666a43a494ca1d4bf8347dbdb60e51 Mon Sep 17 00:00:00 2001 From: Jonathan Whitaker Date: Tue, 12 Dec 2023 17:33:32 -0700 Subject: [PATCH 1/4] feat: initial OpenFGA `v2beta1` protobuf API development --- openfga/v2beta1/openfga.proto | 163 ++++++++++++++++++++++++++ openfga/v2beta1/openfga_service.proto | 36 ++++++ openfga/v2beta1/watch_service.proto | 12 ++ 3 files changed, 211 insertions(+) create mode 100644 openfga/v2beta1/openfga.proto create mode 100644 openfga/v2beta1/openfga_service.proto create mode 100644 openfga/v2beta1/watch_service.proto diff --git a/openfga/v2beta1/openfga.proto b/openfga/v2beta1/openfga.proto new file mode 100644 index 00000000..7ef296a8 --- /dev/null +++ b/openfga/v2beta1/openfga.proto @@ -0,0 +1,163 @@ +syntax = "proto3"; + +package openfga.v2beta1; + +import "google/protobuf/struct.proto"; + +message RelationshipTuple { + + Subject subject = 1; + + string relation = 2; + + Object object = 3; + + RelationshipCondition condition = 4; +} + +message RelationshipCondition { + string name = 1; + google.protobuf.Struct context = 2; +} + +message RelationshipTupleWithoutCondition { + Subject subject = 1; + string relation = 2; + Object object = 3; +} + +message Subject { + oneof subject_ref{ + Object object = 1; + TypedWildcard typed_wildcard = 2; + SubjectSet subject_set = 3; + } +} + +message Object { + string type = 1; + string id = 2; +} + +// TypedWildcard represents the public wildcard of the specified object type. +// For example, `user:*` represents all objects of the `user` object type. +message TypedWildcard { + string type = 1; +} + +message SubjectSet { + Object object = 1; + string relation = 2; +} + +message AuthorizationModel { + string id = 1; + map relations = 2; + map conditions = 3; +} + +message Relation { + + // The name of the relation. + string name = 1; + + // The relationship rewrite rule for the relation. + RelationRewrite rewrite = 2; + + // A list of type restrictions that apply to the relation. + repeated TypeRestriction type_restrictions = 3; +} + +// RelationRewrite represents a relationship rule that is used to rewrite or define a relation +// in terms of a direct relationship or some other rewritten relationship definition including +// through set operations involving union, intersection, and exclusion. +message RelationRewrite { + oneof rewrite_rule { + DirectRelationship direct = 1; + ComputedRelationship computed = 2; + TupleToSubjectSetRelationship tuple_to_subjectset = 3; + // union, intersection, exclusion + } +} + +// DirectRelationship represents a directly assignable relationship. +// +// These relationships are the kinds of relationships that are permissible for writes to an +// OpenFGA store. The kinds of objects that can be assignable to the directly assignable +// relationship are based on the relations type restrictions. +message DirectRelationship {} + +// ComputedRelationship represents a relationship that is rewritten through a recomputed relation. +// +// For example, if a relation `viewer` is rewritten through a computed relationship `editor`. In +// Zanzibar nomenclature this is identical to computed userset rewrites. +message ComputedRelationship { + string relation = 1; +} + +// TupleToSubjectSetRelationship represents a relationship +// +// In Zanzibar nomenclature this is identical to tuple to userset rewrites. +message TupleToSubjectSetRelationship { + string tupleset_relation = 1; + ComputedRelationship computed_relationship = 2; +} + +// TypeRestriction represents a relationship constraint that applies to a directly +// assignable relationship. A directl relationship is only permissible if the type +// restriction allows it. +message TypeRestriction { + oneof type_reference { + UnconditionedObjectTypeRestriction unconditioned_object_type_reference = 1; + ConditionedObjectTypeRestriction conditioned_object_type_reference = 2; + TypedWildcard typed_wildcard_reference = 3; + SubjectSetTypeRestriction subject_set_reference = 4; + } +} + +// UnconditionedObjectTypeRestriction represents a type restriction that enforces a relationship with +// only a specific object type and is not conditioned on anything. +message UnconditionedObjectTypeRestriction { + string type = 1; +} + +// ConditionedObjectTypeRestriction represents a type restriction that enforces a relationship with +// a specific object type and is conditioned on the provided condition name. +message ConditionedObjectTypeRestriction { + string type = 1; + string condition = 2; +} + +// SubjectSetTypeRestriction represents a type restriction that references a relation defined on a specific +// object type. For example `group#member` or `team#admin`. +message SubjectSetTypeRestriction { + string type = 1; + string relation = 2; +} + +message Condition { + string name = 1; + string expression = 2; + map parameters = 3; +} + +message ConditionParameterTypeRef { + enum TypeName { + TYPE_NAME_UNSPECIFIED = 0; + TYPE_NAME_ANY = 1; + TYPE_NAME_BOOL = 2; + TYPE_NAME_STRING = 3; + TYPE_NAME_INT = 4; + TYPE_NAME_UINT = 5; + TYPE_NAME_DOUBLE = 6; + TYPE_NAME_DURATION = 7; + TYPE_NAME_TIMESTAMP = 8; + TYPE_NAME_MAP = 9; + TYPE_NAME_LIST = 10; + TYPE_NAME_IPADDRESS = 11; + } + + TypeName type_name = 1; + + repeated ConditionParameterTypeRef generic_types = 2; +} \ No newline at end of file diff --git a/openfga/v2beta1/openfga_service.proto b/openfga/v2beta1/openfga_service.proto new file mode 100644 index 00000000..bc15359f --- /dev/null +++ b/openfga/v2beta1/openfga_service.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package openfga.v2beta1; + +service OpenFGAService { + rpc Check(CheckRequest) returns (CheckResponse); + rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); + + rpc DeleteRelationships(DeleteRelationshipsRequest) returns (DeleteRelationshipResponse); + rpc WriteRelationships(WriteRelationshipsRequest) returns (WriteRelationshipsResponse); + + rpc WriteModel(WriteModelRequest) returns (WriteModelResponse); + rpc ReadModel(ReadModelRequest) returns (ReadModelResponse); + rpc ListModels(ListModelsRequest) returns (ListModelsResponse); +} + +message CheckRequest{} +message CheckResponse{} + +message ListObjectsRequest{} +message ListObjectsResponse{} + +message DeleteRelationshipsRequest{} +message DeleteRelationshipResponse{} + +message WriteRelationshipsRequest{} +message WriteRelationshipsResponse{} + +message WriteModelRequest{} +message WriteModelResponse{} + +message ReadModelRequest{} +message ReadModelResponse{} + +message ListModelsRequest{} +message ListModelsResponse{} \ No newline at end of file diff --git a/openfga/v2beta1/watch_service.proto b/openfga/v2beta1/watch_service.proto new file mode 100644 index 00000000..bf0882da --- /dev/null +++ b/openfga/v2beta1/watch_service.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package watch.v2beta1; + +service WatchService { + + // WatchChanges implements a streaming watch over an FGA store's changelog + rpc WatchChanges(WatchChangesRequest) returns (stream WatchChangesResponse); +} + +message WatchChangesRequest {} +message WatchChangesResponse {} \ No newline at end of file From 13a3d1117abde7ed002084af7d2868f0101b6ce3 Mon Sep 17 00:00:00 2001 From: Jonathan Whitaker Date: Wed, 13 Dec 2023 11:18:23 -0700 Subject: [PATCH 2/4] chore: add `store_id` fields and fields for Check and ListObjects --- openfga/v2beta1/openfga_service.proto | 44 ++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/openfga/v2beta1/openfga_service.proto b/openfga/v2beta1/openfga_service.proto index bc15359f..49b2a5b2 100644 --- a/openfga/v2beta1/openfga_service.proto +++ b/openfga/v2beta1/openfga_service.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package openfga.v2beta1; +import "google/protobuf/struct.proto"; + service OpenFGAService { rpc Check(CheckRequest) returns (CheckResponse); rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); @@ -14,23 +16,51 @@ service OpenFGAService { rpc ListModels(ListModelsRequest) returns (ListModelsResponse); } -message CheckRequest{} +message CheckRequest{ + string store_id = 1; + string model_id = 2; + Subject subject = 3; + string relation = 4; + Object object = 5; + repeated RelationshipTuple contextual_tuples = 6; + google.protobuf.Struct context = 7; +} + message CheckResponse{} -message ListObjectsRequest{} +message ListObjectsRequest{ + string store_id = 1; + string model_id = 2; + Subject subject = 3; + string relation = 4; + string object_type = 5; + repeated RelationshipTuple contextual_tuples = 6; + google.protobuf.Struct context = 7; +} message ListObjectsResponse{} -message DeleteRelationshipsRequest{} +message DeleteRelationshipsRequest{ + string store_id = 1; +} message DeleteRelationshipResponse{} -message WriteRelationshipsRequest{} +message WriteRelationshipsRequest{ + string store_id = 1; +} message WriteRelationshipsResponse{} -message WriteModelRequest{} +message WriteModelRequest{ + string store_id = 1; +} message WriteModelResponse{} -message ReadModelRequest{} +message ReadModelRequest{ + string store_id = 1; + string model_id = 2; +} message ReadModelResponse{} -message ListModelsRequest{} +message ListModelsRequest{ + string store_id = 1; +} message ListModelsResponse{} \ No newline at end of file From 9938dd02c7801b683f8e26fcb74add155839afcd Mon Sep 17 00:00:00 2001 From: Jonathan Whitaker Date: Wed, 13 Dec 2023 11:21:27 -0700 Subject: [PATCH 3/4] chore: fix import --- openfga/v2beta1/openfga_service.proto | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openfga/v2beta1/openfga_service.proto b/openfga/v2beta1/openfga_service.proto index 49b2a5b2..1c90577b 100644 --- a/openfga/v2beta1/openfga_service.proto +++ b/openfga/v2beta1/openfga_service.proto @@ -3,12 +3,13 @@ syntax = "proto3"; package openfga.v2beta1; import "google/protobuf/struct.proto"; +import "openfga/v2beta1/openfga.proto"; service OpenFGAService { rpc Check(CheckRequest) returns (CheckResponse); rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); - rpc DeleteRelationships(DeleteRelationshipsRequest) returns (DeleteRelationshipResponse); + rpc DeleteRelationships(DeleteRelationshipsRequest) returns (DeleteRelationshipsResponse); rpc WriteRelationships(WriteRelationshipsRequest) returns (WriteRelationshipsResponse); rpc WriteModel(WriteModelRequest) returns (WriteModelResponse); @@ -31,10 +32,10 @@ message CheckResponse{} message ListObjectsRequest{ string store_id = 1; string model_id = 2; - Subject subject = 3; + openfga.v2beta1.Subject subject = 3; string relation = 4; string object_type = 5; - repeated RelationshipTuple contextual_tuples = 6; + repeated openfga.v2beta1.RelationshipTuple contextual_tuples = 6; google.protobuf.Struct context = 7; } message ListObjectsResponse{} @@ -42,7 +43,7 @@ message ListObjectsResponse{} message DeleteRelationshipsRequest{ string store_id = 1; } -message DeleteRelationshipResponse{} +message DeleteRelationshipsResponse{} message WriteRelationshipsRequest{ string store_id = 1; From ba5b25663224b22609066ff95434dda59af3f605 Mon Sep 17 00:00:00 2001 From: Jonathan Whitaker Date: Fri, 15 Dec 2023 13:45:10 -0700 Subject: [PATCH 4/4] chore: cleanup and add some new RPC definitions --- openfga/v2beta1/openfga_service.proto | 67 +++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/openfga/v2beta1/openfga_service.proto b/openfga/v2beta1/openfga_service.proto index 1c90577b..338d429b 100644 --- a/openfga/v2beta1/openfga_service.proto +++ b/openfga/v2beta1/openfga_service.proto @@ -3,14 +3,18 @@ syntax = "proto3"; package openfga.v2beta1; import "google/protobuf/struct.proto"; +import "google/rpc/status.proto"; import "openfga/v2beta1/openfga.proto"; service OpenFGAService { rpc Check(CheckRequest) returns (CheckResponse); + rpc BatchCheck(BatchCheckRequest) returns (BatchCheckResponse); + rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); - rpc DeleteRelationships(DeleteRelationshipsRequest) returns (DeleteRelationshipsResponse); - rpc WriteRelationships(WriteRelationshipsRequest) returns (WriteRelationshipsResponse); + rpc DeleteRelationshipTuples(DeleteRelationshipTuplesRequest) returns (DeleteRelationshipTuplesResponse); + rpc WriteRelationshipTuples(WriteRelationshipTuplesRequest) returns (WriteRelationshipTuplesResponse); + rpc ReadRelationshipTuples(ReadRelationshipTuplesRequest) returns (ReadRelationshipTuplesResponse); rpc WriteModel(WriteModelRequest) returns (WriteModelResponse); rpc ReadModel(ReadModelRequest) returns (ReadModelResponse); @@ -29,26 +33,73 @@ message CheckRequest{ message CheckResponse{} +message BatchCheckRequest { + + // One or more individual Check requests to evaluate in the batch. + repeated BatchCheckRequestItem requests = 1; +} + +message BatchCheckResponse { + + // The response pairings for each request item and the result/response it produced. + repeated BatchCheckResponsePair response_pairs = 1; +} + +// BatchCheckRequestItem represents a single Check request item used in a BatchCheckRequest. +message BatchCheckRequestItem { + Subject subject = 1; + string relation = 2; + Object object = 3; + repeated RelationshipTuple contextual_tuples = 4; + google.protobuf.Struct context = 5; +} + +// BatchCheckResponseItem represents an individual response for a singular BatchCheckRequestItem produced +// by a BatchCheck RPC. +message BatchCheckResponseItem { + CheckResponse response = 1; +} + +// BatchCheckResponsePair represents the pairing of an individual BatchCheckRequestItem in a +// BatchCheckRequest and the result which the individual request produced. Each BatchCheckRequestItem +// can produce either a response item or an error. +message BatchCheckResponsePair { + BatchCheckRequestItem request = 1; + + oneof result { + BatchCheckResponseItem response_item = 2; + google.rpc.Status error = 3; + } +} + message ListObjectsRequest{ string store_id = 1; string model_id = 2; - openfga.v2beta1.Subject subject = 3; + Subject subject = 3; string relation = 4; string object_type = 5; - repeated openfga.v2beta1.RelationshipTuple contextual_tuples = 6; + RelationshipTuple contextual_tuples = 6; google.protobuf.Struct context = 7; } message ListObjectsResponse{} -message DeleteRelationshipsRequest{ +message DeleteRelationshipTuplesRequest{ string store_id = 1; } -message DeleteRelationshipsResponse{} +message DeleteRelationshipTuplesResponse{} -message WriteRelationshipsRequest{ +message WriteRelationshipTuplesRequest{ string store_id = 1; } -message WriteRelationshipsResponse{} +message WriteRelationshipTuplesResponse{} + +message ReadRelationshipTuplesRequest { + string store_id = 1; +} + +message ReadRelationshipTuplesResponse { + repeated RelationshipTuple relationship_tuples = 1; +} message WriteModelRequest{ string store_id = 1;