From cb090f2dfef87590ba1ca8a2efcbea131678923e Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Mon, 27 Jan 2025 12:06:42 -0500 Subject: [PATCH 01/11] Add zookie proto def to api Signed-off-by: Jonathan Marcantonio --- api/kessel/relations/v1beta1/check.pb.go | 100 +++--- api/kessel/relations/v1beta1/check.proto | 2 + api/kessel/relations/v1beta1/common.pb.go | 70 +++- api/kessel/relations/v1beta1/common.proto | 5 + api/kessel/relations/v1beta1/lookup.pb.go | 241 ++++++++------ api/kessel/relations/v1beta1/lookup.proto | 4 + .../relations/v1beta1/relation_tuples.pb.go | 312 ++++++++++-------- .../relations/v1beta1/relation_tuples.proto | 10 +- openapi.yaml | 36 +- 9 files changed, 506 insertions(+), 274 deletions(-) diff --git a/api/kessel/relations/v1beta1/check.pb.go b/api/kessel/relations/v1beta1/check.pb.go index 0c0e94fe..1b6d4db2 100644 --- a/api/kessel/relations/v1beta1/check.pb.go +++ b/api/kessel/relations/v1beta1/check.pb.go @@ -77,6 +77,7 @@ type CheckRequest struct { Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + Zookie *Zookie `protobuf:"bytes,4,opt,name=zookie,proto3" json:"zookie,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -132,9 +133,17 @@ func (x *CheckRequest) GetSubject() *SubjectReference { return nil } +func (x *CheckRequest) GetZookie() *Zookie { + if x != nil { + return x.Zookie + } + return nil +} + type CheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Allowed CheckResponse_Allowed `protobuf:"varint,1,opt,name=allowed,proto3,enum=kessel.relations.v1beta1.CheckResponse_Allowed" json:"allowed,omitempty"` + CheckedAt *Zookie `protobuf:"bytes,2,opt,name=checked_at,json=checkedAt,proto3" json:"checked_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -176,6 +185,13 @@ func (x *CheckResponse) GetAllowed() CheckResponse_Allowed { return CheckResponse_ALLOWED_UNSPECIFIED } +func (x *CheckResponse) GetCheckedAt() *Zookie { + if x != nil { + return x.CheckedAt + } + return nil +} + var File_kessel_relations_v1beta1_check_proto protoreflect.FileDescriptor var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ @@ -189,7 +205,7 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x74, 0x6f, 0x22, 0x8a, 0x02, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, @@ -202,34 +218,41 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xa3, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, - 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x22, 0x47, 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, - 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, - 0x44, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, - 0x57, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x32, 0x89, 0x01, 0x0a, 0x12, - 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x73, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, - 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, - 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, + 0xe4, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, + 0x69, 0x65, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x22, 0x47, 0x0a, + 0x07, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, + 0x57, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x52, 0x55, + 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x32, 0x89, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, + 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, + 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, + 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -252,18 +275,21 @@ var file_kessel_relations_v1beta1_check_proto_goTypes = []any{ (*CheckResponse)(nil), // 2: kessel.relations.v1beta1.CheckResponse (*ObjectReference)(nil), // 3: kessel.relations.v1beta1.ObjectReference (*SubjectReference)(nil), // 4: kessel.relations.v1beta1.SubjectReference + (*Zookie)(nil), // 5: kessel.relations.v1beta1.Zookie } var file_kessel_relations_v1beta1_check_proto_depIdxs = []int32{ 3, // 0: kessel.relations.v1beta1.CheckRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference 4, // 1: kessel.relations.v1beta1.CheckRequest.subject:type_name -> kessel.relations.v1beta1.SubjectReference - 0, // 2: kessel.relations.v1beta1.CheckResponse.allowed:type_name -> kessel.relations.v1beta1.CheckResponse.Allowed - 1, // 3: kessel.relations.v1beta1.KesselCheckService.Check:input_type -> kessel.relations.v1beta1.CheckRequest - 2, // 4: kessel.relations.v1beta1.KesselCheckService.Check:output_type -> kessel.relations.v1beta1.CheckResponse - 4, // [4:5] is the sub-list for method output_type - 3, // [3:4] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 5, // 2: kessel.relations.v1beta1.CheckRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie + 0, // 3: kessel.relations.v1beta1.CheckResponse.allowed:type_name -> kessel.relations.v1beta1.CheckResponse.Allowed + 5, // 4: kessel.relations.v1beta1.CheckResponse.checked_at:type_name -> kessel.relations.v1beta1.Zookie + 1, // 5: kessel.relations.v1beta1.KesselCheckService.Check:input_type -> kessel.relations.v1beta1.CheckRequest + 2, // 6: kessel.relations.v1beta1.KesselCheckService.Check:output_type -> kessel.relations.v1beta1.CheckResponse + 6, // [6:7] is the sub-list for method output_type + 5, // [5:6] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_kessel_relations_v1beta1_check_proto_init() } diff --git a/api/kessel/relations/v1beta1/check.proto b/api/kessel/relations/v1beta1/check.proto index 721cd62b..0dd343a5 100644 --- a/api/kessel/relations/v1beta1/check.proto +++ b/api/kessel/relations/v1beta1/check.proto @@ -26,6 +26,7 @@ message CheckRequest { ObjectReference resource = 1 [(buf.validate.field).required = true]; string relation = 2 [(buf.validate.field).string.min_len = 1]; SubjectReference subject = 3 [(buf.validate.field).required = true]; + Zookie zookie = 4; } message CheckResponse { @@ -36,5 +37,6 @@ message CheckResponse { // e.g. ALLOWED_CONDITIONAL = 3; } Allowed allowed = 1; + Zookie checked_at = 2; } diff --git a/api/kessel/relations/v1beta1/common.pb.go b/api/kessel/relations/v1beta1/common.pb.go index 80a2b217..b9ccb577 100644 --- a/api/kessel/relations/v1beta1/common.pb.go +++ b/api/kessel/relations/v1beta1/common.pb.go @@ -346,6 +346,51 @@ func (x *ObjectType) GetName() string { return "" } +// The Zookie is used to provide consistency between write and read requests. +type Zookie struct { + state protoimpl.MessageState `protogen:"open.v1"` + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Zookie) Reset() { + *x = Zookie{} + mi := &file_kessel_relations_v1beta1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Zookie) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Zookie) ProtoMessage() {} + +func (x *Zookie) ProtoReflect() protoreflect.Message { + mi := &file_kessel_relations_v1beta1_common_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Zookie.ProtoReflect.Descriptor instead. +func (*Zookie) Descriptor() ([]byte, []int) { + return file_kessel_relations_v1beta1_common_proto_rawDescGZIP(), []int{6} +} + +func (x *Zookie) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + var File_kessel_relations_v1beta1_common_proto protoreflect.FileDescriptor var file_kessel_relations_v1beta1_common_proto_rawDesc = string([]byte{ @@ -401,15 +446,17 @@ var file_kessel_relations_v1beta1_common_proto_rawDesc = string([]byte{ 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x27, 0x0a, 0x06, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, + 0x1d, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x72, + 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, + 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -424,7 +471,7 @@ func file_kessel_relations_v1beta1_common_proto_rawDescGZIP() []byte { return file_kessel_relations_v1beta1_common_proto_rawDescData } -var file_kessel_relations_v1beta1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_kessel_relations_v1beta1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_kessel_relations_v1beta1_common_proto_goTypes = []any{ (*Relationship)(nil), // 0: kessel.relations.v1beta1.Relationship (*SubjectReference)(nil), // 1: kessel.relations.v1beta1.SubjectReference @@ -432,6 +479,7 @@ var file_kessel_relations_v1beta1_common_proto_goTypes = []any{ (*ResponsePagination)(nil), // 3: kessel.relations.v1beta1.ResponsePagination (*ObjectReference)(nil), // 4: kessel.relations.v1beta1.ObjectReference (*ObjectType)(nil), // 5: kessel.relations.v1beta1.ObjectType + (*Zookie)(nil), // 6: kessel.relations.v1beta1.Zookie } var file_kessel_relations_v1beta1_common_proto_depIdxs = []int32{ 4, // 0: kessel.relations.v1beta1.Relationship.resource:type_name -> kessel.relations.v1beta1.ObjectReference @@ -458,7 +506,7 @@ func file_kessel_relations_v1beta1_common_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_kessel_relations_v1beta1_common_proto_rawDesc), len(file_kessel_relations_v1beta1_common_proto_rawDesc)), NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/api/kessel/relations/v1beta1/common.proto b/api/kessel/relations/v1beta1/common.proto index 189be9fb..d06bac96 100644 --- a/api/kessel/relations/v1beta1/common.proto +++ b/api/kessel/relations/v1beta1/common.proto @@ -49,3 +49,8 @@ message ObjectType { string namespace = 1 [(buf.validate.field).string.min_len = 1]; string name = 2 [(buf.validate.field).string.min_len = 1]; } + +// The Zookie is used to provide consistency between write and read requests. +message Zookie { + string token = 1 [(buf.validate.field).string.min_len = 1]; +} diff --git a/api/kessel/relations/v1beta1/lookup.pb.go b/api/kessel/relations/v1beta1/lookup.pb.go index c3b3b508..30befcf7 100644 --- a/api/kessel/relations/v1beta1/lookup.pb.go +++ b/api/kessel/relations/v1beta1/lookup.pb.go @@ -29,6 +29,7 @@ type LookupResourcesRequest struct { Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` Pagination *RequestPagination `protobuf:"bytes,4,opt,name=pagination,proto3,oneof" json:"pagination,omitempty"` + Zookie *Zookie `protobuf:"bytes,5,opt,name=zookie,proto3,oneof" json:"zookie,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -91,10 +92,18 @@ func (x *LookupResourcesRequest) GetPagination() *RequestPagination { return nil } +func (x *LookupResourcesRequest) GetZookie() *Zookie { + if x != nil { + return x.Zookie + } + return nil +} + type LookupResourcesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` Pagination *ResponsePagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + LookedUpAt *Zookie `protobuf:"bytes,3,opt,name=looked_up_at,json=lookedUpAt,proto3" json:"looked_up_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -143,6 +152,13 @@ func (x *LookupResourcesResponse) GetPagination() *ResponsePagination { return nil } +func (x *LookupResourcesResponse) GetLookedUpAt() *Zookie { + if x != nil { + return x.LookedUpAt + } + return nil +} + type LookupSubjectsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` @@ -150,6 +166,7 @@ type LookupSubjectsRequest struct { SubjectType *ObjectType `protobuf:"bytes,3,opt,name=subject_type,json=subjectType,proto3" json:"subject_type,omitempty"` SubjectRelation *string `protobuf:"bytes,4,opt,name=subject_relation,json=subjectRelation,proto3,oneof" json:"subject_relation,omitempty"` Pagination *RequestPagination `protobuf:"bytes,5,opt,name=pagination,proto3,oneof" json:"pagination,omitempty"` + Zookie *Zookie `protobuf:"bytes,6,opt,name=zookie,proto3,oneof" json:"zookie,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -219,10 +236,18 @@ func (x *LookupSubjectsRequest) GetPagination() *RequestPagination { return nil } +func (x *LookupSubjectsRequest) GetZookie() *Zookie { + if x != nil { + return x.Zookie + } + return nil +} + type LookupSubjectsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Subject *SubjectReference `protobuf:"bytes,1,opt,name=subject,proto3" json:"subject,omitempty"` Pagination *ResponsePagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + LookedUpAt *Zookie `protobuf:"bytes,3,opt,name=looked_up_at,json=lookedUpAt,proto3" json:"looked_up_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -271,6 +296,13 @@ func (x *LookupSubjectsResponse) GetPagination() *ResponsePagination { return nil } +func (x *LookupSubjectsResponse) GetLookedUpAt() *Zookie { + if x != nil { + return x.LookedUpAt + } + return nil +} + var File_kessel_relations_v1beta1_lookup_proto protoreflect.FileDescriptor var file_kessel_relations_v1beta1_lookup_proto_rawDesc = string([]byte{ @@ -284,7 +316,7 @@ var file_kessel_relations_v1beta1_lookup_proto_rawDesc = string([]byte{ 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x16, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x6f, 0x74, 0x6f, 0x22, 0x89, 0x03, 0x0a, 0x16, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, @@ -303,82 +335,100 @@ var file_kessel_relations_v1beta1_lookup_proto_rawDesc = string([]byte{ 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xae, 0x01, 0x0a, 0x17, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x45, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, - 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x82, 0x03, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x4d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x23, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x65, 0x73, - 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, - 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, - 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xac, 0x01, 0x0a, 0x16, - 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x48, 0x01, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, + 0x69, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, + 0xf2, 0x01, 0x0a, 0x17, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x42, 0x0a, 0x0c, 0x6c, 0x6f, 0x6f, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x5f, 0x61, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x0a, 0x6c, 0x6f, 0x6f, 0x6b, 0x65, 0x64, + 0x55, 0x70, 0x41, 0x74, 0x22, 0xcc, 0x03, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, + 0xc8, 0x01, 0x01, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x23, 0x0a, + 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x06, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x4c, 0x0a, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xbf, 0x02, 0x0a, 0x13, 0x4b, - 0x65, 0x73, 0x73, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x90, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, + 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, - 0x12, 0x11, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x30, 0x01, 0x12, 0x94, 0x01, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x6b, 0x65, 0x73, 0x73, - 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6b, 0x65, - 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x30, 0x01, 0x42, 0x72, 0x0a, 0x28, - 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, - 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, - 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, - 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x48, 0x02, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x7a, 0x6f, 0x6f, + 0x6b, 0x69, 0x65, 0x22, 0xf0, 0x01, 0x0a, 0x16, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0c, 0x6c, 0x6f, 0x6f, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x5f, + 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x0a, 0x6c, 0x6f, 0x6f, 0x6b, + 0x65, 0x64, 0x55, 0x70, 0x41, 0x74, 0x32, 0xbf, 0x02, 0x0a, 0x13, 0x4b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x90, + 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x30, + 0x01, 0x12, 0x94, 0x01, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, + 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x30, 0x01, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -402,29 +452,34 @@ var file_kessel_relations_v1beta1_lookup_proto_goTypes = []any{ (*ObjectType)(nil), // 4: kessel.relations.v1beta1.ObjectType (*SubjectReference)(nil), // 5: kessel.relations.v1beta1.SubjectReference (*RequestPagination)(nil), // 6: kessel.relations.v1beta1.RequestPagination - (*ObjectReference)(nil), // 7: kessel.relations.v1beta1.ObjectReference - (*ResponsePagination)(nil), // 8: kessel.relations.v1beta1.ResponsePagination + (*Zookie)(nil), // 7: kessel.relations.v1beta1.Zookie + (*ObjectReference)(nil), // 8: kessel.relations.v1beta1.ObjectReference + (*ResponsePagination)(nil), // 9: kessel.relations.v1beta1.ResponsePagination } var file_kessel_relations_v1beta1_lookup_proto_depIdxs = []int32{ 4, // 0: kessel.relations.v1beta1.LookupResourcesRequest.resource_type:type_name -> kessel.relations.v1beta1.ObjectType 5, // 1: kessel.relations.v1beta1.LookupResourcesRequest.subject:type_name -> kessel.relations.v1beta1.SubjectReference 6, // 2: kessel.relations.v1beta1.LookupResourcesRequest.pagination:type_name -> kessel.relations.v1beta1.RequestPagination - 7, // 3: kessel.relations.v1beta1.LookupResourcesResponse.resource:type_name -> kessel.relations.v1beta1.ObjectReference - 8, // 4: kessel.relations.v1beta1.LookupResourcesResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination - 7, // 5: kessel.relations.v1beta1.LookupSubjectsRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference - 4, // 6: kessel.relations.v1beta1.LookupSubjectsRequest.subject_type:type_name -> kessel.relations.v1beta1.ObjectType - 6, // 7: kessel.relations.v1beta1.LookupSubjectsRequest.pagination:type_name -> kessel.relations.v1beta1.RequestPagination - 5, // 8: kessel.relations.v1beta1.LookupSubjectsResponse.subject:type_name -> kessel.relations.v1beta1.SubjectReference - 8, // 9: kessel.relations.v1beta1.LookupSubjectsResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination - 2, // 10: kessel.relations.v1beta1.KesselLookupService.LookupSubjects:input_type -> kessel.relations.v1beta1.LookupSubjectsRequest - 0, // 11: kessel.relations.v1beta1.KesselLookupService.LookupResources:input_type -> kessel.relations.v1beta1.LookupResourcesRequest - 3, // 12: kessel.relations.v1beta1.KesselLookupService.LookupSubjects:output_type -> kessel.relations.v1beta1.LookupSubjectsResponse - 1, // 13: kessel.relations.v1beta1.KesselLookupService.LookupResources:output_type -> kessel.relations.v1beta1.LookupResourcesResponse - 12, // [12:14] is the sub-list for method output_type - 10, // [10:12] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 7, // 3: kessel.relations.v1beta1.LookupResourcesRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie + 8, // 4: kessel.relations.v1beta1.LookupResourcesResponse.resource:type_name -> kessel.relations.v1beta1.ObjectReference + 9, // 5: kessel.relations.v1beta1.LookupResourcesResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination + 7, // 6: kessel.relations.v1beta1.LookupResourcesResponse.looked_up_at:type_name -> kessel.relations.v1beta1.Zookie + 8, // 7: kessel.relations.v1beta1.LookupSubjectsRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference + 4, // 8: kessel.relations.v1beta1.LookupSubjectsRequest.subject_type:type_name -> kessel.relations.v1beta1.ObjectType + 6, // 9: kessel.relations.v1beta1.LookupSubjectsRequest.pagination:type_name -> kessel.relations.v1beta1.RequestPagination + 7, // 10: kessel.relations.v1beta1.LookupSubjectsRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie + 5, // 11: kessel.relations.v1beta1.LookupSubjectsResponse.subject:type_name -> kessel.relations.v1beta1.SubjectReference + 9, // 12: kessel.relations.v1beta1.LookupSubjectsResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination + 7, // 13: kessel.relations.v1beta1.LookupSubjectsResponse.looked_up_at:type_name -> kessel.relations.v1beta1.Zookie + 2, // 14: kessel.relations.v1beta1.KesselLookupService.LookupSubjects:input_type -> kessel.relations.v1beta1.LookupSubjectsRequest + 0, // 15: kessel.relations.v1beta1.KesselLookupService.LookupResources:input_type -> kessel.relations.v1beta1.LookupResourcesRequest + 3, // 16: kessel.relations.v1beta1.KesselLookupService.LookupSubjects:output_type -> kessel.relations.v1beta1.LookupSubjectsResponse + 1, // 17: kessel.relations.v1beta1.KesselLookupService.LookupResources:output_type -> kessel.relations.v1beta1.LookupResourcesResponse + 16, // [16:18] is the sub-list for method output_type + 14, // [14:16] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_kessel_relations_v1beta1_lookup_proto_init() } diff --git a/api/kessel/relations/v1beta1/lookup.proto b/api/kessel/relations/v1beta1/lookup.proto index e0439c4e..7c523de9 100644 --- a/api/kessel/relations/v1beta1/lookup.proto +++ b/api/kessel/relations/v1beta1/lookup.proto @@ -29,11 +29,13 @@ message LookupResourcesRequest { string relation = 2 [(buf.validate.field).string.min_len = 1]; SubjectReference subject = 3 [(buf.validate.field).required = true]; optional RequestPagination pagination = 4; + optional Zookie zookie = 5; } message LookupResourcesResponse { ObjectReference resource = 1; ResponsePagination pagination = 2; + Zookie looked_up_at = 3; } message LookupSubjectsRequest { @@ -42,9 +44,11 @@ message LookupSubjectsRequest { ObjectType subject_type = 3 [(buf.validate.field).required = true]; optional string subject_relation = 4; optional RequestPagination pagination = 5; + optional Zookie zookie = 6; } message LookupSubjectsResponse { SubjectReference subject = 1; ResponsePagination pagination = 2; + Zookie looked_up_at = 3; } \ No newline at end of file diff --git a/api/kessel/relations/v1beta1/relation_tuples.pb.go b/api/kessel/relations/v1beta1/relation_tuples.pb.go index cef5eab2..46cbf337 100644 --- a/api/kessel/relations/v1beta1/relation_tuples.pb.go +++ b/api/kessel/relations/v1beta1/relation_tuples.pb.go @@ -168,6 +168,7 @@ func (x *CreateTuplesRequest) GetTuples() []*Relationship { type CreateTuplesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` + CreatedAt *Zookie `protobuf:"bytes,1,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -202,10 +203,18 @@ func (*CreateTuplesResponse) Descriptor() ([]byte, []int) { return file_kessel_relations_v1beta1_relation_tuples_proto_rawDescGZIP(), []int{3} } +func (x *CreateTuplesResponse) GetCreatedAt() *Zookie { + if x != nil { + return x.CreatedAt + } + return nil +} + type ReadTuplesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Filter *RelationTupleFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` Pagination *RequestPagination `protobuf:"bytes,2,opt,name=pagination,proto3,oneof" json:"pagination,omitempty"` + Zookie *Zookie `protobuf:"bytes,3,opt,name=zookie,proto3,oneof" json:"zookie,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -254,10 +263,18 @@ func (x *ReadTuplesRequest) GetPagination() *RequestPagination { return nil } +func (x *ReadTuplesRequest) GetZookie() *Zookie { + if x != nil { + return x.Zookie + } + return nil +} + type ReadTuplesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Tuple *Relationship `protobuf:"bytes,1,opt,name=tuple,proto3" json:"tuple,omitempty"` Pagination *ResponsePagination `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + ReadAt *Zookie `protobuf:"bytes,3,opt,name=read_at,json=readAt,proto3" json:"read_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -306,6 +323,13 @@ func (x *ReadTuplesResponse) GetPagination() *ResponsePagination { return nil } +func (x *ReadTuplesResponse) GetReadAt() *Zookie { + if x != nil { + return x.ReadAt + } + return nil +} + type DeleteTuplesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Filter *RelationTupleFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` @@ -352,6 +376,7 @@ func (x *DeleteTuplesRequest) GetFilter() *RelationTupleFilter { type DeleteTuplesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` + DeletedAt *Zookie `protobuf:"bytes,1,opt,name=deleted_at,json=deletedAt,proto3" json:"deleted_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -386,6 +411,13 @@ func (*DeleteTuplesResponse) Descriptor() ([]byte, []int) { return file_kessel_relations_v1beta1_relation_tuples_proto_rawDescGZIP(), []int{7} } +func (x *DeleteTuplesResponse) GetDeletedAt() *Zookie { + if x != nil { + return x.DeletedAt + } + return nil +} + // RelationTupleFilter is used to filter tuples based on their resource, relation, and subject. // All fields are optional but capabilities may vary based on the chosen store and its indexes. // At least one field must be provided. @@ -564,121 +596,138 @@ var file_kessel_relations_v1beta1_relation_tuples_proto_rawDesc = string([]byte{ 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x06, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x22, - 0x16, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, + 0x57, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x8d, 0x02, 0x0a, 0x11, 0x52, 0x65, 0x61, + 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, + 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, + 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x06, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x50, 0x0a, + 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, + 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, + 0x3d, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x48, 0x01, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, + 0x07, 0x5f, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x61, + 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3c, 0x0a, 0x05, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, + 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x05, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x12, 0x4c, 0x0a, + 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x07, 0x72, + 0x65, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, + 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, + 0x72, 0x65, 0x61, 0x64, 0x41, 0x74, 0x22, 0x64, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x06, 0xba, 0x48, - 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, - 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0d, - 0x0a, 0x0b, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa0, 0x01, - 0x0a, 0x12, 0x52, 0x65, 0x61, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x05, 0x74, 0x75, 0x70, - 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x64, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, - 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe8, - 0x02, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x53, 0x0a, 0x0e, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x04, 0x52, 0x0d, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x11, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, - 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x09, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xd3, 0x04, - 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, - 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, - 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, - 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, - 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, - 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6b, + 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x57, 0x0a, 0x14, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, + 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xe8, 0x02, 0x0a, 0x13, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x32, 0x0a, + 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x02, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x88, 0x01, + 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, + 0x01, 0x01, 0x12, 0x53, 0x0a, 0x0e, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x48, 0x04, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x10, + 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, + 0x0f, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x12, 0x30, 0x0a, 0x11, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x02, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, + 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, + 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xd3, 0x04, 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x54, + 0x75, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x0c, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x75, 0x70, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x75, 0x70, - 0x6c, 0x65, 0x73, 0x30, 0x01, 0x12, 0x86, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x70, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, + 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x2a, 0x0f, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0xa2, - 0x01, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x75, 0x70, - 0x6c, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x75, 0x70, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x62, 0x75, 0x6c, 0x6b, 0x69, 0x6d, 0x70, 0x6f, 0x72, - 0x74, 0x28, 0x01, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, - 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, - 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x52, + 0x65, 0x61, 0x64, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x30, 0x01, 0x12, 0x86, 0x01, 0x0a, + 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2d, 0x2e, + 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, + 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6b, + 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x11, 0x2a, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, + 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0xa2, 0x01, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x42, 0x75, 0x6c, 0x6b, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x75, 0x6c, 0x6b, + 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, + 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, + 0x75, 0x6c, 0x6b, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x75, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x62, 0x75, + 0x6c, 0x6b, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x28, 0x01, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, + 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, + 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -706,31 +755,36 @@ var file_kessel_relations_v1beta1_relation_tuples_proto_goTypes = []any{ (*RelationTupleFilter)(nil), // 8: kessel.relations.v1beta1.RelationTupleFilter (*SubjectFilter)(nil), // 9: kessel.relations.v1beta1.SubjectFilter (*Relationship)(nil), // 10: kessel.relations.v1beta1.Relationship - (*RequestPagination)(nil), // 11: kessel.relations.v1beta1.RequestPagination - (*ResponsePagination)(nil), // 12: kessel.relations.v1beta1.ResponsePagination + (*Zookie)(nil), // 11: kessel.relations.v1beta1.Zookie + (*RequestPagination)(nil), // 12: kessel.relations.v1beta1.RequestPagination + (*ResponsePagination)(nil), // 13: kessel.relations.v1beta1.ResponsePagination } var file_kessel_relations_v1beta1_relation_tuples_proto_depIdxs = []int32{ 10, // 0: kessel.relations.v1beta1.ImportBulkTuplesRequest.tuples:type_name -> kessel.relations.v1beta1.Relationship 10, // 1: kessel.relations.v1beta1.CreateTuplesRequest.tuples:type_name -> kessel.relations.v1beta1.Relationship - 8, // 2: kessel.relations.v1beta1.ReadTuplesRequest.filter:type_name -> kessel.relations.v1beta1.RelationTupleFilter - 11, // 3: kessel.relations.v1beta1.ReadTuplesRequest.pagination:type_name -> kessel.relations.v1beta1.RequestPagination - 10, // 4: kessel.relations.v1beta1.ReadTuplesResponse.tuple:type_name -> kessel.relations.v1beta1.Relationship - 12, // 5: kessel.relations.v1beta1.ReadTuplesResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination - 8, // 6: kessel.relations.v1beta1.DeleteTuplesRequest.filter:type_name -> kessel.relations.v1beta1.RelationTupleFilter - 9, // 7: kessel.relations.v1beta1.RelationTupleFilter.subject_filter:type_name -> kessel.relations.v1beta1.SubjectFilter - 2, // 8: kessel.relations.v1beta1.KesselTupleService.CreateTuples:input_type -> kessel.relations.v1beta1.CreateTuplesRequest - 4, // 9: kessel.relations.v1beta1.KesselTupleService.ReadTuples:input_type -> kessel.relations.v1beta1.ReadTuplesRequest - 6, // 10: kessel.relations.v1beta1.KesselTupleService.DeleteTuples:input_type -> kessel.relations.v1beta1.DeleteTuplesRequest - 0, // 11: kessel.relations.v1beta1.KesselTupleService.ImportBulkTuples:input_type -> kessel.relations.v1beta1.ImportBulkTuplesRequest - 3, // 12: kessel.relations.v1beta1.KesselTupleService.CreateTuples:output_type -> kessel.relations.v1beta1.CreateTuplesResponse - 5, // 13: kessel.relations.v1beta1.KesselTupleService.ReadTuples:output_type -> kessel.relations.v1beta1.ReadTuplesResponse - 7, // 14: kessel.relations.v1beta1.KesselTupleService.DeleteTuples:output_type -> kessel.relations.v1beta1.DeleteTuplesResponse - 1, // 15: kessel.relations.v1beta1.KesselTupleService.ImportBulkTuples:output_type -> kessel.relations.v1beta1.ImportBulkTuplesResponse - 12, // [12:16] is the sub-list for method output_type - 8, // [8:12] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 11, // 2: kessel.relations.v1beta1.CreateTuplesResponse.created_at:type_name -> kessel.relations.v1beta1.Zookie + 8, // 3: kessel.relations.v1beta1.ReadTuplesRequest.filter:type_name -> kessel.relations.v1beta1.RelationTupleFilter + 12, // 4: kessel.relations.v1beta1.ReadTuplesRequest.pagination:type_name -> kessel.relations.v1beta1.RequestPagination + 11, // 5: kessel.relations.v1beta1.ReadTuplesRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie + 10, // 6: kessel.relations.v1beta1.ReadTuplesResponse.tuple:type_name -> kessel.relations.v1beta1.Relationship + 13, // 7: kessel.relations.v1beta1.ReadTuplesResponse.pagination:type_name -> kessel.relations.v1beta1.ResponsePagination + 11, // 8: kessel.relations.v1beta1.ReadTuplesResponse.read_at:type_name -> kessel.relations.v1beta1.Zookie + 8, // 9: kessel.relations.v1beta1.DeleteTuplesRequest.filter:type_name -> kessel.relations.v1beta1.RelationTupleFilter + 11, // 10: kessel.relations.v1beta1.DeleteTuplesResponse.deleted_at:type_name -> kessel.relations.v1beta1.Zookie + 9, // 11: kessel.relations.v1beta1.RelationTupleFilter.subject_filter:type_name -> kessel.relations.v1beta1.SubjectFilter + 2, // 12: kessel.relations.v1beta1.KesselTupleService.CreateTuples:input_type -> kessel.relations.v1beta1.CreateTuplesRequest + 4, // 13: kessel.relations.v1beta1.KesselTupleService.ReadTuples:input_type -> kessel.relations.v1beta1.ReadTuplesRequest + 6, // 14: kessel.relations.v1beta1.KesselTupleService.DeleteTuples:input_type -> kessel.relations.v1beta1.DeleteTuplesRequest + 0, // 15: kessel.relations.v1beta1.KesselTupleService.ImportBulkTuples:input_type -> kessel.relations.v1beta1.ImportBulkTuplesRequest + 3, // 16: kessel.relations.v1beta1.KesselTupleService.CreateTuples:output_type -> kessel.relations.v1beta1.CreateTuplesResponse + 5, // 17: kessel.relations.v1beta1.KesselTupleService.ReadTuples:output_type -> kessel.relations.v1beta1.ReadTuplesResponse + 7, // 18: kessel.relations.v1beta1.KesselTupleService.DeleteTuples:output_type -> kessel.relations.v1beta1.DeleteTuplesResponse + 1, // 19: kessel.relations.v1beta1.KesselTupleService.ImportBulkTuples:output_type -> kessel.relations.v1beta1.ImportBulkTuplesResponse + 16, // [16:20] is the sub-list for method output_type + 12, // [12:16] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_kessel_relations_v1beta1_relation_tuples_proto_init() } diff --git a/api/kessel/relations/v1beta1/relation_tuples.proto b/api/kessel/relations/v1beta1/relation_tuples.proto index 1bdbffa2..e2f317d5 100644 --- a/api/kessel/relations/v1beta1/relation_tuples.proto +++ b/api/kessel/relations/v1beta1/relation_tuples.proto @@ -58,21 +58,27 @@ message CreateTuplesRequest { bool upsert = 1; repeated Relationship tuples = 2; } -message CreateTuplesResponse {} +message CreateTuplesResponse { + Zookie created_at = 1; +} message ReadTuplesRequest { RelationTupleFilter filter = 1 [(buf.validate.field).required = true]; optional RequestPagination pagination = 2; + optional Zookie zookie = 3; } message ReadTuplesResponse { Relationship tuple = 1; ResponsePagination pagination = 2; + Zookie read_at = 3; } message DeleteTuplesRequest { RelationTupleFilter filter = 1 [(buf.validate.field).required = true]; } -message DeleteTuplesResponse {} +message DeleteTuplesResponse { + Zookie deleted_at = 1; +} // RelationTupleFilter is used to filter tuples based on their resource, relation, and subject. // All fields are optional but capabilities may vary based on the chosen store and its indexes. diff --git a/openapi.yaml b/openapi.yaml index ac9c016e..0bd6dcc3 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -95,6 +95,10 @@ paths: in: query schema: type: string + - name: zookie.token + in: query + schema: + type: string responses: "200": description: OK @@ -145,6 +149,10 @@ paths: in: query schema: type: string + - name: zookie.token + in: query + schema: + type: string responses: "200": description: OK @@ -199,6 +207,10 @@ paths: in: query schema: type: string + - name: zookie.token + in: query + schema: + type: string responses: "200": description: OK @@ -312,12 +324,16 @@ components: type: string subject: $ref: '#/components/schemas/kessel.relations.v1beta1.SubjectReference' + zookie: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.CheckResponse: type: object properties: allowed: type: integer format: enum + checkedAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.CreateTuplesRequest: type: object properties: @@ -333,10 +349,14 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.Relationship' kessel.relations.v1beta1.CreateTuplesResponse: type: object - properties: {} + properties: + createdAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.DeleteTuplesResponse: type: object - properties: {} + properties: + deletedAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.ImportBulkTuplesRequest: type: object properties: @@ -356,6 +376,8 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.ObjectReference' pagination: $ref: '#/components/schemas/kessel.relations.v1beta1.ResponsePagination' + lookedUpAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.LookupSubjectsResponse: type: object properties: @@ -363,6 +385,8 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.SubjectReference' pagination: $ref: '#/components/schemas/kessel.relations.v1beta1.ResponsePagination' + lookedUpAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.ObjectReference: type: object properties: @@ -384,6 +408,8 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.Relationship' pagination: $ref: '#/components/schemas/kessel.relations.v1beta1.ResponsePagination' + readAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.Relationship: type: object properties: @@ -410,6 +436,12 @@ components: subject: $ref: '#/components/schemas/kessel.relations.v1beta1.ObjectReference' description: A reference to a Subject or, if a `relation` is provided, a Subject Set. + kessel.relations.v1beta1.Zookie: + type: object + properties: + token: + type: string + description: The Zookie is used to provide consistency between write and read requests. tags: - name: KesselCheckService - name: KesselLookupService From 5bc00f9a4b0cb009d71264f0012db3298a3cddf0 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Mon, 27 Jan 2025 14:41:02 -0500 Subject: [PATCH 02/11] Disable fully consistent mode Signed-off-by: Jonathan Marcantonio --- configs/config.yaml | 2 +- deploy/kessel-relations.yaml | 2 +- internal/data/LocalSpiceDbContainer.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/config.yaml b/configs/config.yaml index 0966e7cf..4f5816fe 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -17,4 +17,4 @@ data: token: "${PRESHARED}" # token takes precedence over tokenFile tokenFile: "${PRESHARED_FILE:.secrets/local-spicedb-secret}" schemaFile: "${SCHEMA_FILE:deploy/schema.zed}" - fullyConsistent: true + fullyConsistent: false diff --git a/deploy/kessel-relations.yaml b/deploy/kessel-relations.yaml index c0743af4..7d7fb1f6 100644 --- a/deploy/kessel-relations.yaml +++ b/deploy/kessel-relations.yaml @@ -34,7 +34,7 @@ objects: token: "${PRESHARED}" # token takes precedence over tokenFile tokenFile: "${PRESHARED_FILE:.secrets/local-spicedb-secret}" schemaFile: "${SCHEMA_FILE:deploy/schema.zed}" - fullyConsistent: true + fullyConsistent: false - apiVersion: v1 kind: Secret metadata: diff --git a/internal/data/LocalSpiceDbContainer.go b/internal/data/LocalSpiceDbContainer.go index 3d3801a3..9532bdf3 100644 --- a/internal/data/LocalSpiceDbContainer.go +++ b/internal/data/LocalSpiceDbContainer.go @@ -35,7 +35,7 @@ const ( SpicedbRelationsBootstrapFile = "" // FullyConsistent specifices the consistency mode used for our read API calls // may experience different results between tests and manual probing if the values differ - FullyConsistent = true // Should probably be inline with our config file. (TODO: Can we make our tests grab the same value?) + FullyConsistent = false // Should probably be inline with our config file. (TODO: Can we make our tests grab the same value?) ) // LocalSpiceDbContainer struct that holds pointers to the container, dockertest pool and exposes the port From 04229bb85efcb2afdcb42af6b8efba70b7832797 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Wed, 29 Jan 2025 10:45:24 -0500 Subject: [PATCH 03/11] Expose zookie in API + update tests Signed-off-by: Jonathan Marcantonio --- internal/biz/lookup.go | 4 +- internal/biz/relationships.go | 20 ++-- internal/data/LocalSpiceDbContainer.go | 2 +- internal/data/spicedb.go | 91 +++++++++++------- internal/data/spicedb_test.go | 128 +++++++++++++++++++------ internal/service/lookup.go | 2 + internal/service/lookup_test.go | 28 +++--- internal/service/relationships.go | 10 +- 8 files changed, 192 insertions(+), 93 deletions(-) diff --git a/internal/biz/lookup.go b/internal/biz/lookup.go index b3084da0..053971c8 100644 --- a/internal/biz/lookup.go +++ b/internal/biz/lookup.go @@ -50,7 +50,7 @@ func (s *GetSubjectsUsecase) Get(ctx context.Context, req *v1beta1.LookupSubject subs, errs, err := s.repo.LookupSubjects(ctx, req.SubjectType, subjectRelation, req.Relation, &v1beta1.ObjectReference{ Type: req.Resource.Type, Id: req.Resource.Id, - }, limit, continuation) + }, limit, continuation, req.Zookie) if err != nil { return nil, nil, err @@ -72,7 +72,7 @@ func (r *GetResourcesUsecase) Get(ctx context.Context, req *v1beta1.LookupResour continuation = ContinuationToken(*req.Pagination.ContinuationToken) } } - resources, errs, err := r.repo.LookupResources(ctx, req.ResourceType, req.Relation, req.Subject, limit, continuation) + resources, errs, err := r.repo.LookupResources(ctx, req.ResourceType, req.Relation, req.Subject, limit, continuation, req.Zookie) if err != nil { return nil, nil, err } diff --git a/internal/biz/relationships.go b/internal/biz/relationships.go index 46d63f12..2e7c3bd6 100644 --- a/internal/biz/relationships.go +++ b/internal/biz/relationships.go @@ -2,6 +2,7 @@ package biz import ( "context" + "google.golang.org/grpc" v1beta1 "github.com/project-kessel/relations-api/api/kessel/relations/v1beta1" @@ -16,24 +17,27 @@ type ContinuationToken string type SubjectResult struct { Subject *v1beta1.SubjectReference Continuation ContinuationToken + Zookie *v1beta1.Zookie } type ResourceResult struct { Resource *v1beta1.ObjectReference Continuation ContinuationToken + Zookie *v1beta1.Zookie } type RelationshipResult struct { Relationship *v1beta1.Relationship Continuation ContinuationToken + Zookie *v1beta1.Zookie } type ZanzibarRepository interface { Check(ctx context.Context, request *v1beta1.CheckRequest) (*v1beta1.CheckResponse, error) - CreateRelationships(context.Context, []*v1beta1.Relationship, TouchSemantics) error - ReadRelationships(ctx context.Context, filter *v1beta1.RelationTupleFilter, limit uint32, continuation ContinuationToken) (chan *RelationshipResult, chan error, error) - DeleteRelationships(context.Context, *v1beta1.RelationTupleFilter) error - LookupSubjects(ctx context.Context, subjectType *v1beta1.ObjectType, subject_relation, relation string, resource *v1beta1.ObjectReference, limit uint32, continuation ContinuationToken) (chan *SubjectResult, chan error, error) - LookupResources(ctx context.Context, resouce_type *v1beta1.ObjectType, relation string, subject *v1beta1.SubjectReference, limit uint32, continuation ContinuationToken) (chan *ResourceResult, chan error, error) + CreateRelationships(context.Context, []*v1beta1.Relationship, TouchSemantics) (*v1beta1.CreateTuplesResponse, error) + ReadRelationships(ctx context.Context, filter *v1beta1.RelationTupleFilter, limit uint32, continuation ContinuationToken, zookie *v1beta1.Zookie) (chan *RelationshipResult, chan error, error) + DeleteRelationships(context.Context, *v1beta1.RelationTupleFilter) (*v1beta1.DeleteTuplesResponse, error) + LookupSubjects(ctx context.Context, subjectType *v1beta1.ObjectType, subject_relation, relation string, resource *v1beta1.ObjectReference, limit uint32, continuation ContinuationToken, zookie *v1beta1.Zookie) (chan *SubjectResult, chan error, error) + LookupResources(ctx context.Context, resouce_type *v1beta1.ObjectType, relation string, subject *v1beta1.SubjectReference, limit uint32, continuation ContinuationToken, zookie *v1beta1.Zookie) (chan *ResourceResult, chan error, error) IsBackendAvailable() error ImportBulkTuples(stream grpc.ClientStreamingServer[v1beta1.ImportBulkTuplesRequest, v1beta1.ImportBulkTuplesResponse]) error } @@ -60,7 +64,7 @@ func NewCreateRelationshipsUsecase(repo ZanzibarRepository, logger log.Logger) * return &CreateRelationshipsUsecase{repo: repo, log: log.NewHelper(logger)} } -func (rc *CreateRelationshipsUsecase) CreateRelationships(ctx context.Context, r []*v1beta1.Relationship, touch bool) error { +func (rc *CreateRelationshipsUsecase) CreateRelationships(ctx context.Context, r []*v1beta1.Relationship, touch bool) (*v1beta1.CreateTuplesResponse, error) { return rc.repo.CreateRelationships(ctx, r, TouchSemantics(touch)) } @@ -87,7 +91,7 @@ func (rc *ReadRelationshipsUsecase) ReadRelationships(ctx context.Context, req * } } - relationships, errs, err := rc.repo.ReadRelationships(ctx, req.Filter, limit, continuation) + relationships, errs, err := rc.repo.ReadRelationships(ctx, req.Filter, limit, continuation, req.Zookie) if err != nil { return nil, nil, err @@ -105,7 +109,7 @@ func NewDeleteRelationshipsUsecase(repo ZanzibarRepository, logger log.Logger) * return &DeleteRelationshipsUsecase{repo: repo, log: log.NewHelper(logger)} } -func (rc *DeleteRelationshipsUsecase) DeleteRelationships(ctx context.Context, r *v1beta1.RelationTupleFilter) error { +func (rc *DeleteRelationshipsUsecase) DeleteRelationships(ctx context.Context, r *v1beta1.RelationTupleFilter) (*v1beta1.DeleteTuplesResponse, error) { return rc.repo.DeleteRelationships(ctx, r) } diff --git a/internal/data/LocalSpiceDbContainer.go b/internal/data/LocalSpiceDbContainer.go index 9532bdf3..5f94fa68 100644 --- a/internal/data/LocalSpiceDbContainer.go +++ b/internal/data/LocalSpiceDbContainer.go @@ -209,7 +209,7 @@ func CheckForRelationship(client biz.ZanzibarRepository, subjectID string, subje SubjectId: &subjectID, Relation: subjectRelationRef, }, - }, 1, biz.ContinuationToken("")) + }, 1, biz.ContinuationToken(""), nil) if err != nil { panic(err) diff --git a/internal/data/spicedb.go b/internal/data/spicedb.go index 123f6222..815973fd 100644 --- a/internal/data/spicedb.go +++ b/internal/data/spicedb.go @@ -25,11 +25,11 @@ import ( // SpiceDbRepository . type SpiceDbRepository struct { - client *authzed.Client - healthClient grpc_health_v1.HealthClient - schemaFilePath string - isInitialized bool - consistency *v1.Consistency + client *authzed.Client + healthClient grpc_health_v1.HealthClient + schemaFilePath string + isInitialized bool + fullyConsistent bool } const ( @@ -91,15 +91,7 @@ func NewSpiceDbRepository(c *conf.Data, logger log.Logger) (*SpiceDbRepository, log.NewHelper(logger).Info("spicedb connection cleanup requested (nothing to clean up)") } - // Default consistency for read APIs is minimize_latency - // will attempt to minimize the latency of the API call by selecting data that is most likely exist in the cache. - consistency := &v1.Consistency{Requirement: &v1.Consistency_MinimizeLatency{MinimizeLatency: true}} - if c.SpiceDb.FullyConsistent { - // will ensure that all data used is fully consistent with the latest data available within the SpiceDB datastore. - consistency = &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}} - } - - return &SpiceDbRepository{client, healthClient, c.SpiceDb.SchemaFile, false, consistency}, cleanup, nil + return &SpiceDbRepository{client, healthClient, c.SpiceDb.SchemaFile, false, c.SpiceDb.FullyConsistent}, cleanup, nil } func (s *SpiceDbRepository) initialize() error { @@ -124,7 +116,7 @@ func (s *SpiceDbRepository) initialize() error { return nil } -func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *apiV1beta1.ObjectType, subject_relation, relation string, object *apiV1beta1.ObjectReference, limit uint32, continuation biz.ContinuationToken) (chan *biz.SubjectResult, chan error, error) { +func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *apiV1beta1.ObjectType, subject_relation, relation string, object *apiV1beta1.ObjectReference, limit uint32, continuation biz.ContinuationToken, zookie *apiV1beta1.Zookie) (chan *biz.SubjectResult, chan error, error) { if err := s.initialize(); err != nil { return nil, nil, err } @@ -137,7 +129,7 @@ func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *ap } req := &v1.LookupSubjectsRequest{ - Consistency: s.consistency, + Consistency: s.determineConsistency(zookie), Resource: &v1.ObjectReference{ ObjectType: kesselTypeToSpiceDBType(object.Type), ObjectId: object.Id, @@ -185,6 +177,7 @@ func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *ap }, }, Continuation: continuation, + Zookie: &apiV1beta1.Zookie{Token: msg.GetLookedUpAt().GetToken()}, } } }() @@ -192,7 +185,7 @@ func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *ap return subjects, errs, nil } -func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *apiV1beta1.ObjectType, relation string, subject *apiV1beta1.SubjectReference, limit uint32, continuation biz.ContinuationToken) (chan *biz.ResourceResult, chan error, error) { +func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *apiV1beta1.ObjectType, relation string, subject *apiV1beta1.SubjectReference, limit uint32, continuation biz.ContinuationToken, zookie *apiV1beta1.Zookie) (chan *biz.ResourceResult, chan error, error) { if err := s.initialize(); err != nil { return nil, nil, err } @@ -204,7 +197,7 @@ func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *a } } client, err := s.client.LookupResources(ctx, &v1.LookupResourcesRequest{ - Consistency: s.consistency, + Consistency: s.determineConsistency(zookie), ResourceObjectType: kesselTypeToSpiceDBType(resouce_type), Permission: relation, Subject: &v1.SubjectReference{ @@ -248,6 +241,7 @@ func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *a Id: resId, }, Continuation: continuation, + Zookie: &apiV1beta1.Zookie{Token: msg.GetLookedUpAt().GetToken()}, } } }() @@ -297,9 +291,9 @@ func (s *SpiceDbRepository) ImportBulkTuples(stream grpc.ClientStreamingServer[a } -func (s *SpiceDbRepository) CreateRelationships(ctx context.Context, rels []*apiV1beta1.Relationship, touch biz.TouchSemantics) error { +func (s *SpiceDbRepository) CreateRelationships(ctx context.Context, rels []*apiV1beta1.Relationship, touch biz.TouchSemantics) (*apiV1beta1.CreateTuplesResponse, error) { if err := s.initialize(); err != nil { - return err + return nil, err } var relationshipUpdates []*v1.RelationshipUpdate @@ -322,17 +316,18 @@ func (s *SpiceDbRepository) CreateRelationships(ctx context.Context, rels []*api }) } - _, err := s.client.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{ + resp, err := s.client.WriteRelationships(ctx, &v1.WriteRelationshipsRequest{ Updates: relationshipUpdates, }) if err != nil { - return fmt.Errorf("error writing relationships to SpiceDB: %w", err) + return nil, fmt.Errorf("error writing relationships to SpiceDB: %w", err) } - return nil + + return &apiV1beta1.CreateTuplesResponse{CreatedAt: &apiV1beta1.Zookie{Token: resp.GetWrittenAt().GetToken()}}, nil } -func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1beta1.RelationTupleFilter, limit uint32, continuation biz.ContinuationToken) (chan *biz.RelationshipResult, chan error, error) { +func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1beta1.RelationTupleFilter, limit uint32, continuation biz.ContinuationToken, zookie *apiV1beta1.Zookie) (chan *biz.RelationshipResult, chan error, error) { if err := s.initialize(); err != nil { return nil, nil, err } @@ -358,7 +353,7 @@ func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1 } req := &v1.ReadRelationshipsRequest{ - Consistency: s.consistency, + Consistency: s.determineConsistency(zookie), RelationshipFilter: relationshipFilter, OptionalLimit: limit, OptionalCursor: cursor, @@ -407,6 +402,7 @@ func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1 }, }, Continuation: continuation, + Zookie: &apiV1beta1.Zookie{Token: msg.ReadAt.GetToken()}, } } }() @@ -414,9 +410,9 @@ func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1 return relationshipTuples, errs, nil } -func (s *SpiceDbRepository) DeleteRelationships(ctx context.Context, filter *apiV1beta1.RelationTupleFilter) error { +func (s *SpiceDbRepository) DeleteRelationships(ctx context.Context, filter *apiV1beta1.RelationTupleFilter) (*apiV1beta1.DeleteTuplesResponse, error) { if err := s.initialize(); err != nil { - return err + return nil, err } if filter.GetRelation() != "" { @@ -427,19 +423,19 @@ func (s *SpiceDbRepository) DeleteRelationships(ctx context.Context, filter *api relationshipFilter, err := createSpiceDbRelationshipFilter(filter) if err != nil { - return kerrors.BadRequest("SpiceDb request validation", err.Error()).WithCause(err) + return nil, kerrors.BadRequest("SpiceDb request validation", err.Error()).WithCause(err) } req := &v1.DeleteRelationshipsRequest{RelationshipFilter: relationshipFilter} - _, err = s.client.DeleteRelationships(ctx, req) + resp, err := s.client.DeleteRelationships(ctx, req) // TODO: we have not specified an option in our API to allow partial deletions, so currently it's all or nothing if err != nil { - return fmt.Errorf("error invoking DeleteRelationships in SpiceDB %w", err) + return nil, fmt.Errorf("error invoking DeleteRelationships in SpiceDB %w", err) } - return nil + return &apiV1beta1.DeleteTuplesResponse{DeletedAt: &apiV1beta1.Zookie{Token: resp.GetDeletedAt().GetToken()}}, nil } func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRequest) (*apiV1beta1.CheckResponse, error) { @@ -460,7 +456,7 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRe ObjectId: check.GetResource().GetId(), } req := &v1.CheckPermissionRequest{ - Consistency: s.consistency, + Consistency: s.determineConsistency(check.Zookie), Resource: resource, Permission: check.GetRelation(), Subject: subject, @@ -471,10 +467,16 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRe } if checkResponse.Permissionship == v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION { - return &apiV1beta1.CheckResponse{Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE}, nil + return &apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: &apiV1beta1.Zookie{Token: checkResponse.GetCheckedAt().GetToken()}, + }, nil } - return &apiV1beta1.CheckResponse{Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE}, nil + return &apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE, + CheckedAt: &apiV1beta1.Zookie{Token: checkResponse.GetCheckedAt().GetToken()}, + }, nil } func (s *SpiceDbRepository) IsBackendAvailable() error { @@ -621,3 +623,24 @@ func readFile(file string) (string, error) { return string(bytes), nil } + +func (s *SpiceDbRepository) determineConsistency(zookie *apiV1beta1.Zookie) *v1.Consistency { + if s.fullyConsistent { + // will ensure that all data used is fully consistent with the latest data available within the SpiceDB datastore. + return &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}} + } + + if zookie != nil { + return &v1.Consistency{ + Requirement: &v1.Consistency_AtLeastAsFresh{ + AtLeastAsFresh: &v1.ZedToken{Token: zookie.GetToken()}, + }, + } + } + // Default consistency for read APIs is minimize_latency + return &v1.Consistency{ + Requirement: &v1.Consistency_MinimizeLatency{ + MinimizeLatency: true, + }, + } +} diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index dd051dc7..a194395e 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -61,7 +61,7 @@ func TestCreateRelationship(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -89,7 +89,7 @@ func TestCreateRelationshipWithSubjectRelation(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -141,10 +141,10 @@ func TestSecondCreateRelationshipFailsWithTouchFalse(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.NoError(t, err) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.Error(t, err) assert.Equal(t, codes.AlreadyExists, status.Convert(err).Code()) @@ -170,12 +170,12 @@ func TestSecondCreateRelationshipSucceedsWithTouchTrue(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.NoError(t, err) touch = true - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -298,7 +298,7 @@ func TestDoesNotCreateRelationshipWithSlashInSubjectType(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.Error(t, err) } @@ -317,7 +317,7 @@ func TestDoesNotCreateRelationshipWithSlashInObjectType(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.Error(t, err) } @@ -336,7 +336,7 @@ func TestCreateRelationshipFailsWithBadSubjectType(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.Error(t, err) assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code()) assert.Contains(t, err.Error(), @@ -358,7 +358,7 @@ func TestCreateRelationshipFailsWithBadObjectType(t *testing.T) { touch := biz.TouchSemantics(false) - err = spiceDbRepo.CreateRelationships(ctx, rels, touch) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, touch) assert.Error(t, err) assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code()) assert.Contains(t, err.Error(), @@ -386,7 +386,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) assert.Error(t, err) @@ -399,7 +399,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) assert.Error(t, err) @@ -412,7 +412,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectId: pointerize("bob"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) assert.Error(t, err) @@ -425,7 +425,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectId: pointerize("bob"), SubjectNamespace: pointerize("rbac"), }, - }, 0, "") + }, 0, "", nil) assert.Error(t, err) @@ -439,7 +439,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) assert.NoError(t, err) @@ -451,7 +451,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) assert.NoError(t, err) @@ -463,7 +463,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectFilter: &apiV1beta1.SubjectFilter{ SubjectId: pointerize("bob"), }, - }, 0, "") + }, 0, "", nil) assert.NoError(t, err) @@ -473,7 +473,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectFilter: &apiV1beta1.SubjectFilter{ SubjectId: pointerize("bob"), }, - }, 0, "") + }, 0, "", nil) assert.NoError(t, err) @@ -484,7 +484,7 @@ func TestSupportedNsTypeTupleFilterCombinationsInReadRelationships(t *testing.T) SubjectFilter: &apiV1beta1.SubjectFilter{ SubjectId: pointerize("bob"), }, - }, 0, "") + }, 0, "", nil) assert.NoError(t, err) } @@ -503,7 +503,7 @@ func TestWriteAndReadBackRelationships(t *testing.T) { createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), } - err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) if !assert.NoError(t, err) { return } @@ -520,7 +520,7 @@ func TestWriteAndReadBackRelationships(t *testing.T) { SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) if !assert.NoError(t, err) { return @@ -544,7 +544,7 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), } - err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) if !assert.NoError(t, err) { return } @@ -561,7 +561,7 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) if !assert.NoError(t, err) { return @@ -570,7 +570,7 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { readrels := spiceRelChanToSlice(readRelChan) assert.Equal(t, 1, len(readrels)) - err = spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + _, err = spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ ResourceId: pointerize("bob_club"), ResourceNamespace: pointerize("rbac"), ResourceType: pointerize("group"), @@ -598,7 +598,7 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { SubjectNamespace: pointerize("rbac"), SubjectType: pointerize("principal"), }, - }, 0, "") + }, 0, "", nil) if !assert.NoError(t, err) { return @@ -609,6 +609,65 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { } +func TestSpiceDbRepository_CheckPermissionZookie(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + if !assert.NoError(t, err) { + return + } + + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + createRelationship("rbac", "workspace", "test", "user_grant", "rbac", "role_binding", "rb_test", ""), + createRelationship("rbac", "role_binding", "rb_test", "granted", "rbac", "role", "rl1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "bob", ""), + createRelationship("rbac", "role", "rl1", "view_widget", "rbac", "principal", "*", ""), + } + + relationshipResp, err := spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + if !assert.NoError(t, err) { + return + } + + subject := &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "bob", + }, + } + + resource := &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + } + // zookie received from creating relationships. + createdZookie := relationshipResp.GetCreatedAt() + // zed permission check rbac/workspace:test view_widget rbac/principal:bob --explain + check := apiV1beta1.CheckRequest{ + Subject: subject, + Relation: "view_widget", + Resource: resource, + Zookie: createdZookie, // pass createdAt zookie + } + resp, err := spiceDbRepo.Check(ctx, &check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse := apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: createdZookie, // same zookie as created zookie. + } + assert.Equal(t, &checkResponse, resp) + +} + func TestSpiceDbRepository_CheckPermission(t *testing.T) { t.Parallel() @@ -626,7 +685,7 @@ func TestSpiceDbRepository_CheckPermission(t *testing.T) { createRelationship("rbac", "role", "rl1", "view_widget", "rbac", "principal", "*", ""), } - err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + _, err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) if !assert.NoError(t, err) { return } @@ -659,13 +718,16 @@ func TestSpiceDbRepository_CheckPermission(t *testing.T) { return } //apiV1.CheckResponse_ALLOWED_TRUE + dummyZookie := "AAAAAAAAHHHHH" checkResponse := apiV1beta1.CheckResponse{ - Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: &apiV1beta1.Zookie{Token: dummyZookie}, } + resp.CheckedAt = &apiV1beta1.Zookie{Token: dummyZookie} assert.Equal(t, &checkResponse, resp) //Remove // rbac/role_binding:rb_test#t_subject@rbac/principal:bob - err = spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + _, err = spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ ResourceId: pointerize("rb_test"), ResourceNamespace: pointerize("rbac"), ResourceType: pointerize("role_binding"), @@ -691,9 +753,12 @@ func TestSpiceDbRepository_CheckPermission(t *testing.T) { if !assert.NoError(t, err) { return } + dummyZookie = "AAAAAAAAHHHHH" checkResponsev2 := apiV1beta1.CheckResponse{ - Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE, + Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE, + CheckedAt: &apiV1beta1.Zookie{Token: dummyZookie}, } + resp2.CheckedAt = &apiV1beta1.Zookie{Token: dummyZookie} assert.Equal(t, &checkResponsev2, resp2) } @@ -727,9 +792,12 @@ func runSpiceDBCheck(t *testing.T, ctx context.Context, spiceDbRepo *SpiceDbRepo resp, err := spiceDbRepo.Check(ctx, &check) assert.NoError(t, err) + dummyZookie := "AAAAAAAAHHHHH" expectedResponse := apiV1beta1.CheckResponse{ - Allowed: expectedAllowed, + Allowed: expectedAllowed, + CheckedAt: &apiV1beta1.Zookie{Token: dummyZookie}, } + resp.CheckedAt = &apiV1beta1.Zookie{Token: dummyZookie} assert.Equal(t, &expectedResponse, resp) } diff --git a/internal/service/lookup.go b/internal/service/lookup.go index ced3089d..6562d57b 100644 --- a/internal/service/lookup.go +++ b/internal/service/lookup.go @@ -37,6 +37,7 @@ func (s *LookupService) LookupSubjects(req *pb.LookupSubjectsRequest, conn pb.Ke err = conn.Send(&pb.LookupSubjectsResponse{ Subject: sub.Subject, Pagination: &pb.ResponsePagination{ContinuationToken: string(sub.Continuation)}, + LookedUpAt: sub.Zookie, }) if err != nil { return fmt.Errorf("error sending retrieved subject to the client: %w", err) @@ -63,6 +64,7 @@ func (s *LookupService) LookupResources(req *pb.LookupResourcesRequest, conn pb. err = conn.Send(&pb.LookupResourcesResponse{ Resource: re.Resource, Pagination: &pb.ResponsePagination{ContinuationToken: string(re.Continuation)}, + LookedUpAt: re.Zookie, }) if err != nil { return fmt.Errorf("error sending retrieved resource to the client: %w", err) diff --git a/internal/service/lookup_test.go b/internal/service/lookup_test.go index 5fcf1ec1..27b0ece4 100644 --- a/internal/service/lookup_test.go +++ b/internal/service/lookup_test.go @@ -21,7 +21,7 @@ func TestLookupService_LookupSubjects_NoResults(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -45,7 +45,7 @@ func TestLookupService_LookupResources_NoResults(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -72,9 +72,9 @@ func TestLookupService_LookupSubjects_OneResult(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) - err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") + _, err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -98,7 +98,7 @@ func TestLookupService_LookupResources_OneResult(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -125,9 +125,9 @@ func TestLookupService_LookupResources_TwoResults(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) - err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") + _, err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -155,11 +155,11 @@ func TestLookupService_LookupSubjects_TwoResults(t *testing.T) { spicedb, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") assert.NoError(t, err) - err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") + _, err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") assert.NoError(t, err) - err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u2") + _, err = seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u2") assert.NoError(t, err) container.WaitForQuantizationInterval() @@ -184,7 +184,7 @@ func TestLookupService_LookupResources_IgnoresSubjectRelation(t *testing.T) { assert.NoError(t, err) memberRelation := "member" - err = spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ + _, err = spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ { Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("role_binding"), Id: "rb1"}, Relation: "subject", @@ -193,7 +193,7 @@ func TestLookupService_LookupResources_IgnoresSubjectRelation(t *testing.T) { }, biz.TouchSemantics(true)) assert.NoError(t, err) - err = spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ + _, err = spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ { Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("group"), Id: "g1"}, Relation: "member", @@ -244,7 +244,7 @@ func createLookupService(spicedb *data.SpiceDbRepository) *LookupService { ) return NewLookupService(logger, biz.NewGetSubjectsUseCase(spicedb), biz.NewGetResourcesUseCase(spicedb)) } -func seedWidgetInDefaultWorkspace(ctx context.Context, spicedb *data.SpiceDbRepository, thing string) error { +func seedWidgetInDefaultWorkspace(ctx context.Context, spicedb *data.SpiceDbRepository, thing string) (*v1beta1.CreateTuplesResponse, error) { return spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ { Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("widget"), Id: thing}, @@ -254,7 +254,7 @@ func seedWidgetInDefaultWorkspace(ctx context.Context, spicedb *data.SpiceDbRepo }, biz.TouchSemantics(true)) } -func seedUserWithViewThingInDefaultWorkspace(ctx context.Context, spicedb *data.SpiceDbRepository, user string) error { +func seedUserWithViewThingInDefaultWorkspace(ctx context.Context, spicedb *data.SpiceDbRepository, user string) (*v1beta1.CreateTuplesResponse, error) { return spicedb.CreateRelationships(ctx, []*v1beta1.Relationship{ { Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("role"), Id: "viewers"}, diff --git a/internal/service/relationships.go b/internal/service/relationships.go index 1a736d25..33983a65 100644 --- a/internal/service/relationships.go +++ b/internal/service/relationships.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "google.golang.org/grpc" "github.com/project-kessel/relations-api/internal/biz" @@ -32,12 +33,12 @@ func NewRelationshipsService(logger log.Logger, createUseCase *biz.CreateRelatio } func (s *RelationshipsService) CreateTuples(ctx context.Context, req *pb.CreateTuplesRequest) (*pb.CreateTuplesResponse, error) { - err := s.createUsecase.CreateRelationships(ctx, req.Tuples, req.GetUpsert()) //The generated .GetUpsert() defaults to false + resp, err := s.createUsecase.CreateRelationships(ctx, req.Tuples, req.GetUpsert()) //The generated .GetUpsert() defaults to false if err != nil { return nil, fmt.Errorf("error creating tuples: %w", err) } - return &pb.CreateTuplesResponse{}, nil + return &pb.CreateTuplesResponse{CreatedAt: resp.GetCreatedAt()}, nil } func (s *RelationshipsService) ReadTuples(req *pb.ReadTuplesRequest, conn pb.KesselTupleService_ReadTuplesServer) error { @@ -53,6 +54,7 @@ func (s *RelationshipsService) ReadTuples(req *pb.ReadTuplesRequest, conn pb.Kes err = conn.Send(&pb.ReadTuplesResponse{ Tuple: rel.Relationship, Pagination: &pb.ResponsePagination{ContinuationToken: string(rel.Continuation)}, + ReadAt: rel.Zookie, }) if err != nil { return fmt.Errorf("error sending retrieved tuple to the client: %w", err) @@ -68,12 +70,12 @@ func (s *RelationshipsService) ReadTuples(req *pb.ReadTuplesRequest, conn pb.Kes } func (s *RelationshipsService) DeleteTuples(ctx context.Context, req *pb.DeleteTuplesRequest) (*pb.DeleteTuplesResponse, error) { - err := s.deleteUsecase.DeleteRelationships(ctx, req.Filter) + resp, err := s.deleteUsecase.DeleteRelationships(ctx, req.Filter) if err != nil { return nil, fmt.Errorf("error deleting tuples: %w", err) } - return &pb.DeleteTuplesResponse{}, nil + return &pb.DeleteTuplesResponse{DeletedAt: resp.GetDeletedAt()}, nil } func (s *RelationshipsService) ImportBulkTuples(stream grpc.ClientStreamingServer[pb.ImportBulkTuplesRequest, pb.ImportBulkTuplesResponse]) error { From aebe3ee1bb583775ba87937f9ad8aa329fd73918 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Wed, 29 Jan 2025 11:10:10 -0500 Subject: [PATCH 04/11] Update test to use same zookie... Signed-off-by: Jonathan Marcantonio --- internal/data/spicedb_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index a194395e..c6be4da7 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -646,14 +646,13 @@ func TestSpiceDbRepository_CheckPermissionZookie(t *testing.T) { }, Id: "test", } - // zookie received from creating relationships. - createdZookie := relationshipResp.GetCreatedAt() + // no wait, immediately read after write. // zed permission check rbac/workspace:test view_widget rbac/principal:bob --explain check := apiV1beta1.CheckRequest{ Subject: subject, Relation: "view_widget", Resource: resource, - Zookie: createdZookie, // pass createdAt zookie + Zookie: relationshipResp.GetCreatedAt(), // pass createdAt zookie } resp, err := spiceDbRepo.Check(ctx, &check) if !assert.NoError(t, err) { @@ -662,7 +661,7 @@ func TestSpiceDbRepository_CheckPermissionZookie(t *testing.T) { //apiV1.CheckResponse_ALLOWED_TRUE checkResponse := apiV1beta1.CheckResponse{ Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, - CheckedAt: createdZookie, // same zookie as created zookie. + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. } assert.Equal(t, &checkResponse, resp) From df5121138d54a17473c293f1ecb8640b82fdf1d7 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Wed, 29 Jan 2025 14:38:08 -0500 Subject: [PATCH 05/11] Add basic test cases that ensure zookie's handle read after write for requests Signed-off-by: Jonathan Marcantonio --- internal/data/LocalSpiceDbContainer.go | 4 +- internal/data/spicedb_test.go | 120 +++++++++++++++++++-- internal/service/lookup_test.go | 143 +++++++++++++++++++++++++ internal/service/relationships_test.go | 102 ++++++++++++++++++ 4 files changed, 356 insertions(+), 13 deletions(-) diff --git a/internal/data/LocalSpiceDbContainer.go b/internal/data/LocalSpiceDbContainer.go index 5f94fa68..571b99c8 100644 --- a/internal/data/LocalSpiceDbContainer.go +++ b/internal/data/LocalSpiceDbContainer.go @@ -190,7 +190,7 @@ func (l *LocalSpiceDbContainer) Close() { } // CheckForRelationship returns true if the given subject has the given relationship to the given resource, otherwise false -func CheckForRelationship(client biz.ZanzibarRepository, subjectID string, subjectNamespace string, subjectType string, subjectRelationship string, relationship string, resourceNamespace string, resourceType string, resourceID string) bool { +func CheckForRelationship(client biz.ZanzibarRepository, subjectID string, subjectNamespace string, subjectType string, subjectRelationship string, relationship string, resourceNamespace string, resourceType string, resourceID string, zookie *v1beta1.Zookie) bool { ctx := context.TODO() var subjectRelationRef *string = nil //Relation is optional @@ -209,7 +209,7 @@ func CheckForRelationship(client biz.ZanzibarRepository, subjectID string, subje SubjectId: &subjectID, Relation: subjectRelationRef, }, - }, 1, biz.ContinuationToken(""), nil) + }, 1, biz.ContinuationToken(""), zookie) if err != nil { panic(err) diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index c6be4da7..a44d0703 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -52,7 +52,7 @@ func TestCreateRelationship(t *testing.T) { spiceDbRepo, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.False(t, preExisting) rels := []*apiV1beta1.Relationship{ @@ -66,7 +66,30 @@ func TestCreateRelationship(t *testing.T) { container.WaitForQuantizationInterval() - exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) + assert.True(t, exists) +} + +func TestCreateRelationshipWithZookie(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) + assert.False(t, preExisting) + + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + } + + touch := biz.TouchSemantics(false) + + resp, err := spiceDbRepo.CreateRelationships(ctx, rels, touch) + assert.NoError(t, err) + + exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", resp.GetCreatedAt()) assert.True(t, exists) } @@ -77,7 +100,7 @@ func TestCreateRelationshipWithSubjectRelation(t *testing.T) { spiceDbRepo, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.False(t, preExisting) rels := []*apiV1beta1.Relationship{ @@ -94,10 +117,10 @@ func TestCreateRelationshipWithSubjectRelation(t *testing.T) { container.WaitForQuantizationInterval() - exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.True(t, exists) - exists = CheckForRelationship(spiceDbRepo, "bob_club", "rbac", "group", "member", "subject", "rbac", "role_binding", "fan_binding") + exists = CheckForRelationship(spiceDbRepo, "bob_club", "rbac", "group", "member", "subject", "rbac", "role_binding", "fan_binding", nil) assert.True(t, exists) // zed permission check rbac/role_binding:fan_binding subject rbac/principal:bob @@ -132,7 +155,7 @@ func TestSecondCreateRelationshipFailsWithTouchFalse(t *testing.T) { spiceDbRepo, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.False(t, preExisting) rels := []*apiV1beta1.Relationship{ @@ -150,7 +173,7 @@ func TestSecondCreateRelationshipFailsWithTouchFalse(t *testing.T) { container.WaitForQuantizationInterval() - exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.True(t, exists) } @@ -161,7 +184,7 @@ func TestSecondCreateRelationshipSucceedsWithTouchTrue(t *testing.T) { spiceDbRepo, err := container.CreateSpiceDbRepository() assert.NoError(t, err) - preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + preExisting := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.False(t, preExisting) rels := []*apiV1beta1.Relationship{ @@ -180,7 +203,7 @@ func TestSecondCreateRelationshipSucceedsWithTouchTrue(t *testing.T) { container.WaitForQuantizationInterval() - exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + exists := CheckForRelationship(spiceDbRepo, "bob", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.True(t, exists) } @@ -254,7 +277,7 @@ func TestImportBulkTuples(t *testing.T) { assert.NoError(t, err) container.WaitForQuantizationInterval() - exists := CheckForRelationship(spiceDbRepo, "bob5", "rbac", "principal", "", "member", "rbac", "group", "bob_club") + exists := CheckForRelationship(spiceDbRepo, "bob5", "rbac", "principal", "", "member", "rbac", "group", "bob_club", nil) assert.True(t, exists) } @@ -609,7 +632,82 @@ func TestWriteReadBackDeleteAndReadBackRelationships(t *testing.T) { } -func TestSpiceDbRepository_CheckPermissionZookie(t *testing.T) { +func TestWriteReadBackDeleteAndReadBackRelationships_WithZookie(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + if !assert.NoError(t, err) { + return + } + + assert.NoError(t, err) + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + } + + respCreate, err := spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + if !assert.NoError(t, err) { + return + } + + readRelChan, _, err := spiceDbRepo.ReadRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &apiV1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }, 0, "", respCreate.GetCreatedAt()) + + if !assert.NoError(t, err) { + return + } + + readrels := spiceRelChanToSlice(readRelChan) + assert.Equal(t, 1, len(readrels)) + + respDelete, err := spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &apiV1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }) + + if !assert.NoError(t, err) { + return + } + + readRelChan, _, err = spiceDbRepo.ReadRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &apiV1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }, 0, "", respDelete.GetDeletedAt()) + + if !assert.NoError(t, err) { + return + } + + readrels = spiceRelChanToSlice(readRelChan) + assert.Equal(t, 0, len(readrels)) + +} + +func TestSpiceDbRepository_CheckPermission_WithZookie(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/internal/service/lookup_test.go b/internal/service/lookup_test.go index 27b0ece4..5a7ed09f 100644 --- a/internal/service/lookup_test.go +++ b/internal/service/lookup_test.go @@ -39,6 +39,30 @@ func TestLookupService_LookupSubjects_NoResults(t *testing.T) { assert.Empty(t, results) } +func TestLookupService_LookupSubjects_NoResults_WithZookie(t *testing.T) { + t.Parallel() + ctx := context.TODO() + spicedb, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + resp, err := seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + assert.NoError(t, err) + + service := createLookupService(spicedb) + + responseCollector := NewLookup_SubjectsServerStub(ctx) + err = service.LookupSubjects(&v1beta1.LookupSubjectsRequest{ + SubjectType: rbac_ns_type("principal"), + Relation: "view", + Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("widget"), Id: "thing1"}, + Zookie: resp.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + results := responseCollector.GetResponses() + + assert.Empty(t, results) +} + func TestLookupService_LookupResources_NoResults(t *testing.T) { t.Parallel() ctx := context.TODO() @@ -66,6 +90,33 @@ func TestLookupService_LookupResources_NoResults(t *testing.T) { assert.Empty(t, results) } +func TestLookupService_LookupResources_NoResults_WithZookie(t *testing.T) { + t.Parallel() + ctx := context.TODO() + spicedb, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + resp, err := seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + assert.NoError(t, err) + + service := createLookupService(spicedb) + + responseCollector := NewLookup_ResourcesServerStub(ctx) + err = service.LookupResources(&v1beta1.LookupResourcesRequest{ + Subject: &v1beta1.SubjectReference{Subject: &v1beta1.ObjectReference{Type: rbac_ns_type("workspace"), Id: "default"}}, + Relation: "view_widget", + ResourceType: &v1beta1.ObjectType{ + Name: "workspace", + Namespace: "rbac", + }, + Zookie: resp.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + results := responseCollector.GetResponses() + + assert.Empty(t, results) +} + func TestLookupService_LookupSubjects_OneResult(t *testing.T) { t.Parallel() ctx := context.TODO() @@ -92,6 +143,32 @@ func TestLookupService_LookupSubjects_OneResult(t *testing.T) { assert.ElementsMatch(t, []string{"u1"}, ids) } +func TestLookupService_LookupSubjects_OneResult_WithZookie(t *testing.T) { + t.Parallel() + ctx := context.TODO() + spicedb, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + _, err = seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + assert.NoError(t, err) + resp, err := seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") + assert.NoError(t, err) + + service := createLookupService(spicedb) + + responseCollector := NewLookup_SubjectsServerStub(ctx) + err = service.LookupSubjects(&v1beta1.LookupSubjectsRequest{ + SubjectType: rbac_ns_type("principal"), + Relation: "view", + Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("widget"), Id: "thing1"}, + Zookie: resp.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + ids := responseCollector.GetIDs() + + assert.ElementsMatch(t, []string{"u1"}, ids) +} + func TestLookupService_LookupResources_OneResult(t *testing.T) { t.Parallel() ctx := context.TODO() @@ -118,6 +195,32 @@ func TestLookupService_LookupResources_OneResult(t *testing.T) { assert.ElementsMatch(t, []string{"thing1"}, ids) } +func TestLookupService_LookupResources_OneResult_WithZookie(t *testing.T) { + t.Parallel() + ctx := context.TODO() + spicedb, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + resp, err := seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + assert.NoError(t, err) + + service := createLookupService(spicedb) + + responseCollector := NewLookup_ResourcesServerStub(ctx) + err = service.LookupResources(&v1beta1.LookupResourcesRequest{ + Subject: &v1beta1.SubjectReference{Subject: &v1beta1.ObjectReference{Type: rbac_ns_type("workspace"), Id: "default"}}, + Relation: "workspace", + ResourceType: &v1beta1.ObjectType{ + Name: "widget", + Namespace: "rbac", + }, + Zookie: resp.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + ids := responseCollector.GetIDs() + + assert.ElementsMatch(t, []string{"thing1"}, ids) +} func TestLookupService_LookupResources_TwoResults(t *testing.T) { t.Parallel() @@ -177,6 +280,46 @@ func TestLookupService_LookupSubjects_TwoResults(t *testing.T) { assert.ElementsMatch(t, []string{"u1", "u2"}, ids) } +func TestLookupService_LookupSubjectsMissingItems_WithWrongZookie(t *testing.T) { + t.Parallel() + ctx := context.TODO() + spicedb, err := container.CreateSpiceDbRepository() + assert.NoError(t, err) + + resp1, err := seedWidgetInDefaultWorkspace(ctx, spicedb, "thing1") + assert.NoError(t, err) + resp2, err := seedUserWithViewThingInDefaultWorkspace(ctx, spicedb, "u1") + assert.NoError(t, err) + + service := createLookupService(spicedb) + + // using first zookie resp1 we expect missing ids + responseCollector := NewLookup_SubjectsServerStub(ctx) + err = service.LookupSubjects(&v1beta1.LookupSubjectsRequest{ + SubjectType: rbac_ns_type("principal"), + Relation: "view", + Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("widget"), Id: "thing1"}, + Zookie: resp1.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + ids := responseCollector.GetIDs() + + assert.ElementsMatch(t, []string{}, ids) + + // using latest zookie resp2 we expect all ids! + responseCollector = NewLookup_SubjectsServerStub(ctx) + err = service.LookupSubjects(&v1beta1.LookupSubjectsRequest{ + SubjectType: rbac_ns_type("principal"), + Relation: "view", + Resource: &v1beta1.ObjectReference{Type: rbac_ns_type("widget"), Id: "thing1"}, + Zookie: resp2.GetCreatedAt(), + }, responseCollector) + assert.NoError(t, err) + ids = responseCollector.GetIDs() + + assert.ElementsMatch(t, []string{"u1"}, ids) +} + func TestLookupService_LookupResources_IgnoresSubjectRelation(t *testing.T) { t.Parallel() ctx := context.TODO() diff --git a/internal/service/relationships_test.go b/internal/service/relationships_test.go index 3a096aca..a392e55d 100644 --- a/internal/service/relationships_test.go +++ b/internal/service/relationships_test.go @@ -92,6 +92,53 @@ func TestRelationshipsService_CreateRelationships(t *testing.T) { } +func TestRelationshipsService_CreateRelationships_WithZookie(t *testing.T) { + t.Parallel() + err, relationshipsService := setup(t) + assert.NoError(t, err) + ctx := context.Background() + expected := createRelationship(rbac_ns_type("group"), "bob_club", "member", rbac_ns_type("principal"), "bob", "") + + req := &v1beta1.CreateTuplesRequest{ + Tuples: []*v1beta1.Relationship{ + expected, + }, + } + reqCopy := proto.Clone(req) + resp, err := relationshipsService.CreateTuples(ctx, reqCopy.(*v1beta1.CreateTuplesRequest)) + assert.NoError(t, err) + + readReq := &v1beta1.ReadTuplesRequest{Filter: &v1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &v1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }, + Zookie: resp.GetCreatedAt(), + } + collectingServer := NewRelationships_ReadRelationshipsServerStub(ctx) + err = relationshipsService.ReadTuples(readReq, collectingServer) + if err != nil { + t.FailNow() + } + responseRelationships := collectingServer.responses + + for _, resp := range responseRelationships { + assert.Equal(t, expected.Resource.Id, resp.Tuple.Resource.Id) + assert.Equal(t, expected.Resource.Type.Namespace, resp.Tuple.Resource.Type.Namespace) + assert.Equal(t, expected.Resource.Type.Name, resp.Tuple.Resource.Type.Name) + assert.Equal(t, expected.Subject.Subject.Id, resp.Tuple.Subject.Subject.Id) + assert.Equal(t, expected.Subject.Subject.Type.Namespace, resp.Tuple.Subject.Subject.Type.Namespace) + assert.Equal(t, expected.Subject.Subject.Type.Name, resp.Tuple.Subject.Subject.Type.Name) + assert.Equal(t, expected.Relation, resp.Tuple.Relation) + } +} + func TestRelationshipsService_CreateRelationshipsWithTouchFalse(t *testing.T) { t.Parallel() err, relationshipsService := setup(t) @@ -254,6 +301,61 @@ func TestRelationshipsService_DeleteRelationships(t *testing.T) { assert.NoError(t, err) } +func TestRelationshipsService_DeleteRelationships_WithZookie(t *testing.T) { + t.Parallel() + err, relationshipsService := setup(t) + assert.NoError(t, err) + + expected := createRelationship(rbac_ns_type("group"), "bob_club", "member", rbac_ns_type("principal"), "bob", "") + + ctx := context.Background() + req := &v1beta1.CreateTuplesRequest{ + Tuples: []*v1beta1.Relationship{ + expected, + }, + } + _, err = relationshipsService.CreateTuples(ctx, req) + assert.NoError(t, err) + + delreq := &v1beta1.DeleteTuplesRequest{Filter: &v1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &v1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }} + resp, err := relationshipsService.DeleteTuples(ctx, delreq) + assert.NoError(t, err) + + readReq := &v1beta1.ReadTuplesRequest{Filter: &v1beta1.RelationTupleFilter{ + ResourceId: pointerize("bob_club"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("group"), + Relation: pointerize("member"), + SubjectFilter: &v1beta1.SubjectFilter{ + SubjectId: pointerize("bob"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }, + Zookie: resp.GetDeletedAt(), + } + + collectingServer := NewRelationships_ReadRelationshipsServerStub(ctx) + err = relationshipsService.ReadTuples(readReq, collectingServer) + if err != nil { + t.FailNow() + } + responses := collectingServer.responses + + assert.Equal(t, 0, len(responses)) + assert.NoError(t, err) +} + func setup(t *testing.T) (error, *RelationshipsService) { logger := log.With(log.NewStdLogger(os.Stdout), "ts", log.DefaultTimestamp, From 01e911a03911eb5b842f6e74592211484987d4de Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Thu, 30 Jan 2025 09:30:02 -0500 Subject: [PATCH 06/11] Keep ephemeral env fullyConsistent Signed-off-by: Jonathan Marcantonio --- deploy/kessel-relations.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kessel-relations.yaml b/deploy/kessel-relations.yaml index 7d7fb1f6..c0743af4 100644 --- a/deploy/kessel-relations.yaml +++ b/deploy/kessel-relations.yaml @@ -34,7 +34,7 @@ objects: token: "${PRESHARED}" # token takes precedence over tokenFile tokenFile: "${PRESHARED_FILE:.secrets/local-spicedb-secret}" schemaFile: "${SCHEMA_FILE:deploy/schema.zed}" - fullyConsistent: false + fullyConsistent: true - apiVersion: v1 kind: Secret metadata: From 42154329738c9860e9b515477572f5a20df07c01 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Thu, 30 Jan 2025 14:43:24 -0500 Subject: [PATCH 07/11] Add a new enemy problem-like test Signed-off-by: Jonathan Marcantonio --- internal/data/spicedb_test.go | 240 ++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index a44d0703..f5e97b18 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -859,6 +859,246 @@ func TestSpiceDbRepository_CheckPermission(t *testing.T) { assert.Equal(t, &checkResponsev2, resp2) } +func TestSpiceDbRepository_NewEnemyProblem_Success(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + if !assert.NoError(t, err) { + return + } + + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + createRelationship("rbac", "workspace", "test", "user_grant", "rbac", "role_binding", "rb_test", ""), + createRelationship("rbac", "role_binding", "rb_test", "granted", "rbac", "role", "rl1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "u1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "u2", ""), + createRelationship("rbac", "role", "rl1", "view_widget", "rbac", "principal", "*", ""), + } + + relationshipResp, err := spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + if !assert.NoError(t, err) { + return + } + + // u1 + u1Check := apiV1beta1.CheckRequest{ + Subject: &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "u1", + }, + }, + Relation: "view_widget", + Resource: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + }, + Zookie: relationshipResp.GetCreatedAt(), // pass createdAt zookie + } + + // no wait, immediately read after write. + // zed permission check rbac/workspace:test user_grant rbac/principal:u1 --explain + resp, err := spiceDbRepo.Check(ctx, &u1Check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse := apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) + + // u2 + u2Check := apiV1beta1.CheckRequest{ + Subject: &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "u2", + }, + }, + Relation: "view_widget", + Resource: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + }, + Zookie: relationshipResp.GetCreatedAt(), // pass createdAt zookie + } + + // zed permission check rbac/workspace:test user_grant rbac/principal:u2 --explain + resp, err = spiceDbRepo.Check(ctx, &u2Check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse = apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) + + // remove access from u1, keep access for u2. + respDelete, err := spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + ResourceId: pointerize("rb_test"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("role_binding"), + Relation: pointerize("subject"), + SubjectFilter: &apiV1beta1.SubjectFilter{ + SubjectId: pointerize("u1"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }) + if !assert.NoError(t, err) { + return + } + + // ensure u1 no longer has access, while u2 still does. + + // zed permission check rbac/workspace:test user_grant rbac/principal:u1 --explain + u1Check.Zookie = respDelete.GetDeletedAt() + resp, err = spiceDbRepo.Check(ctx, &u1Check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_FALSE + checkResponse = apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE, + CheckedAt: respDelete.GetDeletedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) + + // zed permission check rbac/workspace:test user_grant rbac/principal:u2 --explain + u2Check.Zookie = respDelete.GetDeletedAt() + resp, err = spiceDbRepo.Check(ctx, &u2Check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse = apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: respDelete.GetDeletedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) +} + +func TestSpiceDbRepository_NewEnemyProblem_Failure(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + if !assert.NoError(t, err) { + return + } + + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + createRelationship("rbac", "workspace", "test", "user_grant", "rbac", "role_binding", "rb_test", ""), + createRelationship("rbac", "role_binding", "rb_test", "granted", "rbac", "role", "rl1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "u1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "u2", ""), + createRelationship("rbac", "role", "rl1", "view_widget", "rbac", "principal", "*", ""), + } + + relationshipResp, err := spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + if !assert.NoError(t, err) { + return + } + + // u1 + u1Check := apiV1beta1.CheckRequest{ + Subject: &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "u1", + }, + }, + Relation: "view_widget", + Resource: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + }, + Zookie: relationshipResp.GetCreatedAt(), // pass createdAt zookie + } + + // u2 + u2Check := apiV1beta1.CheckRequest{ + Subject: &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "u2", + }, + }, + Relation: "view_widget", + Resource: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + }, + Zookie: relationshipResp.GetCreatedAt(), // pass createdAt zookie + } + + // remove access from u1, keep access for u2. + _, err = spiceDbRepo.DeleteRelationships(ctx, &apiV1beta1.RelationTupleFilter{ + ResourceId: pointerize("rb_test"), + ResourceNamespace: pointerize("rbac"), + ResourceType: pointerize("role_binding"), + Relation: pointerize("subject"), + SubjectFilter: &apiV1beta1.SubjectFilter{ + SubjectId: pointerize("u1"), + SubjectNamespace: pointerize("rbac"), + SubjectType: pointerize("principal"), + }, + }) + if !assert.NoError(t, err) { + return + } + + // u1 has access even though we removed access. u2 still has access. + + // zed permission check rbac/workspace:test user_grant rbac/principal:u1 --explain + resp, err := spiceDbRepo.Check(ctx, &u1Check) // we're passing a zookie revision before deletion occurred. + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse := apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. + } + // we technically dont have access, but according to zookie revision we do! + assert.Equal(t, &checkResponse, resp) // we expect true even with removed access. + + // zed permission check rbac/workspace:test user_grant rbac/principal:u2 --explain + resp, err = spiceDbRepo.Check(ctx, &u2Check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse = apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) +} + func pointerize(value string) *string { //Used to turn string literals into pointers return &value } From 1c6b17e118b56a49ba70b329774aa69abc385245 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Thu, 30 Jan 2025 14:54:36 -0500 Subject: [PATCH 08/11] Compare returned zookie. Signed-off-by: Jonathan Marcantonio --- internal/data/spicedb_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index f5e97b18..71225913 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -974,7 +974,7 @@ func TestSpiceDbRepository_NewEnemyProblem_Success(t *testing.T) { //apiV1.CheckResponse_ALLOWED_FALSE checkResponse = apiV1beta1.CheckResponse{ Allowed: apiV1beta1.CheckResponse_ALLOWED_FALSE, - CheckedAt: respDelete.GetDeletedAt(), // returned zookie may not be same as created zookie. + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. } assert.Equal(t, &checkResponse, resp) @@ -987,7 +987,7 @@ func TestSpiceDbRepository_NewEnemyProblem_Success(t *testing.T) { //apiV1.CheckResponse_ALLOWED_TRUE checkResponse = apiV1beta1.CheckResponse{ Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, - CheckedAt: respDelete.GetDeletedAt(), // returned zookie may not be same as created zookie. + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. } assert.Equal(t, &checkResponse, resp) } From e9c61354718855a3297be1d0bf46f31d301c0402 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Thu, 30 Jan 2025 15:30:22 -0500 Subject: [PATCH 09/11] Check for full consistency Signed-off-by: Jonathan Marcantonio --- internal/data/spicedb_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index 71225913..4a25aa91 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -1083,8 +1083,13 @@ func TestSpiceDbRepository_NewEnemyProblem_Failure(t *testing.T) { Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. } - // we technically dont have access, but according to zookie revision we do! - assert.Equal(t, &checkResponse, resp) // we expect true even with removed access. + + if spiceDbRepo.fullyConsistent { // new enemy problem doesn't apply if we're fully consistent. + checkResponse.Allowed = apiV1beta1.CheckResponse_ALLOWED_FALSE + assert.Equal(t, &checkResponse, resp) + } else { // we technically dont have access, but according to zookie revision we do! + assert.Equal(t, &checkResponse, resp) // we expect true even with removed access. + } // zed permission check rbac/workspace:test user_grant rbac/principal:u2 --explain resp, err = spiceDbRepo.Check(ctx, &u2Check) From 21a9c9e713a7de3c673836862503724a5c5b9746 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Fri, 31 Jan 2025 11:20:17 -0500 Subject: [PATCH 10/11] Full consistency field for check requests Signed-off-by: Jonathan Marcantonio --- api/kessel/relations/v1beta1/check.pb.go | 91 +++++++++++++----------- api/kessel/relations/v1beta1/check.proto | 1 + internal/data/spicedb.go | 12 ++-- internal/data/spicedb_test.go | 58 +++++++++++++++ openapi.yaml | 2 + 5 files changed, 118 insertions(+), 46 deletions(-) diff --git a/api/kessel/relations/v1beta1/check.pb.go b/api/kessel/relations/v1beta1/check.pb.go index 1b6d4db2..5c9f5f1e 100644 --- a/api/kessel/relations/v1beta1/check.pb.go +++ b/api/kessel/relations/v1beta1/check.pb.go @@ -73,13 +73,14 @@ func (CheckResponse_Allowed) EnumDescriptor() ([]byte, []int) { } type CheckRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` - Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` - Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` - Zookie *Zookie `protobuf:"bytes,4,opt,name=zookie,proto3" json:"zookie,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` + Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + Zookie *Zookie `protobuf:"bytes,4,opt,name=zookie,proto3" json:"zookie,omitempty"` + FullyConsistent bool `protobuf:"varint,5,opt,name=fully_consistent,json=fullyConsistent,proto3" json:"fully_consistent,omitempty"` // defaults to false + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CheckRequest) Reset() { @@ -140,6 +141,13 @@ func (x *CheckRequest) GetZookie() *Zookie { return nil } +func (x *CheckRequest) GetFullyConsistent() bool { + if x != nil { + return x.FullyConsistent + } + return false +} + type CheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Allowed CheckResponse_Allowed `protobuf:"varint,1,opt,name=allowed,proto3,enum=kessel.relations.v1beta1.CheckResponse_Allowed" json:"allowed,omitempty"` @@ -205,7 +213,7 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x8a, 0x02, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x74, 0x6f, 0x22, 0xb5, 0x02, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, @@ -221,38 +229,41 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, - 0xe4, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, - 0x69, 0x65, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x22, 0x47, 0x0a, - 0x07, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, - 0x57, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x52, 0x55, - 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x46, - 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x32, 0x89, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, - 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, - 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, - 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, - 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, - 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, - 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x79, + 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x0d, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, + 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x07, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x09, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x22, 0x47, 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, + 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x11, + 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, + 0x02, 0x32, 0x89, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6b, 0x65, 0x73, 0x73, + 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x72, 0x0a, + 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, + 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( diff --git a/api/kessel/relations/v1beta1/check.proto b/api/kessel/relations/v1beta1/check.proto index 0dd343a5..6c9d41e1 100644 --- a/api/kessel/relations/v1beta1/check.proto +++ b/api/kessel/relations/v1beta1/check.proto @@ -27,6 +27,7 @@ message CheckRequest { string relation = 2 [(buf.validate.field).string.min_len = 1]; SubjectReference subject = 3 [(buf.validate.field).required = true]; Zookie zookie = 4; + bool fully_consistent = 5; // defaults to false } message CheckResponse { diff --git a/internal/data/spicedb.go b/internal/data/spicedb.go index 815973fd..9cb7af0c 100644 --- a/internal/data/spicedb.go +++ b/internal/data/spicedb.go @@ -129,7 +129,7 @@ func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *ap } req := &v1.LookupSubjectsRequest{ - Consistency: s.determineConsistency(zookie), + Consistency: s.determineConsistency(zookie, false), Resource: &v1.ObjectReference{ ObjectType: kesselTypeToSpiceDBType(object.Type), ObjectId: object.Id, @@ -197,7 +197,7 @@ func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *a } } client, err := s.client.LookupResources(ctx, &v1.LookupResourcesRequest{ - Consistency: s.determineConsistency(zookie), + Consistency: s.determineConsistency(zookie, false), ResourceObjectType: kesselTypeToSpiceDBType(resouce_type), Permission: relation, Subject: &v1.SubjectReference{ @@ -353,7 +353,7 @@ func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1 } req := &v1.ReadRelationshipsRequest{ - Consistency: s.determineConsistency(zookie), + Consistency: s.determineConsistency(zookie, false), RelationshipFilter: relationshipFilter, OptionalLimit: limit, OptionalCursor: cursor, @@ -456,7 +456,7 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRe ObjectId: check.GetResource().GetId(), } req := &v1.CheckPermissionRequest{ - Consistency: s.determineConsistency(check.Zookie), + Consistency: s.determineConsistency(check.Zookie, check.GetFullyConsistent()), Resource: resource, Permission: check.GetRelation(), Subject: subject, @@ -624,8 +624,8 @@ func readFile(file string) (string, error) { return string(bytes), nil } -func (s *SpiceDbRepository) determineConsistency(zookie *apiV1beta1.Zookie) *v1.Consistency { - if s.fullyConsistent { +func (s *SpiceDbRepository) determineConsistency(zookie *apiV1beta1.Zookie, fullyConsistent bool) *v1.Consistency { + if s.fullyConsistent || fullyConsistent { // will ensure that all data used is fully consistent with the latest data available within the SpiceDB datastore. return &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}} } diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index 4a25aa91..8a17e3aa 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -765,6 +765,64 @@ func TestSpiceDbRepository_CheckPermission_WithZookie(t *testing.T) { } +func TestSpiceDbRepository_CheckPermission_FullyConsistent(t *testing.T) { + t.Parallel() + + ctx := context.Background() + spiceDbRepo, err := container.CreateSpiceDbRepository() + if !assert.NoError(t, err) { + return + } + + rels := []*apiV1beta1.Relationship{ + createRelationship("rbac", "group", "bob_club", "member", "rbac", "principal", "bob", ""), + createRelationship("rbac", "workspace", "test", "user_grant", "rbac", "role_binding", "rb_test", ""), + createRelationship("rbac", "role_binding", "rb_test", "granted", "rbac", "role", "rl1", ""), + createRelationship("rbac", "role_binding", "rb_test", "subject", "rbac", "principal", "bob", ""), + createRelationship("rbac", "role", "rl1", "view_widget", "rbac", "principal", "*", ""), + } + + _, err = spiceDbRepo.CreateRelationships(ctx, rels, biz.TouchSemantics(true)) + if !assert.NoError(t, err) { + return + } + + subject := &apiV1beta1.SubjectReference{ + Subject: &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "principal", Namespace: "rbac", + }, + Id: "bob", + }, + } + + resource := &apiV1beta1.ObjectReference{ + Type: &apiV1beta1.ObjectType{ + Name: "workspace", Namespace: "rbac", + }, + Id: "test", + } + // no wait, immediately read after write. + // zed permission check rbac/workspace:test view_widget rbac/principal:bob --explain + check := apiV1beta1.CheckRequest{ + Subject: subject, + Relation: "view_widget", + Resource: resource, + FullyConsistent: true, // no need to pass zookie + } + resp, err := spiceDbRepo.Check(ctx, &check) + if !assert.NoError(t, err) { + return + } + //apiV1.CheckResponse_ALLOWED_TRUE + checkResponse := apiV1beta1.CheckResponse{ + Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. + } + assert.Equal(t, &checkResponse, resp) + +} + func TestSpiceDbRepository_CheckPermission(t *testing.T) { t.Parallel() diff --git a/openapi.yaml b/openapi.yaml index 0bd6dcc3..e73a4334 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -326,6 +326,8 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.SubjectReference' zookie: $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' + fullyConsistent: + type: boolean kessel.relations.v1beta1.CheckResponse: type: object properties: From 31e1718694ed28d80bbddbc2e3fe078638cecdb7 Mon Sep 17 00:00:00 2001 From: Jonathan Marcantonio Date: Fri, 31 Jan 2025 13:35:59 -0500 Subject: [PATCH 11/11] Implement a CheckForUpdate rpc for full consistency checks Signed-off-by: Jonathan Marcantonio --- api/kessel/relations/v1beta1/check.pb.go | 342 ++++++++++++++---- api/kessel/relations/v1beta1/check.proto | 24 +- api/kessel/relations/v1beta1/check_grpc.pb.go | 40 +- api/kessel/relations/v1beta1/check_http.pb.go | 39 ++ cmd/kessel-relations/wire_gen.go | 3 +- internal/biz/biz.go | 2 +- internal/biz/relationships.go | 14 + internal/data/spicedb.go | 53 ++- internal/data/spicedb_test.go | 19 +- internal/service/check.go | 20 +- openapi.yaml | 37 +- 11 files changed, 494 insertions(+), 99 deletions(-) diff --git a/api/kessel/relations/v1beta1/check.pb.go b/api/kessel/relations/v1beta1/check.pb.go index 5c9f5f1e..bcd52f3e 100644 --- a/api/kessel/relations/v1beta1/check.pb.go +++ b/api/kessel/relations/v1beta1/check.pb.go @@ -72,15 +72,63 @@ func (CheckResponse_Allowed) EnumDescriptor() ([]byte, []int) { return file_kessel_relations_v1beta1_check_proto_rawDescGZIP(), []int{1, 0} } +type CheckForUpdateResponse_Allowed int32 + +const ( + CheckForUpdateResponse_ALLOWED_UNSPECIFIED CheckForUpdateResponse_Allowed = 0 + CheckForUpdateResponse_ALLOWED_TRUE CheckForUpdateResponse_Allowed = 1 + CheckForUpdateResponse_ALLOWED_FALSE CheckForUpdateResponse_Allowed = 2 // e.g. ALLOWED_CONDITIONAL = 3; +) + +// Enum value maps for CheckForUpdateResponse_Allowed. +var ( + CheckForUpdateResponse_Allowed_name = map[int32]string{ + 0: "ALLOWED_UNSPECIFIED", + 1: "ALLOWED_TRUE", + 2: "ALLOWED_FALSE", + } + CheckForUpdateResponse_Allowed_value = map[string]int32{ + "ALLOWED_UNSPECIFIED": 0, + "ALLOWED_TRUE": 1, + "ALLOWED_FALSE": 2, + } +) + +func (x CheckForUpdateResponse_Allowed) Enum() *CheckForUpdateResponse_Allowed { + p := new(CheckForUpdateResponse_Allowed) + *p = x + return p +} + +func (x CheckForUpdateResponse_Allowed) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CheckForUpdateResponse_Allowed) Descriptor() protoreflect.EnumDescriptor { + return file_kessel_relations_v1beta1_check_proto_enumTypes[1].Descriptor() +} + +func (CheckForUpdateResponse_Allowed) Type() protoreflect.EnumType { + return &file_kessel_relations_v1beta1_check_proto_enumTypes[1] +} + +func (x CheckForUpdateResponse_Allowed) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CheckForUpdateResponse_Allowed.Descriptor instead. +func (CheckForUpdateResponse_Allowed) EnumDescriptor() ([]byte, []int) { + return file_kessel_relations_v1beta1_check_proto_rawDescGZIP(), []int{3, 0} +} + type CheckRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` - Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` - Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` - Zookie *Zookie `protobuf:"bytes,4,opt,name=zookie,proto3" json:"zookie,omitempty"` - FullyConsistent bool `protobuf:"varint,5,opt,name=fully_consistent,json=fullyConsistent,proto3" json:"fully_consistent,omitempty"` // defaults to false - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` + Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + Zookie *Zookie `protobuf:"bytes,4,opt,name=zookie,proto3" json:"zookie,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CheckRequest) Reset() { @@ -141,13 +189,6 @@ func (x *CheckRequest) GetZookie() *Zookie { return nil } -func (x *CheckRequest) GetFullyConsistent() bool { - if x != nil { - return x.FullyConsistent - } - return false -} - type CheckResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Allowed CheckResponse_Allowed `protobuf:"varint,1,opt,name=allowed,proto3,enum=kessel.relations.v1beta1.CheckResponse_Allowed" json:"allowed,omitempty"` @@ -200,6 +241,118 @@ func (x *CheckResponse) GetCheckedAt() *Zookie { return nil } +type CheckForUpdateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Resource *ObjectReference `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + Relation string `protobuf:"bytes,2,opt,name=relation,proto3" json:"relation,omitempty"` + Subject *SubjectReference `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckForUpdateRequest) Reset() { + *x = CheckForUpdateRequest{} + mi := &file_kessel_relations_v1beta1_check_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckForUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckForUpdateRequest) ProtoMessage() {} + +func (x *CheckForUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_kessel_relations_v1beta1_check_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckForUpdateRequest.ProtoReflect.Descriptor instead. +func (*CheckForUpdateRequest) Descriptor() ([]byte, []int) { + return file_kessel_relations_v1beta1_check_proto_rawDescGZIP(), []int{2} +} + +func (x *CheckForUpdateRequest) GetResource() *ObjectReference { + if x != nil { + return x.Resource + } + return nil +} + +func (x *CheckForUpdateRequest) GetRelation() string { + if x != nil { + return x.Relation + } + return "" +} + +func (x *CheckForUpdateRequest) GetSubject() *SubjectReference { + if x != nil { + return x.Subject + } + return nil +} + +type CheckForUpdateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Allowed CheckForUpdateResponse_Allowed `protobuf:"varint,1,opt,name=allowed,proto3,enum=kessel.relations.v1beta1.CheckForUpdateResponse_Allowed" json:"allowed,omitempty"` + CheckedAt *Zookie `protobuf:"bytes,2,opt,name=checked_at,json=checkedAt,proto3" json:"checked_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckForUpdateResponse) Reset() { + *x = CheckForUpdateResponse{} + mi := &file_kessel_relations_v1beta1_check_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckForUpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckForUpdateResponse) ProtoMessage() {} + +func (x *CheckForUpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_kessel_relations_v1beta1_check_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckForUpdateResponse.ProtoReflect.Descriptor instead. +func (*CheckForUpdateResponse) Descriptor() ([]byte, []int) { + return file_kessel_relations_v1beta1_check_proto_rawDescGZIP(), []int{3} +} + +func (x *CheckForUpdateResponse) GetAllowed() CheckForUpdateResponse_Allowed { + if x != nil { + return x.Allowed + } + return CheckForUpdateResponse_ALLOWED_UNSPECIFIED +} + +func (x *CheckForUpdateResponse) GetCheckedAt() *Zookie { + if x != nil { + return x.CheckedAt + } + return nil +} + var File_kessel_relations_v1beta1_check_proto protoreflect.FileDescriptor var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ @@ -213,7 +366,7 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xb5, 0x02, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x74, 0x6f, 0x22, 0x8a, 0x02, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, @@ -229,41 +382,77 @@ var file_kessel_relations_v1beta1_check_proto_rawDesc = string([]byte{ 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, - 0x29, 0x0a, 0x10, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x79, - 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x0d, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x07, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, - 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x07, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, + 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x06, 0x7a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x22, + 0xe4, 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x49, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0a, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, + 0x69, 0x65, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x22, 0x47, 0x0a, + 0x07, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, + 0x57, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x52, 0x55, + 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x46, + 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x22, 0xd9, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x46, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x4d, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x23, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x22, 0xf6, 0x01, 0x0a, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, + 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, + 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x46, + 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, + 0x41, 0x74, 0x22, 0x47, 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, + 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, + 0x44, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, + 0x57, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, 0x02, 0x32, 0xa3, 0x02, 0x0a, 0x12, + 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x73, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x09, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x22, 0x47, 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, - 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x52, 0x55, 0x45, 0x10, 0x01, 0x12, 0x11, - 0x0a, 0x0d, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x10, - 0x02, 0x32, 0x89, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x12, 0x26, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6b, 0x65, 0x73, 0x73, - 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x72, 0x0a, - 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x65, 0x73, - 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2d, - 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x97, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x65, + 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x66, 0x6f, 0x72, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x42, 0x72, 0x0a, 0x28, 0x6f, 0x72, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x50, 0x01, 0x5a, + 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2d, 0x6b, 0x65, 0x73, 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x65, 0x73, + 0x73, 0x65, 0x6c, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -278,29 +467,38 @@ func file_kessel_relations_v1beta1_check_proto_rawDescGZIP() []byte { return file_kessel_relations_v1beta1_check_proto_rawDescData } -var file_kessel_relations_v1beta1_check_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_kessel_relations_v1beta1_check_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_kessel_relations_v1beta1_check_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_kessel_relations_v1beta1_check_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_kessel_relations_v1beta1_check_proto_goTypes = []any{ - (CheckResponse_Allowed)(0), // 0: kessel.relations.v1beta1.CheckResponse.Allowed - (*CheckRequest)(nil), // 1: kessel.relations.v1beta1.CheckRequest - (*CheckResponse)(nil), // 2: kessel.relations.v1beta1.CheckResponse - (*ObjectReference)(nil), // 3: kessel.relations.v1beta1.ObjectReference - (*SubjectReference)(nil), // 4: kessel.relations.v1beta1.SubjectReference - (*Zookie)(nil), // 5: kessel.relations.v1beta1.Zookie + (CheckResponse_Allowed)(0), // 0: kessel.relations.v1beta1.CheckResponse.Allowed + (CheckForUpdateResponse_Allowed)(0), // 1: kessel.relations.v1beta1.CheckForUpdateResponse.Allowed + (*CheckRequest)(nil), // 2: kessel.relations.v1beta1.CheckRequest + (*CheckResponse)(nil), // 3: kessel.relations.v1beta1.CheckResponse + (*CheckForUpdateRequest)(nil), // 4: kessel.relations.v1beta1.CheckForUpdateRequest + (*CheckForUpdateResponse)(nil), // 5: kessel.relations.v1beta1.CheckForUpdateResponse + (*ObjectReference)(nil), // 6: kessel.relations.v1beta1.ObjectReference + (*SubjectReference)(nil), // 7: kessel.relations.v1beta1.SubjectReference + (*Zookie)(nil), // 8: kessel.relations.v1beta1.Zookie } var file_kessel_relations_v1beta1_check_proto_depIdxs = []int32{ - 3, // 0: kessel.relations.v1beta1.CheckRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference - 4, // 1: kessel.relations.v1beta1.CheckRequest.subject:type_name -> kessel.relations.v1beta1.SubjectReference - 5, // 2: kessel.relations.v1beta1.CheckRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie - 0, // 3: kessel.relations.v1beta1.CheckResponse.allowed:type_name -> kessel.relations.v1beta1.CheckResponse.Allowed - 5, // 4: kessel.relations.v1beta1.CheckResponse.checked_at:type_name -> kessel.relations.v1beta1.Zookie - 1, // 5: kessel.relations.v1beta1.KesselCheckService.Check:input_type -> kessel.relations.v1beta1.CheckRequest - 2, // 6: kessel.relations.v1beta1.KesselCheckService.Check:output_type -> kessel.relations.v1beta1.CheckResponse - 6, // [6:7] is the sub-list for method output_type - 5, // [5:6] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 6, // 0: kessel.relations.v1beta1.CheckRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference + 7, // 1: kessel.relations.v1beta1.CheckRequest.subject:type_name -> kessel.relations.v1beta1.SubjectReference + 8, // 2: kessel.relations.v1beta1.CheckRequest.zookie:type_name -> kessel.relations.v1beta1.Zookie + 0, // 3: kessel.relations.v1beta1.CheckResponse.allowed:type_name -> kessel.relations.v1beta1.CheckResponse.Allowed + 8, // 4: kessel.relations.v1beta1.CheckResponse.checked_at:type_name -> kessel.relations.v1beta1.Zookie + 6, // 5: kessel.relations.v1beta1.CheckForUpdateRequest.resource:type_name -> kessel.relations.v1beta1.ObjectReference + 7, // 6: kessel.relations.v1beta1.CheckForUpdateRequest.subject:type_name -> kessel.relations.v1beta1.SubjectReference + 1, // 7: kessel.relations.v1beta1.CheckForUpdateResponse.allowed:type_name -> kessel.relations.v1beta1.CheckForUpdateResponse.Allowed + 8, // 8: kessel.relations.v1beta1.CheckForUpdateResponse.checked_at:type_name -> kessel.relations.v1beta1.Zookie + 2, // 9: kessel.relations.v1beta1.KesselCheckService.Check:input_type -> kessel.relations.v1beta1.CheckRequest + 4, // 10: kessel.relations.v1beta1.KesselCheckService.CheckForUpdate:input_type -> kessel.relations.v1beta1.CheckForUpdateRequest + 3, // 11: kessel.relations.v1beta1.KesselCheckService.Check:output_type -> kessel.relations.v1beta1.CheckResponse + 5, // 12: kessel.relations.v1beta1.KesselCheckService.CheckForUpdate:output_type -> kessel.relations.v1beta1.CheckForUpdateResponse + 11, // [11:13] is the sub-list for method output_type + 9, // [9:11] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_kessel_relations_v1beta1_check_proto_init() } @@ -314,8 +512,8 @@ func file_kessel_relations_v1beta1_check_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_kessel_relations_v1beta1_check_proto_rawDesc), len(file_kessel_relations_v1beta1_check_proto_rawDesc)), - NumEnums: 1, - NumMessages: 2, + NumEnums: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/api/kessel/relations/v1beta1/check.proto b/api/kessel/relations/v1beta1/check.proto index 6c9d41e1..b5caaf9b 100644 --- a/api/kessel/relations/v1beta1/check.proto +++ b/api/kessel/relations/v1beta1/check.proto @@ -20,6 +20,13 @@ service KesselCheckService { body: "*" }; }; + + rpc CheckForUpdate (CheckForUpdateRequest) returns (CheckForUpdateResponse) { + option (google.api.http) = { + post: "/v1beta1/checkforupdate" + body: "*" + }; + }; } message CheckRequest { @@ -27,7 +34,6 @@ message CheckRequest { string relation = 2 [(buf.validate.field).string.min_len = 1]; SubjectReference subject = 3 [(buf.validate.field).required = true]; Zookie zookie = 4; - bool fully_consistent = 5; // defaults to false } message CheckResponse { @@ -41,3 +47,19 @@ message CheckResponse { Zookie checked_at = 2; } +message CheckForUpdateRequest { // fully consistent + ObjectReference resource = 1 [(buf.validate.field).required = true]; + string relation = 2 [(buf.validate.field).string.min_len = 1]; + SubjectReference subject = 3 [(buf.validate.field).required = true]; +} + +message CheckForUpdateResponse { + enum Allowed { + ALLOWED_UNSPECIFIED = 0; + ALLOWED_TRUE = 1; + ALLOWED_FALSE = 2; + // e.g. ALLOWED_CONDITIONAL = 3; + } + Allowed allowed = 1; + Zookie checked_at = 2; +} \ No newline at end of file diff --git a/api/kessel/relations/v1beta1/check_grpc.pb.go b/api/kessel/relations/v1beta1/check_grpc.pb.go index f65ab59c..55206c60 100644 --- a/api/kessel/relations/v1beta1/check_grpc.pb.go +++ b/api/kessel/relations/v1beta1/check_grpc.pb.go @@ -19,7 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - KesselCheckService_Check_FullMethodName = "/kessel.relations.v1beta1.KesselCheckService/Check" + KesselCheckService_Check_FullMethodName = "/kessel.relations.v1beta1.KesselCheckService/Check" + KesselCheckService_CheckForUpdate_FullMethodName = "/kessel.relations.v1beta1.KesselCheckService/CheckForUpdate" ) // KesselCheckServiceClient is the client API for KesselCheckService service. @@ -29,6 +30,7 @@ type KesselCheckServiceClient interface { // Checks for the existence of a single Relationship // (a Relation between a Resource and a Subject or Subject Set). Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*CheckResponse, error) + CheckForUpdate(ctx context.Context, in *CheckForUpdateRequest, opts ...grpc.CallOption) (*CheckForUpdateResponse, error) } type kesselCheckServiceClient struct { @@ -49,6 +51,16 @@ func (c *kesselCheckServiceClient) Check(ctx context.Context, in *CheckRequest, return out, nil } +func (c *kesselCheckServiceClient) CheckForUpdate(ctx context.Context, in *CheckForUpdateRequest, opts ...grpc.CallOption) (*CheckForUpdateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CheckForUpdateResponse) + err := c.cc.Invoke(ctx, KesselCheckService_CheckForUpdate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // KesselCheckServiceServer is the server API for KesselCheckService service. // All implementations must embed UnimplementedKesselCheckServiceServer // for forward compatibility. @@ -56,6 +68,7 @@ type KesselCheckServiceServer interface { // Checks for the existence of a single Relationship // (a Relation between a Resource and a Subject or Subject Set). Check(context.Context, *CheckRequest) (*CheckResponse, error) + CheckForUpdate(context.Context, *CheckForUpdateRequest) (*CheckForUpdateResponse, error) mustEmbedUnimplementedKesselCheckServiceServer() } @@ -69,6 +82,9 @@ type UnimplementedKesselCheckServiceServer struct{} func (UnimplementedKesselCheckServiceServer) Check(context.Context, *CheckRequest) (*CheckResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Check not implemented") } +func (UnimplementedKesselCheckServiceServer) CheckForUpdate(context.Context, *CheckForUpdateRequest) (*CheckForUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CheckForUpdate not implemented") +} func (UnimplementedKesselCheckServiceServer) mustEmbedUnimplementedKesselCheckServiceServer() {} func (UnimplementedKesselCheckServiceServer) testEmbeddedByValue() {} @@ -108,6 +124,24 @@ func _KesselCheckService_Check_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _KesselCheckService_CheckForUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckForUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KesselCheckServiceServer).CheckForUpdate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: KesselCheckService_CheckForUpdate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KesselCheckServiceServer).CheckForUpdate(ctx, req.(*CheckForUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // KesselCheckService_ServiceDesc is the grpc.ServiceDesc for KesselCheckService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -119,6 +153,10 @@ var KesselCheckService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Check", Handler: _KesselCheckService_Check_Handler, }, + { + MethodName: "CheckForUpdate", + Handler: _KesselCheckService_CheckForUpdate_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "kessel/relations/v1beta1/check.proto", diff --git a/api/kessel/relations/v1beta1/check_http.pb.go b/api/kessel/relations/v1beta1/check_http.pb.go index 94c81fec..63886827 100644 --- a/api/kessel/relations/v1beta1/check_http.pb.go +++ b/api/kessel/relations/v1beta1/check_http.pb.go @@ -20,16 +20,19 @@ var _ = binding.EncodeURL const _ = http.SupportPackageIsVersion1 const OperationKesselCheckServiceCheck = "/kessel.relations.v1beta1.KesselCheckService/Check" +const OperationKesselCheckServiceCheckForUpdate = "/kessel.relations.v1beta1.KesselCheckService/CheckForUpdate" type KesselCheckServiceHTTPServer interface { // Check Checks for the existence of a single Relationship // (a Relation between a Resource and a Subject or Subject Set). Check(context.Context, *CheckRequest) (*CheckResponse, error) + CheckForUpdate(context.Context, *CheckForUpdateRequest) (*CheckForUpdateResponse, error) } func RegisterKesselCheckServiceHTTPServer(s *http.Server, srv KesselCheckServiceHTTPServer) { r := s.Route("/") r.POST("/v1beta1/check", _KesselCheckService_Check0_HTTP_Handler(srv)) + r.POST("/v1beta1/checkforupdate", _KesselCheckService_CheckForUpdate0_HTTP_Handler(srv)) } func _KesselCheckService_Check0_HTTP_Handler(srv KesselCheckServiceHTTPServer) func(ctx http.Context) error { @@ -54,8 +57,31 @@ func _KesselCheckService_Check0_HTTP_Handler(srv KesselCheckServiceHTTPServer) f } } +func _KesselCheckService_CheckForUpdate0_HTTP_Handler(srv KesselCheckServiceHTTPServer) func(ctx http.Context) error { + return func(ctx http.Context) error { + var in CheckForUpdateRequest + if err := ctx.Bind(&in); err != nil { + return err + } + if err := ctx.BindQuery(&in); err != nil { + return err + } + http.SetOperation(ctx, OperationKesselCheckServiceCheckForUpdate) + h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.CheckForUpdate(ctx, req.(*CheckForUpdateRequest)) + }) + out, err := h(ctx, &in) + if err != nil { + return err + } + reply := out.(*CheckForUpdateResponse) + return ctx.Result(200, reply) + } +} + type KesselCheckServiceHTTPClient interface { Check(ctx context.Context, req *CheckRequest, opts ...http.CallOption) (rsp *CheckResponse, err error) + CheckForUpdate(ctx context.Context, req *CheckForUpdateRequest, opts ...http.CallOption) (rsp *CheckForUpdateResponse, err error) } type KesselCheckServiceHTTPClientImpl struct { @@ -78,3 +104,16 @@ func (c *KesselCheckServiceHTTPClientImpl) Check(ctx context.Context, in *CheckR } return &out, nil } + +func (c *KesselCheckServiceHTTPClientImpl) CheckForUpdate(ctx context.Context, in *CheckForUpdateRequest, opts ...http.CallOption) (*CheckForUpdateResponse, error) { + var out CheckForUpdateResponse + pattern := "/v1beta1/checkforupdate" + path := binding.EncodeURL(pattern, in, false) + opts = append(opts, http.Operation(OperationKesselCheckServiceCheckForUpdate)) + opts = append(opts, http.PathTemplate(pattern)) + err := c.cc.Invoke(ctx, "POST", path, in, &out, opts...) + if err != nil { + return nil, err + } + return &out, nil +} diff --git a/cmd/kessel-relations/wire_gen.go b/cmd/kessel-relations/wire_gen.go index e8b1d6c9..536c5914 100644 --- a/cmd/kessel-relations/wire_gen.go +++ b/cmd/kessel-relations/wire_gen.go @@ -36,7 +36,8 @@ func wireApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (* isBackendAvaliableUsecase := biz.NewIsBackendAvailableUsecase(spiceDbRepository) healthService := service.NewHealthService(isBackendAvaliableUsecase) checkUsecase := biz.NewCheckUsecase(spiceDbRepository, logger) - checkService := service.NewCheckService(logger, checkUsecase) + checkForUpdateUsecase := biz.NewCheckForUpdateUsecase(spiceDbRepository, logger) + checkService := service.NewCheckService(logger, checkUsecase, checkForUpdateUsecase) getSubjectsUsecase := biz.NewGetSubjectsUseCase(spiceDbRepository) getResourcesUsecase := biz.NewGetResourcesUseCase(spiceDbRepository) lookupService := service.NewLookupService(logger, getSubjectsUsecase, getResourcesUsecase) diff --git a/internal/biz/biz.go b/internal/biz/biz.go index dc413cde..14ba9c5a 100644 --- a/internal/biz/biz.go +++ b/internal/biz/biz.go @@ -5,4 +5,4 @@ import ( ) // ProviderSet is biz providers. -var ProviderSet = wire.NewSet(NewCreateRelationshipsUsecase, NewReadRelationshipsUsecase, NewDeleteRelationshipsUsecase, NewCheckUsecase, NewGetSubjectsUseCase, NewGetResourcesUseCase, NewIsBackendAvailableUsecase, NewImportBulkTuplesUsecase) +var ProviderSet = wire.NewSet(NewCreateRelationshipsUsecase, NewReadRelationshipsUsecase, NewDeleteRelationshipsUsecase, NewCheckUsecase, NewCheckForUpdateUsecase, NewGetSubjectsUseCase, NewGetResourcesUseCase, NewIsBackendAvailableUsecase, NewImportBulkTuplesUsecase) diff --git a/internal/biz/relationships.go b/internal/biz/relationships.go index 2e7c3bd6..59cdd030 100644 --- a/internal/biz/relationships.go +++ b/internal/biz/relationships.go @@ -33,6 +33,7 @@ type RelationshipResult struct { type ZanzibarRepository interface { Check(ctx context.Context, request *v1beta1.CheckRequest) (*v1beta1.CheckResponse, error) + CheckForUpdate(ctx context.Context, request *v1beta1.CheckForUpdateRequest) (*v1beta1.CheckForUpdateResponse, error) CreateRelationships(context.Context, []*v1beta1.Relationship, TouchSemantics) (*v1beta1.CreateTuplesResponse, error) ReadRelationships(ctx context.Context, filter *v1beta1.RelationTupleFilter, limit uint32, continuation ContinuationToken, zookie *v1beta1.Zookie) (chan *RelationshipResult, chan error, error) DeleteRelationships(context.Context, *v1beta1.RelationTupleFilter) (*v1beta1.DeleteTuplesResponse, error) @@ -55,6 +56,19 @@ func (rc *CheckUsecase) Check(ctx context.Context, check *v1beta1.CheckRequest) return rc.repo.Check(ctx, check) } +type CheckForUpdateUsecase struct { + repo ZanzibarRepository + log *log.Helper +} + +func NewCheckForUpdateUsecase(repo ZanzibarRepository, logger log.Logger) *CheckForUpdateUsecase { + return &CheckForUpdateUsecase{repo: repo, log: log.NewHelper(logger)} +} + +func (rc *CheckForUpdateUsecase) CheckForUpdate(ctx context.Context, check *v1beta1.CheckForUpdateRequest) (*v1beta1.CheckForUpdateResponse, error) { + return rc.repo.CheckForUpdate(ctx, check) +} + type CreateRelationshipsUsecase struct { repo ZanzibarRepository log *log.Helper diff --git a/internal/data/spicedb.go b/internal/data/spicedb.go index 9cb7af0c..030ad331 100644 --- a/internal/data/spicedb.go +++ b/internal/data/spicedb.go @@ -129,7 +129,7 @@ func (s *SpiceDbRepository) LookupSubjects(ctx context.Context, subject_type *ap } req := &v1.LookupSubjectsRequest{ - Consistency: s.determineConsistency(zookie, false), + Consistency: s.determineConsistency(zookie), Resource: &v1.ObjectReference{ ObjectType: kesselTypeToSpiceDBType(object.Type), ObjectId: object.Id, @@ -197,7 +197,7 @@ func (s *SpiceDbRepository) LookupResources(ctx context.Context, resouce_type *a } } client, err := s.client.LookupResources(ctx, &v1.LookupResourcesRequest{ - Consistency: s.determineConsistency(zookie, false), + Consistency: s.determineConsistency(zookie), ResourceObjectType: kesselTypeToSpiceDBType(resouce_type), Permission: relation, Subject: &v1.SubjectReference{ @@ -353,7 +353,7 @@ func (s *SpiceDbRepository) ReadRelationships(ctx context.Context, filter *apiV1 } req := &v1.ReadRelationshipsRequest{ - Consistency: s.determineConsistency(zookie, false), + Consistency: s.determineConsistency(zookie), RelationshipFilter: relationshipFilter, OptionalLimit: limit, OptionalCursor: cursor, @@ -456,7 +456,7 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRe ObjectId: check.GetResource().GetId(), } req := &v1.CheckPermissionRequest{ - Consistency: s.determineConsistency(check.Zookie, check.GetFullyConsistent()), + Consistency: s.determineConsistency(check.Zookie), Resource: resource, Permission: check.GetRelation(), Subject: subject, @@ -479,6 +479,47 @@ func (s *SpiceDbRepository) Check(ctx context.Context, check *apiV1beta1.CheckRe }, nil } +func (s *SpiceDbRepository) CheckForUpdate(ctx context.Context, check *apiV1beta1.CheckForUpdateRequest) (*apiV1beta1.CheckForUpdateResponse, error) { + if err := s.initialize(); err != nil { + return nil, err + } + + subject := &v1.SubjectReference{ + Object: &v1.ObjectReference{ + ObjectType: kesselTypeToSpiceDBType(check.GetSubject().GetSubject().Type), + ObjectId: check.GetSubject().GetSubject().GetId(), + }, + OptionalRelation: check.GetSubject().GetRelation(), + } + + resource := &v1.ObjectReference{ + ObjectType: kesselTypeToSpiceDBType(check.GetResource().GetType()), + ObjectId: check.GetResource().GetId(), + } + req := &v1.CheckPermissionRequest{ + Consistency: &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}}, + Resource: resource, + Permission: check.GetRelation(), + Subject: subject, + } + checkResponse, err := s.client.CheckPermission(ctx, req) + if err != nil { + return &apiV1beta1.CheckForUpdateResponse{Allowed: apiV1beta1.CheckForUpdateResponse_ALLOWED_UNSPECIFIED}, fmt.Errorf("error invoking CheckPermission in SpiceDB: %w", err) + } + + if checkResponse.Permissionship == v1.CheckPermissionResponse_PERMISSIONSHIP_HAS_PERMISSION { + return &apiV1beta1.CheckForUpdateResponse{ + Allowed: apiV1beta1.CheckForUpdateResponse_ALLOWED_TRUE, + CheckedAt: &apiV1beta1.Zookie{Token: checkResponse.GetCheckedAt().GetToken()}, + }, nil + } + + return &apiV1beta1.CheckForUpdateResponse{ + Allowed: apiV1beta1.CheckForUpdateResponse_ALLOWED_FALSE, + CheckedAt: &apiV1beta1.Zookie{Token: checkResponse.GetCheckedAt().GetToken()}, + }, nil +} + func (s *SpiceDbRepository) IsBackendAvailable() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -624,8 +665,8 @@ func readFile(file string) (string, error) { return string(bytes), nil } -func (s *SpiceDbRepository) determineConsistency(zookie *apiV1beta1.Zookie, fullyConsistent bool) *v1.Consistency { - if s.fullyConsistent || fullyConsistent { +func (s *SpiceDbRepository) determineConsistency(zookie *apiV1beta1.Zookie) *v1.Consistency { + if s.fullyConsistent { // will ensure that all data used is fully consistent with the latest data available within the SpiceDB datastore. return &v1.Consistency{Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}} } diff --git a/internal/data/spicedb_test.go b/internal/data/spicedb_test.go index 8a17e3aa..57766c31 100644 --- a/internal/data/spicedb_test.go +++ b/internal/data/spicedb_test.go @@ -765,7 +765,7 @@ func TestSpiceDbRepository_CheckPermission_WithZookie(t *testing.T) { } -func TestSpiceDbRepository_CheckPermission_FullyConsistent(t *testing.T) { +func TestSpiceDbRepository_CheckForUpdatePermission(t *testing.T) { t.Parallel() ctx := context.Background() @@ -804,19 +804,18 @@ func TestSpiceDbRepository_CheckPermission_FullyConsistent(t *testing.T) { } // no wait, immediately read after write. // zed permission check rbac/workspace:test view_widget rbac/principal:bob --explain - check := apiV1beta1.CheckRequest{ - Subject: subject, - Relation: "view_widget", - Resource: resource, - FullyConsistent: true, // no need to pass zookie + check := apiV1beta1.CheckForUpdateRequest{ + Subject: subject, + Relation: "view_widget", + Resource: resource, } - resp, err := spiceDbRepo.Check(ctx, &check) + resp, err := spiceDbRepo.CheckForUpdate(ctx, &check) if !assert.NoError(t, err) { return } - //apiV1.CheckResponse_ALLOWED_TRUE - checkResponse := apiV1beta1.CheckResponse{ - Allowed: apiV1beta1.CheckResponse_ALLOWED_TRUE, + //apiV1.CheckForUpdateResponse_ALLOWED_TRUE + checkResponse := apiV1beta1.CheckForUpdateResponse{ + Allowed: apiV1beta1.CheckForUpdateResponse_ALLOWED_TRUE, CheckedAt: resp.GetCheckedAt(), // returned zookie may not be same as created zookie. } assert.Equal(t, &checkResponse, resp) diff --git a/internal/service/check.go b/internal/service/check.go index 361530e9..a41bde18 100644 --- a/internal/service/check.go +++ b/internal/service/check.go @@ -13,14 +13,16 @@ import ( type CheckService struct { pb.UnimplementedKesselCheckServiceServer - check *biz.CheckUsecase - log *log.Helper + check *biz.CheckUsecase + checkForUpdate *biz.CheckForUpdateUsecase + log *log.Helper } -func NewCheckService(logger log.Logger, checkUseCase *biz.CheckUsecase) *CheckService { +func NewCheckService(logger log.Logger, checkUseCase *biz.CheckUsecase, checkForUpdateUseCase *biz.CheckForUpdateUsecase) *CheckService { return &CheckService{ - check: checkUseCase, - log: log.NewHelper(logger), + check: checkUseCase, + checkForUpdate: checkForUpdateUseCase, + log: log.NewHelper(logger), } } @@ -31,3 +33,11 @@ func (s *CheckService) Check(ctx context.Context, req *pb.CheckRequest) (*pb.Che } return resp, nil } + +func (s *CheckService) CheckForUpdate(ctx context.Context, req *pb.CheckForUpdateRequest) (*pb.CheckForUpdateResponse, error) { + resp, err := s.checkForUpdate.CheckForUpdate(ctx, req) + if err != nil { + return resp, fmt.Errorf("failed to perform checkForUpdate: %w", err) + } + return resp, nil +} diff --git a/openapi.yaml b/openapi.yaml index e73a4334..8cf57d08 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -49,6 +49,24 @@ paths: application/json: schema: $ref: '#/components/schemas/kessel.relations.v1beta1.CheckResponse' + /v1beta1/checkforupdate: + post: + tags: + - KesselCheckService + operationId: KesselCheckService_CheckForUpdate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/kessel.relations.v1beta1.CheckForUpdateRequest' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/kessel.relations.v1beta1.CheckForUpdateResponse' /v1beta1/resources: get: tags: @@ -315,6 +333,23 @@ components: code: type: integer format: uint32 + kessel.relations.v1beta1.CheckForUpdateRequest: + type: object + properties: + resource: + $ref: '#/components/schemas/kessel.relations.v1beta1.ObjectReference' + relation: + type: string + subject: + $ref: '#/components/schemas/kessel.relations.v1beta1.SubjectReference' + kessel.relations.v1beta1.CheckForUpdateResponse: + type: object + properties: + allowed: + type: integer + format: enum + checkedAt: + $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' kessel.relations.v1beta1.CheckRequest: type: object properties: @@ -326,8 +361,6 @@ components: $ref: '#/components/schemas/kessel.relations.v1beta1.SubjectReference' zookie: $ref: '#/components/schemas/kessel.relations.v1beta1.Zookie' - fullyConsistent: - type: boolean kessel.relations.v1beta1.CheckResponse: type: object properties: