Skip to content

Commit d565bad

Browse files
committed
introduces the gRPC API for Authzed Materialize
This is published as v0 and is considered alpha version
1 parent 9c4c264 commit d565bad

2 files changed

Lines changed: 252 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
syntax = "proto3";
2+
package authzed.api.materialize.v0;
3+
4+
import "authzed/api/v1/core.proto";
5+
6+
option go_package = "github.com/authzed/authzed-go/proto/authzed/api/materialize/v0";
7+
option java_package = "com.authzed.api.materialize.v0";
8+
option java_multiple_files = true;
9+
10+
service WatchPermissionsService {
11+
// WatchPermissions returns a stream of PermissionChange events for the given permissions.
12+
//
13+
// WatchPermissions is a long-running RPC, and will stream events until the client
14+
// closes the connection or the server terminates the stream. The consumer is responsible of
15+
// keeping track of the last seen revision and resuming the stream from that point in the event
16+
// of disconnection or client-side restarts.
17+
//
18+
// The API does not offer a sharding mechanism and thus there should only be one consumer per target system.
19+
// Implementing an active-active HA consumer setup over the same target system will require coordinating which
20+
// revisions have been consumed in order to prevent transitioning to an inconsistent state.
21+
//
22+
// Usage of WatchPermissions requires to be explicitly enabled on the service, including the permissions to be
23+
// watched. It requires more resources and is less performant than WatchPermissionsSets. It's usage
24+
// is only recommended when performing the set intersections of WatchPermissionSets in the client side is not viable
25+
// or there is a strict application requirement to use consume the computed permissions.
26+
rpc WatchPermissions(WatchPermissionsRequest) returns (stream WatchPermissionsResponse) {}
27+
}
28+
29+
message WatchPermissionsRequest {
30+
// permissions is a list of permissions to watch for changes. At least one permission must be specified, and it must
31+
// be a subset or equal to the permissions that were enabled for the service.
32+
repeated WatchedPermission permissions = 1;
33+
// optional_starting_after is the revision token to start watching from. If not provided, the stream
34+
// will start from the current revision at the moment of the request.
35+
authzed.api.v1.ZedToken optional_starting_after = 2;
36+
}
37+
38+
message WatchedPermission {
39+
// resource_type is the type of the resource to watch for changes.
40+
string resource_type = 1;
41+
// permission is the permission to watch for changes.
42+
string permission = 2;
43+
// subject_type is the type of the subject to watch for changes.
44+
string subject_type = 3;
45+
// optional_subject_relation is the relation on the subject to watch for changes.
46+
string optional_subject_relation = 4;
47+
}
48+
49+
message WatchPermissionsResponse {
50+
oneof response {
51+
// change is the computed permission delta that has occurred as result of a mutation in origin SpiceDB.
52+
// The consumer should apply this change to the current state of the computed permissions in their target system.
53+
// Once an event arrives with completed_revision instead, the consumer shall consider there are not more changes
54+
// originating from that revision.
55+
//
56+
// The consumer should keep track of the revision in order to resume streaming in the event of consumer restarts.
57+
PermissionChange change = 1;
58+
59+
// completed_revision is the revision token that indicates all changes originating from a revision have been
60+
// streamed and thus the revision should be considered completed. It may also be
61+
// received without accompanying set of changes, indicating that a mutation in the origin SpiceDB cluster did
62+
// not yield any effective changes in the computed permissions
63+
authzed.api.v1.ZedToken completed_revision = 2;
64+
}
65+
}
66+
67+
message PermissionChange {
68+
enum Permissionship {
69+
PERMISSIONSHIP_UNSPECIFIED = 0;
70+
PERMISSIONSHIP_NO_PERMISSION = 1;
71+
PERMISSIONSHIP_HAS_PERMISSION = 2;
72+
PERMISSIONSHIP_CONDITIONAL_PERMISSION = 3;
73+
}
74+
75+
// revision represents the revision at which the change occurred.
76+
authzed.api.v1.ZedToken revision = 1;
77+
78+
// resource is the resource that the permission change is related to.
79+
authzed.api.v1.ObjectReference resource = 2;
80+
// permission is the permission that has changed.
81+
string permission = 3;
82+
// subject is the subject that the permission change is related to.
83+
authzed.api.v1.SubjectReference subject = 4;
84+
// permissionship is the new permissionship of the subject over the resource after the change.
85+
Permissionship permissionship = 5;
86+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
syntax = "proto3";
2+
package authzed.api.materialize.v0;
3+
4+
import "authzed/api/v1/core.proto";
5+
6+
option go_package = "github.com/authzed/authzed-go/proto/authzed/api/materialize/v0";
7+
option java_package = "com.authzed.api.materialize.v0";
8+
option java_multiple_files = true;
9+
10+
service WatchPermissionSetsService {
11+
// WatchPermissionSets returns a stream of changes to the sets which can be used to compute the watched permissions.
12+
//
13+
// WatchPermissionSets lets consumers achieve the same thing as WatchPermissions, but trades off a simpler usage model with
14+
// significantly lower computational requirements. Unlike WatchPermissions, this method returns changes to the sets of permissions,
15+
// rather than the individual permissions. Permission sets are a normalized form of the computed permissions, which
16+
// means that the consumer must perform an extra computation over this representation to obtain the final computed
17+
// permissions, typically by intersecting the provided sets.
18+
//
19+
// For example, this would look like a JOIN between the
20+
// materialize permission sets table in a target relation database, the table with the resources to authorize access
21+
// to, and the table with the subject (e.g. a user).
22+
//
23+
// In exchange, the number of changes issued by WatchPermissionSets will be several orders of magnitude less than those
24+
// emitted by WatchPermissions, which has several implications:
25+
// - significantly less resources to compute the sets
26+
// - significantly less messages to stream over the network
27+
// - significantly less events to ingest on the consumer side
28+
// - less ingestion lag from the origin SpiceDB mutation
29+
//
30+
// The type of scenarios WatchPermissionSets is particularly well suited is when a single change
31+
// in the origin SpiceDB can yield millions of changes. For example, in the GitHub authorization model, assigning a role
32+
// to a top-level team of an organization with hundreds of thousands of employees can lead to an explosion of
33+
// permission change events that would require a lot of computational resources to process, both on Materialize and
34+
// the consumer side.
35+
//
36+
// WatchPermissionSets is thus recommended for any larger scale use case where the fan-out in permission changes that
37+
// emerges from a specific schema and data shape is too large to handle effectively.
38+
//
39+
// The API does not offer a sharding mechanism and thus there should only be one consumer per target system.
40+
// Implementing an active-active HA consumer setup over the same target system will require coordinating which
41+
// revisions have been consumed in order to prevent transitioning to an inconsistent state.
42+
rpc WatchPermissionSets(WatchPermissionSetsRequest) returns (stream WatchPermissionSetsResponse) {}
43+
44+
// LookupPermissionSets returns the current state of the permission sets which can be used to derive the computed permissions.
45+
// It's typically used to backfill the state of the permission sets in the consumer side.
46+
//
47+
// It's a cursored API and the consumer is responsible to keep track of the cursor and use it on each subsequent call.
48+
// Each stream will return <N> permission sets defined by the specified request limit. The server will keep streaming until
49+
// the sets per stream is hit, or the current state of the sets is reached,
50+
// whatever happens first, and then close the stream. The server will indicate there are no more changes to stream
51+
// through the `completed_members` in the cursor.
52+
//
53+
// There may be many elements to stream, and so the consumer should be prepared to resume the stream from the last
54+
// cursor received. Once completed, the consumer may start streaming permission set changes using WatchPermissionSets
55+
// and the revision token from the last LookupPermissionSets response.
56+
rpc LookupPermissionSets(LookupPermissionSetsRequest) returns (stream LookupPermissionSetsResponse) {}
57+
}
58+
59+
message WatchPermissionSetsRequest {
60+
// optional_starting_after is used to specify the SpiceDB revision to start watching from.
61+
// If not specified, the watch will start from the current SpiceDB revision time of the request ("head revision").
62+
authzed.api.v1.ZedToken optional_starting_after = 1;
63+
}
64+
65+
message WatchPermissionSetsResponse {
66+
oneof response {
67+
// change is the permission set delta that has occurred as result of a mutation in origin SpiceDB.
68+
// The consumer should apply this change to the current state of the permission sets in their target system.
69+
// Once an event arrives with completed_revision instead, the consumer shall consider the set of
70+
// changes originating from that revision completed.
71+
//
72+
// The consumer should keep track of the revision in order to resume streaming in the event of consumer restarts.
73+
PermissionSetChange change = 1;
74+
75+
// completed_revision is the revision token that indicates the completion of a set of changes. It may also be
76+
// received without accompanying set of changes, indicating that a mutation in the origin SpiceDB cluster did
77+
// not yield any effective changes in the permission sets
78+
authzed.api.v1.ZedToken completed_revision = 2;
79+
80+
// lookup_permission_sets_required is a signal that the consumer should perform a LookupPermissionSets call because
81+
// the permission set snapshot needs to be rebuilt from scratch. This typically happens when the origin SpiceDB
82+
// cluster has seen its schema changed.
83+
LookupPermissionSetsRequired lookup_permission_sets_required = 3;
84+
}
85+
}
86+
87+
message Cursor {
88+
// limit is the number of permission sets to stream over a single LookupPermissionSets call that was requested.
89+
uint32 limit = 1;
90+
// token is the snapshot revision at which the cursor was computed.
91+
authzed.api.v1.ZedToken token = 4;
92+
// starting_index is an offset of the permission set represented by this cursor
93+
uint32 starting_index = 5;
94+
// completed_members is a boolean flag that indicates that the cursor has reached the end of the permission sets
95+
bool completed_members = 6;
96+
}
97+
98+
message LookupPermissionSetsRequest {
99+
// limit is the number of permission sets to stream over a single LookupPermissionSets. Once the limit is reached,
100+
// the server will close the stream. If more permission sets are available, the consume should open a new stream
101+
// providing optional_starting_after_cursor, using the cursor from the last response.
102+
uint32 limit = 1;
103+
// optional_starting_after_cursor is used to specify the offset to start streaming permission sets from.
104+
Cursor optional_starting_after_cursor = 4;
105+
}
106+
107+
message LookupPermissionSetsResponse {
108+
// change represents the permission set delta necessary to transition an uninitialized target system to
109+
// a specific snapshot revision. In practice it's not different from the WatchPermissionSetsResponse.change, except
110+
// all changes will be of time SET_OPERATION_ADDED because it's assumed there is no known previous state.
111+
//
112+
// Applying the deltas to a previously initialized target system would yield incorrect results.
113+
PermissionSetChange change = 1;
114+
// cursor points to a specific permission set in a revision.
115+
// The consumer should keep track of the cursor in order to resume streaming in the event of consumer restarts. This
116+
// is particularly important in backfill scenarios that may take hours or event days to complete.
117+
Cursor cursor = 2;
118+
}
119+
120+
message PermissionSetChange {
121+
enum SetOperation {
122+
SET_OPERATION_UNSPECIFIED = 0;
123+
SET_OPERATION_ADDED = 1;
124+
SET_OPERATION_REMOVED = 2;
125+
}
126+
127+
// revision represents the revision at which the permission set change occurred.
128+
authzed.api.v1.ZedToken at_revision = 1;
129+
// operation represents the type of set operation that took place as part of the change
130+
SetOperation operation = 2;
131+
// parent_set represents the permission set parent of either another set or a member
132+
SetReference parent_set = 3;
133+
134+
oneof child {
135+
// child_set represents the scenario where another set is considered member of the parent set
136+
SetReference child_set = 4;
137+
// child_member represents the scenario where an specific object is considered member of the parent set
138+
MemberReference child_member = 5;
139+
}
140+
}
141+
142+
message SetReference {
143+
// object_type is the type of object in a permission set
144+
string object_type = 1;
145+
// object_id is the ID of a permission set
146+
string object_id = 2;
147+
// permission_or_relation is the permission or relation referenced by this permission set
148+
string permission_or_relation = 3;
149+
}
150+
151+
message MemberReference {
152+
// object_type is the type of object of a permission set member
153+
string object_type = 1;
154+
// object_id is the ID of a permission set member
155+
string object_id = 2;
156+
// optional_permission_or_relation is the permission or relation referenced by this permission set member
157+
string optional_permission_or_relation = 3;
158+
}
159+
160+
// LookupPermissionSetsRequired is a signal that the consumer should perform a LookupPermissionSets call because
161+
// the permission set snapshot needs to be rebuilt from scratch. This typically happens when the origin SpiceDB
162+
// cluster has seen its schema changed.
163+
message LookupPermissionSetsRequired {
164+
// required_lookup_at is the snapshot revision at which the permission set needs to be rebuilt to.
165+
authzed.api.v1.ZedToken required_lookup_at = 1;
166+
}

0 commit comments

Comments
 (0)