diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 3f1c62411a..5f6ce072df 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -7059,6 +7059,16 @@

UpdateAttributeRequest

Updating the rule of an Attribute will retroactively alter access to existing TDFs of the Attribute name.

+ + values_order + string + repeated +

WARNING!! +Unsafe reordering requires the full list of values in the new order they should be stored. Updating the order of values in a HIERARCHY-rule Attribute Definition +will retroactively alter access to existing TDFs containing those values. Replacing values on an attribute in place is not supported; values can be unsafely deleted +deleted, created, and unsafely re-ordered as necessary.

+ + diff --git a/docs/openapi/policy/unsafe/unsafe.swagger.json b/docs/openapi/policy/unsafe/unsafe.swagger.json index a06e83132f..e9bec6b515 100644 --- a/docs/openapi/policy/unsafe/unsafe.swagger.json +++ b/docs/openapi/policy/unsafe/unsafe.swagger.json @@ -201,6 +201,17 @@ "ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY" ], "default": "ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED" + }, + { + "name": "valuesOrder", + "description": "WARNING!!\nUnsafe reordering requires the full list of values in the new order they should be stored. Updating the order of values in a HIERARCHY-rule Attribute Definition\nwill retroactively alter access to existing TDFs containing those values. Replacing values on an attribute in place is not supported; values can be unsafely deleted\ndeleted, created, and unsafely re-ordered as necessary.", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" } ], "tags": [ diff --git a/protocol/go/policy/unsafe/unsafe.pb.go b/protocol/go/policy/unsafe/unsafe.pb.go index 212083b27e..f87dcc9d77 100644 --- a/protocol/go/policy/unsafe/unsafe.pb.go +++ b/protocol/go/policy/unsafe/unsafe.pb.go @@ -350,6 +350,11 @@ type UpdateAttributeRequest struct { // WARNING!! // Updating the rule of an Attribute will retroactively alter access to existing TDFs of the Attribute name. Rule policy.AttributeRuleTypeEnum `protobuf:"varint,3,opt,name=rule,proto3,enum=policy.AttributeRuleTypeEnum" json:"rule,omitempty"` + // WARNING!! + // Unsafe reordering requires the full list of values in the new order they should be stored. Updating the order of values in a HIERARCHY-rule Attribute Definition + // will retroactively alter access to existing TDFs containing those values. Replacing values on an attribute in place is not supported; values can be unsafely deleted + // deleted, created, and unsafely re-ordered as necessary. + ValuesOrder []string `protobuf:"bytes,4,rep,name=values_order,json=valuesOrder,proto3" json:"values_order,omitempty"` } func (x *UpdateAttributeRequest) Reset() { @@ -405,6 +410,13 @@ func (x *UpdateAttributeRequest) GetRule() policy.AttributeRuleTypeEnum { return policy.AttributeRuleTypeEnum(0) } +func (x *UpdateAttributeRequest) GetValuesOrder() []string { + if x != nil { + return x.ValuesOrder + } + return nil +} + type UpdateAttributeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1021,7 +1033,7 @@ var file_policy_unsafe_unsafe_proto_rawDesc = []byte{ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x9c, 0x03, 0x0a, 0x16, 0x55, 0x70, 0x64, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xbf, 0x03, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0xac, 0x02, 0x0a, 0x04, @@ -1047,169 +1059,171 @@ var file_policy_unsafe_unsafe_proto_rawDesc = []byte{ 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x42, 0x08, 0xba, 0x48, 0x05, 0x82, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x22, 0x4a, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x22, 0x34, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4e, 0x0a, 0x1b, 0x52, 0x65, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x4a, 0x0a, 0x16, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x03, 0x66, - 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0x4a, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x22, 0xdf, 0x02, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0xa7, 0x02, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x90, 0x02, 0xba, 0x48, 0x8c, 0x02, - 0xba, 0x01, 0x83, 0x02, 0x0a, 0x0c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x12, 0xb5, 0x01, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, - 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, - 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, - 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, - 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, - 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, - 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, 0x68, 0x69, 0x73, - 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, - 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, - 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, - 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x39, 0x0a, 0x1f, 0x52, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x47, 0x0a, 0x20, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4f, 0x0a, 0x1b, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0x43, 0x0a, - 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x32, 0xba, 0x0a, 0x0a, 0x0d, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x32, - 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x98, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, - 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, - 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x22, - 0x22, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, - 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, + 0x01, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x4a, 0x0a, 0x17, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x34, 0x0a, 0x1a, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4e, 0x0a, 0x1b, + 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, 0x4a, 0x0a, 0x16, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, + 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, + 0xc8, 0x01, 0x01, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0x4a, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x22, 0xdf, 0x02, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0xa7, 0x02, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x90, 0x02, 0xba, + 0x48, 0x8c, 0x02, 0xba, 0x01, 0x83, 0x02, 0x0a, 0x0c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xb5, 0x01, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x20, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, + 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, + 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, + 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, + 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, + 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, + 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, + 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x39, 0x0a, 0x1f, 0x52, + 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, + 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x47, 0x0a, 0x20, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x4f, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, + 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x03, 0x66, 0x71, 0x6e, + 0x22, 0x43, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0xba, 0x0a, 0x0a, 0x0d, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, - 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x32, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x98, 0x01, 0x0a, 0x13, - 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, - 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x32, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x98, 0x01, 0x0a, 0x13, + 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, + 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, - 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x24, 0x22, 0x22, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x61, 0x63, + 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x24, 0x22, 0x22, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x2a, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x97, 0x01, 0x0a, 0x14, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, - 0x61, 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x32, 0x1e, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2f, - 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xae, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, + 0x19, 0x2a, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x25, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, + 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x32, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x98, + 0x01, 0x0a, 0x13, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x29, 0x2f, 0x75, 0x6e, 0x73, - 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2a, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x2a, - 0x1e, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x42, - 0xac, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, - 0x6e, 0x73, 0x61, 0x66, 0x65, 0x42, 0x0b, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0xa2, 0x02, 0x03, 0x50, 0x55, - 0x58, 0xaa, 0x02, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x55, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0xca, 0x02, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x55, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0xe2, 0x02, 0x19, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x55, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x22, 0x22, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, + 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x25, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x97, 0x01, + 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, + 0x66, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x32, 0x1e, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, + 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xae, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x22, 0x29, 0x2f, + 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x14, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, + 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x2a, 0x1e, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x2f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x42, 0xac, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x42, 0x0b, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, + 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, + 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0xa2, 0x02, + 0x03, 0x50, 0x55, 0x58, 0xaa, 0x02, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x55, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0xca, 0x02, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x55, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0xe2, 0x02, 0x19, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x55, 0x6e, + 0x73, 0x61, 0x66, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x55, 0x6e, 0x73, 0x61, 0x66, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/service/integration/attribute_fqns_test.go b/service/integration/attribute_fqns_test.go index e9f136065a..59e0045fd9 100644 --- a/service/integration/attribute_fqns_test.go +++ b/service/integration/attribute_fqns_test.go @@ -366,6 +366,57 @@ func (s *AttributeFqnSuite) TestGetAttributesByValueFqns_Fails_WithDeactivatedAt s.Require().ErrorIs(err, db.ErrNotFound) } +func (s *AttributeFqnSuite) TestGetAttributesByValueFqns_Fails_WithDeactivatedAttributeValue() { + // create a new namespace + ns, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test_fqn_namespace.goodbye", + }) + s.Require().NoError(err) + + // give it an attribute with two values + attr, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + NamespaceId: ns.GetId(), + Name: "deactivating_attr", + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + }) + s.Require().NoError(err) + + v1, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr.GetId(), &attributes.CreateAttributeValueRequest{ + Value: "value1", + }) + s.Require().NoError(err) + + v2, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr.GetId(), &attributes.CreateAttributeValueRequest{ + Value: "value2", + }) + s.Require().NoError(err) + + // deactivate the first attribute value only + _, err = s.db.PolicyClient.DeactivateAttributeValue(s.ctx, v1.GetId()) + s.Require().NoError(err) + + // get the attribute by the value fqn for v1 + v, err := s.db.PolicyClient.GetAttributesByValueFqns(s.ctx, &attributes.GetAttributeValuesByFqnsRequest{ + Fqns: []string{fqnBuilder(ns.GetName(), attr.GetName(), v1.GetValue())}, + WithValue: &policy.AttributeValueSelector{ + WithSubjectMaps: true, + }, + }) + s.Require().Error(err) + s.Nil(v) + s.Require().ErrorIs(err, db.ErrNotFound) + + // get the attribute by the value fqn for v2 + v, err = s.db.PolicyClient.GetAttributesByValueFqns(s.ctx, &attributes.GetAttributeValuesByFqnsRequest{ + Fqns: []string{fqnBuilder(ns.GetName(), attr.GetName(), v2.GetValue())}, + WithValue: &policy.AttributeValueSelector{ + WithSubjectMaps: true, + }, + }) + s.Require().NoError(err) + s.Len(v, 1) +} + func (s *AttributeFqnSuite) TestGetAttributesByValueFqns_Fails_WithNonValueFqns() { nsFqn := fqnBuilder("example.com", "", "") attrFqn := fqnBuilder("example.com", "attr1", "") diff --git a/service/integration/attributes_test.go b/service/integration/attributes_test.go index 2fa9ed928d..9165ca2f05 100644 --- a/service/integration/attributes_test.go +++ b/service/integration/attributes_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "slices" "strings" "testing" "time" @@ -12,6 +13,7 @@ import ( "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/protocol/go/policy/attributes" "github.com/opentdf/platform/protocol/go/policy/namespaces" + "github.com/opentdf/platform/protocol/go/policy/unsafe" "github.com/opentdf/platform/service/internal/fixtures" "github.com/opentdf/platform/service/pkg/db" policydb "github.com/opentdf/platform/service/policy/db" @@ -214,11 +216,6 @@ func (s *AttributesSuite) Test_CreateAttribute_WithInvalidRuleFails() { s.Nil(createdAttr) } -func (s *AttributesSuite) Test_UnsafeUpdateAttribute_ReplaceValuesOrder() { - // TODO: write test when unsafe behaviors are implemented [https://github.com/opentdf/platform/issues/115] - s.T().Skip("Unsafe service behaviors not yet implemented.") -} - func (s *AttributesSuite) Test_GetAttribute_OrderOfValuesIsPreserved() { values := []string{"first", "second", "third", "fourth"} attr := &attributes.CreateAttributeRequest{ @@ -490,72 +487,323 @@ func (s *AttributesSuite) Test_UpdateAttribute_WithInvalidIdFails() { s.Require().ErrorIs(err, db.ErrNotFound) } -func (s *AttributesSuite) Test_UpdateAttribute_NamespaceIsImmutableOnUpdate() { - s.T().Skip("Defunct test: not possible to test update in this way; check request struct for validation instead.") - original := &attributes.CreateAttributeRequest{ - Name: "test__update_attribute_namespace_immutable", - NamespaceId: s.f.GetNamespaceKey("example.com").ID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED, +func (s *AttributesSuite) Test_UnsafeUpdateAttribute_WithRuleAndNameAndReordering() { + originalName := "test__update_attribute_with_rule_and_name_and_reordering" + newName := "updated_hello" + namespaceID := s.f.GetNamespaceKey("example.org").ID + values := []string{"abc", "def", "xyz", "testing"} + attr := &attributes.CreateAttributeRequest{ + Name: originalName, + NamespaceId: namespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + Values: values, } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, original) + createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) s.Require().NoError(err) s.NotNil(createdAttr) - // should error on attempt to change namespace - update := &attributes.UpdateAttributeRequest{} - resp, err := s.db.PolicyClient.UpdateAttribute(s.ctx, createdAttr.GetId(), update) - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrRestrictViolation) + got, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(got) + nsName := got.GetNamespace().GetName() + updatedVals := []string{"def", "abc", "testing", "xyz"} + updatedValIDs := make([]string, len(values)) + for i, v := range got.GetValues() { + idx := slices.Index(updatedVals, v.GetValue()) + updatedValIDs[i] = got.GetValues()[idx].GetId() + } - // validate namespace should not have been changed - updated, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + // name, rule, order updates respected + updated, err := s.db.PolicyClient.UnsafeUpdateAttribute(s.ctx, &unsafe.UpdateAttributeRequest{ + Id: createdAttr.GetId(), + Name: newName, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY, + ValuesOrder: updatedValIDs, + }) s.Require().NoError(err) s.NotNil(updated) - s.Equal(original.GetNamespaceId(), updated.GetNamespace().GetId()) + s.Equal(newName, updated.GetName()) + s.Equal(policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY, updated.GetRule()) + + // name, rule, order updates respected and fqn is updated + updated, err = s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(updated) + s.Equal(newName, updated.GetName()) + s.Equal(policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY, updated.GetRule()) + s.Equal(fmt.Sprintf("https://%s/attr/%s", nsName, updated.GetName()), updated.GetFqn()) + s.Len(updated.GetValues(), len(values)) + + // values reflect new updated name and requested update order + for i, v := range updated.GetValues() { + s.Equal(updatedVals[i], v.GetValue()) + fqn := fmt.Sprintf("https://%s/attr/%s/value/%s", nsName, newName, v.GetValue()) + + val, err := s.db.PolicyClient.GetAttributesByValueFqns(s.ctx, &attributes.GetAttributeValuesByFqnsRequest{ + Fqns: []string{fqn}, + WithValue: &policy.AttributeValueSelector{ + WithSubjectMaps: true, + }, + }) + s.Require().NoError(err) + s.NotNil(val) + s.Len(val, 1) + s.Equal(v.GetId(), val[fqn].GetValue().GetId()) + } } -func (s *AttributesSuite) Test_UpdateAttributeWithSameNameAndNamespaceConflictFails() { - s.T().Skip("Defunct test: not possible to test update in this way; check request struct for validation instead.") - fixtureData := s.f.GetAttributeKey("example.org/attr/attr3") - original := &attributes.CreateAttributeRequest{ - Name: "test__update_attribute_with_same_name_and_namespace", - NamespaceId: fixtureData.NamespaceID, - Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, +func (s *AttributesSuite) Test_UnsafeUpdateAttribute_WithRule() { + attr := &attributes.CreateAttributeRequest{ + Name: "test__update_attribute_with_rule", + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, } - createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, original) + createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) s.Require().NoError(err) s.NotNil(createdAttr) - conflict := &attributes.UpdateAttributeRequest{} - resp, err := s.db.PolicyClient.UpdateAttribute(s.ctx, fixtureData.ID, conflict) - s.Require().Error(err) - s.Nil(resp) - s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) + got, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(got) + s.Equal(policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, got.GetRule()) + + updated, err := s.db.PolicyClient.UnsafeUpdateAttribute(s.ctx, &unsafe.UpdateAttributeRequest{ + Id: createdAttr.GetId(), + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + }) + s.Require().NoError(err) + s.NotNil(updated) + s.Equal(policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, updated.GetRule()) + + updated, err = s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(updated) + s.Equal(policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, updated.GetRule()) } -func (s *AttributesSuite) Test_DeleteAttribute() { +func (s *AttributesSuite) Test_UnsafeUpdateAttribute_WithNewName() { + originalName := "test__update_attribute_with_new_name" + newName := originalName + "updated" attr := &attributes.CreateAttributeRequest{ - Name: "test__delete_attribute", + Name: originalName, + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + Values: []string{"abc", "def"}, + } + createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) + s.Require().NoError(err) + s.NotNil(createdAttr) + + got, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(got) + + originalFqn := got.GetFqn() + s.NotEqual("", originalFqn) + + // update with a new name + updated, err := s.db.PolicyClient.UnsafeUpdateAttribute(s.ctx, &unsafe.UpdateAttributeRequest{ + Id: createdAttr.GetId(), + Name: newName, + }) + s.Require().NoError(err) + s.NotNil(updated) + s.Equal(newName, updated.GetName()) + s.NotEqual(originalFqn, updated.GetFqn()) + + updated, err = s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(updated) + + // ensure the fqn has changed + s.Equal(fmt.Sprintf("https://%s/attr/%s", got.GetNamespace().GetName(), updated.GetName()), updated.GetFqn()) + + // ensure the name change is reflected + s.Equal(newName, updated.GetName()) + + // ensure everything else is unchanged + s.Equal(got.GetRule(), updated.GetRule()) + s.Len(updated.GetValues(), 2) + s.True(updated.GetActive().GetValue()) + + // values are able to be looked up by fqn with new updated name + fqns := []string{ + fmt.Sprintf("https://%s/attr/%s/value/%s", got.GetNamespace().GetName(), updated.GetName(), updated.GetValues()[0].GetValue()), + fmt.Sprintf("https://%s/attr/%s/value/%s", got.GetNamespace().GetName(), updated.GetName(), updated.GetValues()[1].GetValue()), + } + req := &attributes.GetAttributeValuesByFqnsRequest{ + Fqns: fqns, + WithValue: &policy.AttributeValueSelector{ + WithSubjectMaps: true, + }, + } + retrieved, err := s.db.PolicyClient.GetAttributesByValueFqns(s.ctx, req) + s.Require().NoError(err) + s.NotNil(retrieved) + s.Len(retrieved, 2) + + val, err := s.db.PolicyClient.GetAttributeValue(s.ctx, updated.GetValues()[0].GetId()) + s.Require().NoError(err) + s.NotNil(val) + s.Equal(fqns[0], val.GetFqn()) + s.Contains(val.GetFqn(), updated.GetName()) + + val, err = s.db.PolicyClient.GetAttributeValue(s.ctx, updated.GetValues()[1].GetId()) + s.Require().NoError(err) + s.NotNil(val) + s.Equal(fqns[1], val.GetFqn()) + s.Contains(val.GetFqn(), updated.GetName()) +} + +func (s *AttributesSuite) Test_UnsafeUpdateAttribute_NormalizesCasing() { + created, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + Name: "BANANA_PUDDING", + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + Values: []string{"fig", "jam"}, + }) + s.Require().NoError(err) + s.NotNil(created) + + got, err := s.db.PolicyClient.GetAttribute(s.ctx, created.GetId()) + s.Require().NoError(err) + s.NotNil(got) + s.Equal("banana_pudding", got.GetName()) + s.Contains(got.GetFqn(), "banana_pudding") + + updated, err := s.db.PolicyClient.UnsafeUpdateAttribute(s.ctx, &unsafe.UpdateAttributeRequest{ + Id: created.GetId(), + Name: "STRAWBERRY_SHORTCAKE", + }) + s.Require().NoError(err) + s.NotNil(updated) + s.Equal("strawberry_shortcake", updated.GetName()) + + got, err = s.db.PolicyClient.GetAttribute(s.ctx, created.GetId()) + s.Require().NoError(err) + s.NotNil(got) + s.Equal("strawberry_shortcake", got.GetName()) + s.Contains(got.GetFqn(), "strawberry_shortcake") +} + +func (s *AttributesSuite) Test_UnsafeUpdateAttribute_ReplaceValuesOrder() { + toCreate := &attributes.CreateAttributeRequest{ + Name: "test__unsafe_update_attribute_replace_values_order", + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY, + Values: []string{"first", "second", "third"}, + } + created, err := s.db.PolicyClient.CreateAttribute(s.ctx, toCreate) + s.Require().NoError(err) + s.NotNil(created) + s.Len(created.GetValues(), 3) + + // reverse values order + reversed := make([]string, len(created.GetValues())) + for i, v := range created.GetValues() { + reversed[len(created.GetValues())-i-1] = v.GetId() + } + updated, err := s.db.PolicyClient.UnsafeUpdateAttribute(s.ctx, &unsafe.UpdateAttributeRequest{ + Id: created.GetId(), + ValuesOrder: reversed, + }) + s.Require().NoError(err) + s.NotNil(updated) + s.Len(updated.GetValues(), 3) + + // get attribute and ensure the order of the values is preserved and successfully reversed + got, err := s.db.PolicyClient.GetAttribute(s.ctx, created.GetId()) + s.Require().NoError(err) + s.NotNil(got) + s.Len(got.GetValues(), 3) + for i, v := range got.GetValues() { + s.Equal(reversed[i], v.GetId()) + } +} + +func (s *AttributesSuite) Test_UnsafeDeleteAttribute() { + name := "test__delete_attribute" + attr := &attributes.CreateAttributeRequest{ + Name: name, NamespaceId: fixtureNamespaceID, Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED, + Values: []string{"value1", "value2", "value3"}, } createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) s.Require().NoError(err) s.NotNil(createdAttr) - deleted, err := s.db.PolicyClient.DeleteAttribute(s.ctx, createdAttr.GetId()) + got, _ := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + + deleted, err := s.db.PolicyClient.UnsafeDeleteAttribute(s.ctx, got, got.GetFqn()) s.Require().NoError(err) s.NotNil(deleted) - // should not exist anymore + // attribute should not exist anymore resp, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) s.Require().Error(err) s.Nil(resp) + + // values should not exist anymore (cascade delete) + for _, v := range createdAttr.GetValues() { + resp, err := s.db.PolicyClient.GetAttributeValue(s.ctx, v.GetId()) + s.Require().Error(err) + s.Nil(resp) + } + + // namespace should still exist unaffected + ns, err := s.db.PolicyClient.GetNamespace(s.ctx, fixtureNamespaceID) + s.Require().NoError(err) + s.NotNil(ns) + s.NotEqual("", ns.GetId()) + + // attribute should not be listed anymore + list, err := s.db.PolicyClient.ListAllAttributes(s.ctx, policydb.StateAny, fixtureNamespaceID) + s.Require().NoError(err) + s.NotNil(list) + for _, l := range list { + s.NotEqual(createdAttr.GetId(), l.GetId()) + } + + // attr fqn should not be found + fqn := fmt.Sprintf("https://%s/attr/%s", ns.GetName(), name) + resp, err = s.db.PolicyClient.GetAttributeByFqn(s.ctx, fqn) + s.Require().Error(err) + s.Nil(resp) + s.Require().ErrorIs(err, db.ErrNotFound) + + // value fqns should be deleted and not found + for _, v := range got.GetValues() { + fqns := []string{fmt.Sprintf("https://%s/attr/%s/value/%s", ns.GetName(), name, v.GetValue())} + req := &attributes.GetAttributeValuesByFqnsRequest{ + Fqns: fqns, + WithValue: &policy.AttributeValueSelector{ + WithSubjectMaps: true, + }, + } + retrieved, err := s.db.PolicyClient.GetAttributesByValueFqns(s.ctx, req) + s.Require().Error(err) + s.Nil(retrieved) + s.Require().ErrorIs(err, db.ErrNotFound) + } + + // should be able to create attribute of same name as deleted + createdAttr, err = s.db.PolicyClient.CreateAttribute(s.ctx, attr) + s.Require().NoError(err) + s.NotNil(createdAttr) } -func (s *AttributesSuite) Test_DeleteAttribute_WithInvalidIdFails() { - deleted, err := s.db.PolicyClient.DeleteAttribute(s.ctx, nonExistentAttrID) +func (s *AttributesSuite) Test_UnsafeDeleteAttribute_WithBadFqnFails() { + created, _ := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + Name: "test__delete_attribute_with_bad_fqn", + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + }) + got, _ := s.db.PolicyClient.GetAttribute(s.ctx, created.GetId()) + s.NotNil(got) + s.NotEqual("", got.GetFqn()) + + deleted, err := s.db.PolicyClient.UnsafeDeleteAttribute(s.ctx, got, "bad_fqn") s.Require().Error(err) s.Nil(deleted) s.Require().ErrorIs(err, db.ErrNotFound) @@ -732,6 +980,56 @@ func (s *AttributesSuite) Test_DeactivateAttribute_Cascades_ToValues_Get() { s.False(gotVal.GetActive().GetValue()) } +func (s *AttributesSuite) Test_UnsafeReactivateAttribute() { + // create an attribute + attr := &attributes.CreateAttributeRequest{ + Name: "test__unsafe_reactivate_attribute", + NamespaceId: fixtureNamespaceID, + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + Values: []string{"aa", "bb", "cc"}, + } + createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) + s.Require().NoError(err) + s.NotNil(createdAttr) + + // deactivate the attribute + deactivatedAttr, err := s.db.PolicyClient.DeactivateAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(deactivatedAttr) + s.False(deactivatedAttr.GetActive().GetValue()) + + // reactivate the attribute + reactivatedAttr, err := s.db.PolicyClient.UnsafeReactivateAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(reactivatedAttr) + + // found to be active + activated, err := s.db.PolicyClient.GetAttribute(s.ctx, reactivatedAttr.GetId()) + s.Require().NoError(err) + s.NotNil(activated) + s.True(activated.GetActive().GetValue()) + + // found in successive lookup by fqn + activated, err = s.db.PolicyClient.GetAttributeByFqn(s.ctx, activated.GetFqn()) + s.Require().NoError(err) + s.NotNil(activated) + + // ensure the values are still inactive + for _, v := range reactivatedAttr.GetValues() { + got, err := s.db.PolicyClient.GetAttributeValue(s.ctx, v.GetId()) + s.Require().NoError(err) + s.NotNil(got) + s.False(got.GetActive().GetValue()) + } +} + +func (s *AttributesSuite) Test_UnsafeReactivateAttribute_WithInvalidIdFails() { + reactivatedAttr, err := s.db.PolicyClient.UnsafeReactivateAttribute(s.ctx, nonExistentAttrID) + s.Require().Error(err) + s.Nil(reactivatedAttr) + s.Require().ErrorIs(err, db.ErrNotFound) +} + func (s *AttributesSuite) Test_AssignKeyAccessServerToAttribute_Returns_Error_When_Attribute_Not_Found() { aKas := &attributes.AttributeKeyAccessServer{ AttributeId: nonExistentAttrID, diff --git a/service/integration/namespaces_test.go b/service/integration/namespaces_test.go index 28ddd4aef2..a055795b15 100644 --- a/service/integration/namespaces_test.go +++ b/service/integration/namespaces_test.go @@ -462,8 +462,10 @@ func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_Cascades() { s.Require().NoError(err) s.NotNil(createdVal2) + existing, _ := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) + // delete the namespace - deleted, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, n.GetId()) + deleted, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, existing, existing.GetFqn()) s.Require().NoError(err) s.NotNil(deleted) @@ -496,8 +498,10 @@ func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_ShouldBeAbleToRecreateDelet s.Require().NoError(err) s.NotNil(n) + got, _ := s.db.PolicyClient.GetNamespace(s.ctx, n.GetId()) + // delete the namespace - _, err = s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, n.GetId()) + _, err = s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, got, got.GetFqn()) s.Require().NoError(err) // create the namespace again @@ -506,6 +510,24 @@ func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_ShouldBeAbleToRecreateDelet s.NotNil(n) } +func (s *NamespacesSuite) Test_UnsafeDeleteNamespace_DoesNotExist_ShouldFail() { + ns, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, &policy.Namespace{}, "does.not.exist") + s.Require().Error(err) + s.Require().ErrorIs(err, db.ErrNotFound) + s.Nil(ns) + + created, _ := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "deletingns.com"}) + s.NotNil(created) + got, _ := s.db.PolicyClient.GetNamespace(s.ctx, created.GetId()) + s.NotNil(got) + s.NotEqual("", got.GetFqn()) + + ns, err = s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, got, "https://bad.fqn") + s.Require().Error(err) + s.Require().ErrorIs(err, db.ErrNotFound) + s.Nil(ns) +} + func (s *NamespacesSuite) Test_UnsafeReactivateNamespace_SetsActiveStatusOfNamespace() { // create a namespace n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "reactivating-namespace.com"}) @@ -666,6 +688,50 @@ func (s *NamespacesSuite) Test_UnsafeUpdateNamespace() { s.NotEqual(created.GetId(), recreated.GetId()) } +// validate a namespace with a definition and values are all lookupable by fqn +func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_CascadesInFqns() { + // create a namespace + n, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "updating-namespace.io"}) + s.Require().NoError(err) + s.NotNil(n) + + // create an attribute under that namespace + attr := &attributes.CreateAttributeRequest{ + Name: "test__updating-attr", + NamespaceId: n.GetId(), + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + Values: []string{"test_val1"}, + } + createdAttr, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) + s.Require().NoError(err) + s.NotNil(createdAttr) + createdAttr, _ = s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + createdVal := createdAttr.GetValues()[0] + + // update the namespace + updated, err := s.db.PolicyClient.UnsafeUpdateNamespace(s.ctx, n.GetId(), "updated.ca") + s.Require().NoError(err) + s.NotNil(updated) + + // ensure the namespace has the proper fqn + gotNs, err := s.db.PolicyClient.GetNamespace(s.ctx, updated.GetId()) + s.Require().NoError(err) + s.NotNil(gotNs) + s.Equal("https://updated.ca", gotNs.GetFqn()) + + // ensure the attribute has the proper fqn + gotAttr, err := s.db.PolicyClient.GetAttribute(s.ctx, createdAttr.GetId()) + s.Require().NoError(err) + s.NotNil(gotAttr) + s.Equal("https://updated.ca/attr/test__updating-attr", gotAttr.GetFqn()) + + // ensure the value has the proper fqn + gotVal, err := s.db.PolicyClient.GetAttributeValue(s.ctx, createdVal.GetId()) + s.Require().NoError(err) + s.NotNil(gotVal) + s.Equal("https://updated.ca/attr/test__updating-attr/value/test_val1", gotVal.GetFqn()) +} + func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_DoesNotExist_ShouldFail() { ns, err := s.db.PolicyClient.UnsafeUpdateNamespace(s.ctx, nonExistentNamespaceID, "does.not.exist") s.Require().Error(err) @@ -673,6 +739,27 @@ func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_DoesNotExist_ShouldFail() { s.Nil(ns) } +func (s *NamespacesSuite) Test_UnsafeUpdateNamespace_NormalizeCasing() { + ns, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{Name: "TeStInG-XYZ.com"}) + s.Require().NoError(err) + s.NotNil(ns) + + got, _ := s.db.PolicyClient.GetNamespace(s.ctx, ns.GetId()) + s.NotNil(got) + s.Equal("testing-xyz.com", got.GetName()) + + updated, err := s.db.PolicyClient.UnsafeUpdateNamespace(s.ctx, ns.GetId(), "HELLOWORLD.COM") + s.Require().NoError(err) + s.NotNil(updated) + s.Equal("helloworld.com", updated.GetName()) + + got, _ = s.db.PolicyClient.GetNamespace(s.ctx, ns.GetId()) + s.NotNil(got) + s.Equal("helloworld.com", got.GetName()) + s.Contains(got.GetFqn(), "helloworld.com") + s.Equal("https://helloworld.com", got.GetFqn()) +} + func TestNamespacesSuite(t *testing.T) { if testing.Short() { t.Skip("skipping namespaces integration tests") diff --git a/service/policy/db/attribute_fqn.go b/service/policy/db/attribute_fqn.go index 9873ce055a..0d3de0314e 100644 --- a/service/policy/db/attribute_fqn.go +++ b/service/policy/db/attribute_fqn.go @@ -193,7 +193,7 @@ func (c *PolicyDBClient) GetAttributesByValueFqns(ctx context.Context, r *attrib filtered, selected := filterValues(attr.GetValues(), fqn) if selected == nil { slog.Error("could not find value for FQN", slog.String("fqn", fqn)) - return nil, fmt.Errorf("could not find value for FQN: %s", fqn) + return nil, fmt.Errorf("could not find value for FQN: %s: %w", fqn, db.ErrNotFound) } attr.Values = filtered list[fqn] = &attributes.GetAttributeValuesByFqnsResponse_AttributeAndValue{ diff --git a/service/policy/db/attribute_values.go b/service/policy/db/attribute_values.go index f0507e4e31..c227d3753a 100644 --- a/service/policy/db/attribute_values.go +++ b/service/policy/db/attribute_values.go @@ -197,7 +197,10 @@ func (c PolicyDBClient) CreateAttributeValue(ctx context.Context, attributeID st } // Update FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{valueID: id}) + fqn := c.upsertAttrFqn(ctx, attrFqnUpsertOptions{valueID: id}) + if fqn != "" { + slog.Debug("created new attribute value FQN", slog.String("value_id", id), slog.String("value", value), slog.String("fqn", fqn)) + } rV := &policy.Value{ Id: id, diff --git a/service/policy/db/attributes.go b/service/policy/db/attributes.go index 71a05568f1..8d2498eafc 100644 --- a/service/policy/db/attributes.go +++ b/service/policy/db/attributes.go @@ -14,6 +14,7 @@ import ( "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/protocol/go/policy/attributes" + "github.com/opentdf/platform/protocol/go/policy/unsafe" "github.com/opentdf/platform/service/pkg/db" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/wrapperspb" @@ -511,9 +512,6 @@ func (c PolicyDBClient) CreateAttribute(ctx context.Context, r *attributes.Creat return nil, err } - // Update the FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{attributeID: id}) - // Add values var values []*policy.Value for _, v := range r.GetValues() { @@ -525,6 +523,16 @@ func (c PolicyDBClient) CreateAttribute(ctx context.Context, r *attributes.Creat values = append(values, value) } + // Update the FQNs + namespaceID := r.GetNamespaceId() + fqn := c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: namespaceID, attributeID: id}) + slog.Debug("upserted fqn with new attribute definition", slog.Any("fqn", fqn)) + + for _, v := range values { + fqn = c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: namespaceID, attributeID: id, valueID: v.GetId()}) + slog.Debug("upserted fqn with new attribute value on new definition create", slog.Any("fqn", fqn)) + } + a := &policy.Attribute{ Id: id, Name: name, @@ -539,29 +547,84 @@ func (c PolicyDBClient) CreateAttribute(ctx context.Context, r *attributes.Creat return a, nil } -// TODO: uncomment this and consume when unsafe protos/service is implemented [https://github.com/opentdf/platform/issues/115] -// func unsafeUpdateAttributeSql(id string, updateName string, updateRule string, replaceValuesOrder []string, metadata []byte) (string, []interface{}, error) { -// t := Tables.Attributes -// sb := db.NewStatementBuilder().Update(t.Name()) - -// if updateName != "" { -// sb = sb.Set("name", updateName) -// } -// if updateRule != "" { -// sb = sb.Set("rule", updateRule) -// } -// // validation should happen before calling that: -// // 1) replaceValuesOrder should be the same length as the column's current array length -// // 2) replaceValuesOrder should contain all children value id's of this attribute -// if len(replaceValuesOrder) > 0 { -// sb = sb.Set("values_order", replaceValuesOrder) -// } -// if metadata != nil { -// sb = sb.Set("metadata", metadata) -// } - -// return sb.Where(sq.Eq{t.Field("id"): id}).ToSql() -// } +func unsafeUpdateAttributeSQL(id string, updateName string, updateRule string, replaceValuesOrder []string) (string, []interface{}, error) { + t := Tables.Attributes + sb := db.NewStatementBuilder().Update(t.Name()) + + if updateName != "" { + sb = sb.Set("name", updateName) + } + if updateRule != "" { + sb = sb.Set("rule", updateRule) + } + if len(replaceValuesOrder) > 0 { + sb = sb.Set("values_order", replaceValuesOrder) + } + + return sb.Where(sq.Eq{t.Field("id"): id}). + ToSql() +} + +func (c PolicyDBClient) UnsafeUpdateAttribute(ctx context.Context, r *unsafe.UpdateAttributeRequest) (*policy.Attribute, error) { + id := r.GetId() + before, err := c.GetAttribute(ctx, id) + if err != nil { + return nil, err + } + + // Validate that the values_order contains all the children value id's of this attribute + lenExistingValues := len(before.GetValues()) + lenNewValues := len(r.GetValuesOrder()) + if lenNewValues > 0 { + if lenExistingValues != lenNewValues { + return nil, fmt.Errorf("values_order can only be updated with current attribute values: %w", db.ErrForeignKeyViolation) + } + // check if all the children value id's of this attribute are in the values_order + for _, v := range before.GetValues() { + found := false + for _, vo := range r.GetValuesOrder() { + if v.GetId() == vo { + found = true + break + } + } + if !found { + return nil, fmt.Errorf("values_order can only be updated with current attribute values: %w", db.ErrForeignKeyViolation) + } + } + } + + // Handle case where rule is not actually being updated + rule := "" + if r.GetRule() != policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED { + rule = attributesRuleTypeEnumTransformIn(r.GetRule().String()) + } + + sql, args, err := unsafeUpdateAttributeSQL(id, strings.ToLower(r.GetName()), rule, r.GetValuesOrder()) + if err != nil { + return nil, err + } + + err = c.Exec(ctx, sql, args) + if err != nil { + return nil, err + } + + // Upsert all the FQNs with the definition name mutation + if r.GetName() != "" { + namespaceID := before.GetNamespace().GetId() + fqn := c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: namespaceID, attributeID: id}) + slog.Debug("upserted attribute fqn with new definition name", slog.Any("fqn", fqn)) + if len(before.GetValues()) > 0 { + for _, v := range before.GetValues() { + fqn = c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: namespaceID, attributeID: id, valueID: v.GetId()}) + slog.Debug("upserted attribute value fqn with new definition name", slog.Any("fqn", fqn)) + } + } + } + + return c.GetAttribute(ctx, id) +} func safeUpdateAttributeSQL(id string, metadata []byte) (string, []interface{}, error) { t := Tables.Attributes @@ -601,9 +664,6 @@ func (c PolicyDBClient) UpdateAttribute(ctx context.Context, id string, r *attri return nil, err } - // Update the FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{attributeID: id}) - return &policy.Attribute{ Id: id, }, nil @@ -631,34 +691,65 @@ func (c PolicyDBClient) DeactivateAttribute(ctx context.Context, id string) (*po return c.GetAttribute(ctx, id) } -func deleteAttributeSQL(id string) (string, []interface{}, error) { +func unsafeReactivateAttributeSQL(id string) (string, []interface{}, error) { t := Tables.Attributes return db.NewStatementBuilder(). - Delete(t.Name()). + Update(t.Name()). + Set("active", true). Where(sq.Eq{t.Field("id"): id}). + Suffix("RETURNING \"id\""). ToSql() } -func (c PolicyDBClient) DeleteAttribute(ctx context.Context, id string) (*policy.Attribute, error) { - // get attribute before deleting - a, err := c.GetAttribute(ctx, id) +func (c PolicyDBClient) UnsafeReactivateAttribute(ctx context.Context, id string) (*policy.Attribute, error) { + sql, args, err := unsafeReactivateAttributeSQL(id) if err != nil { - return nil, db.WrapIfKnownInvalidQueryErr(err) + return nil, err + } + + if err := c.Exec(ctx, sql, args); err != nil { + return nil, err + } + + return c.GetAttribute(ctx, id) +} + +func unsafeDeleteAttributeSQL(id string) (string, []interface{}, error) { + t := Tables.Attributes + return db.NewStatementBuilder(). + Delete(t.Name()). + Where(sq.Eq{t.Field("id"): id}). + Suffix("RETURNING \"id\""). + ToSql() +} + +func (c PolicyDBClient) UnsafeDeleteAttribute(ctx context.Context, existing *policy.Attribute, fqn string) (*policy.Attribute, error) { + if existing == nil { + return nil, fmt.Errorf("attribute not found: %w", db.ErrNotFound) } + id := existing.GetId() - sql, args, err := deleteAttributeSQL(id) + if existing.GetFqn() != fqn { + return nil, fmt.Errorf("fqn mismatch: %w", db.ErrNotFound) + } + sql, args, err := unsafeDeleteAttributeSQL(id) if err != nil { - return nil, db.WrapIfKnownInvalidQueryErr(err) + return nil, err } if err := c.Exec(ctx, sql, args); err != nil { return nil, err } - // return the attribute before deleting - return a, nil + return &policy.Attribute{ + Id: id, + }, nil } +/// +/// Key Access Server assignments +/// + func assignKeyAccessServerToAttributeSQL(attributeID, keyAccessServerID string) (string, []interface{}, error) { t := Tables.AttributeKeyAccessGrants return db.NewStatementBuilder(). diff --git a/service/policy/db/namespaces.go b/service/policy/db/namespaces.go index 802b93d87d..03e206dcb4 100644 --- a/service/policy/db/namespaces.go +++ b/service/policy/db/namespaces.go @@ -3,6 +3,7 @@ package db import ( "context" "database/sql" + "fmt" "log/slog" "strings" @@ -208,7 +209,8 @@ func (c PolicyDBClient) CreateNamespace(ctx context.Context, r *namespaces.Creat } // Update FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id}) + fqn := c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id}) + slog.Debug("upserted fqn for created namespace", slog.Any("fqn", fqn)) return &policy.Namespace{ Id: id, @@ -259,9 +261,6 @@ func (c PolicyDBClient) UpdateNamespace(ctx context.Context, id string, r *names return nil, err } - // Update FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id}) - return &policy.Namespace{ Id: id, }, nil @@ -278,7 +277,7 @@ func unsafeUpdateNamespaceSQL(id string, name string) (string, []interface{}, er } func (c PolicyDBClient) UnsafeUpdateNamespace(ctx context.Context, id string, name string) (*policy.Namespace, error) { - sql, args, err := unsafeUpdateNamespaceSQL(id, name) + sql, args, err := unsafeUpdateNamespaceSQL(id, strings.ToLower(name)) if db.IsQueryBuilderSetClauseError(err) { return &policy.Namespace{ Id: id, @@ -292,8 +291,23 @@ func (c PolicyDBClient) UnsafeUpdateNamespace(ctx context.Context, id string, na if err != nil { return nil, err } - // Update FQN - c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id}) + + // Update all FQNs that may contain the namespace name + fqn := c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id}) + slog.Debug("upserted fqn for unsafely updated namespace", slog.Any("fqn", fqn)) + + attrs, err := c.ListAllAttributes(ctx, StateAny, id) + if err != nil { + return nil, err + } + for _, attr := range attrs { + fqn = c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id, attributeID: attr.GetId()}) + slog.Debug("upserted definition fqn for unsafely updated namespace", slog.Any("fqn", fqn)) + for _, value := range attr.GetValues() { + fqn = c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceID: id, attributeID: attr.GetId(), valueID: value.GetId()}) + slog.Debug("upserted value fqn for unsafely updated namespace", slog.Any("fqn", fqn)) + } + } return hydrateNamespaceItem(row, namespaceSelectOptions{}) } @@ -367,7 +381,15 @@ func deleteNamespaceSQL(id string) (string, []interface{}, error) { ToSql() } -func (c PolicyDBClient) UnsafeDeleteNamespace(ctx context.Context, id string) (*policy.Namespace, error) { +func (c PolicyDBClient) UnsafeDeleteNamespace(ctx context.Context, existing *policy.Namespace, fqn string) (*policy.Namespace, error) { + if existing == nil { + return nil, fmt.Errorf("namespace not found: %w", db.ErrNotFound) + } + id := existing.GetId() + + if existing.GetFqn() != fqn { + return nil, fmt.Errorf("fqn mismatch: %w", db.ErrNotFound) + } sql, args, err := deleteNamespaceSQL(id) if err != nil { return nil, err diff --git a/service/policy/unsafe/unsafe.go b/service/policy/unsafe/unsafe.go index 810bbc3572..5b5be02be9 100644 --- a/service/policy/unsafe/unsafe.go +++ b/service/policy/unsafe/unsafe.go @@ -80,12 +80,7 @@ func (s *UnsafeService) DeleteNamespace(ctx context.Context, req *unsafe.DeleteN return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) } - // validate the provided namespace FQN is a match for the provided namespace ID - if existing.GetFqn() != req.GetFqn() { - return nil, db.StatusifyError(db.ErrNotFound, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId()), slog.String("fqn", req.GetFqn())) - } - - deleted, err := s.dbClient.UnsafeDeleteNamespace(ctx, req.GetId()) + deleted, err := s.dbClient.UnsafeDeleteNamespace(ctx, existing, req.GetFqn()) if err != nil { return nil, db.StatusifyError(err, db.ErrTextDeletionFailed, slog.String("id", req.GetId())) } @@ -99,58 +94,58 @@ func (s *UnsafeService) DeleteNamespace(ctx context.Context, req *unsafe.DeleteN // Unsafe Attribute Definition RPCs // -func (s *UnsafeService) UpdateAttribute(_ context.Context, req *unsafe.UpdateAttributeRequest) (*unsafe.UpdateAttributeResponse, error) { - // _, err := s.dbClient.GetAttribute(ctx, req.GetId()) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) - // } +func (s *UnsafeService) UpdateAttribute(ctx context.Context, req *unsafe.UpdateAttributeRequest) (*unsafe.UpdateAttributeResponse, error) { + rsp := &unsafe.UpdateAttributeResponse{} - // item, err := s.dbClient.UnsafeUpdateAttribute(ctx, req.GetId(), req) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextUpdateFailed, slog.String("id", req.GetId()), slog.String("attribute", req.String())) - // } + _, err := s.dbClient.GetAttribute(ctx, req.GetId()) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) + } - return &unsafe.UpdateAttributeResponse{ - Attribute: &policy.Attribute{ - Id: req.GetId(), // stubbed - }, - }, nil + item, err := s.dbClient.UnsafeUpdateAttribute(ctx, req) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextUpdateFailed, slog.String("id", req.GetId()), slog.String("attribute", req.String())) + } + + rsp.Attribute = item + + return rsp, nil } -func (s *UnsafeService) ReactivateAttribute(_ context.Context, req *unsafe.ReactivateAttributeRequest) (*unsafe.ReactivateAttributeResponse, error) { - // _, err := s.dbClient.GetAttribute(ctx, req.GetId()) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) - // } +func (s *UnsafeService) ReactivateAttribute(ctx context.Context, req *unsafe.ReactivateAttributeRequest) (*unsafe.ReactivateAttributeResponse, error) { + rsp := &unsafe.ReactivateAttributeResponse{} - // item, err := s.dbClient.UnsafeReactivateAttribute(ctx, req.GetId()) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextUpdateFailed, slog.String("id", req.GetId())) - // } + _, err := s.dbClient.GetAttribute(ctx, req.GetId()) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) + } - return &unsafe.ReactivateAttributeResponse{ - Attribute: &policy.Attribute{ - Id: req.GetId(), // stubbed - }, - }, nil + item, err := s.dbClient.UnsafeReactivateAttribute(ctx, req.GetId()) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextUpdateFailed, slog.String("id", req.GetId())) + } + + rsp.Attribute = item + + return rsp, nil } -func (s *UnsafeService) DeleteAttribute(_ context.Context, req *unsafe.DeleteAttributeRequest) (*unsafe.DeleteAttributeResponse, error) { - // _, err := s.dbClient.GetAttribute(ctx, req.GetId()) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) - // } +func (s *UnsafeService) DeleteAttribute(ctx context.Context, req *unsafe.DeleteAttributeRequest) (*unsafe.DeleteAttributeResponse, error) { + rsp := &unsafe.DeleteAttributeResponse{} - // err = s.dbClient.UnsafeDeleteAttribute(ctx, req.GetId()) - // if err != nil { - // return nil, db.StatusifyError(err, db.ErrTextDeleteFailed, slog.String("id", req.GetId())) - // } + existing, err := s.dbClient.GetAttribute(ctx, req.GetId()) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextGetRetrievalFailed, slog.String("id", req.GetId())) + } - return &unsafe.DeleteAttributeResponse{ - Attribute: &policy.Attribute{ - Id: req.GetId(), // stubbed - }, - }, nil + deleted, err := s.dbClient.UnsafeDeleteAttribute(ctx, existing, req.GetFqn()) + if err != nil { + return nil, db.StatusifyError(err, db.ErrTextDeletionFailed, slog.String("id", req.GetId())) + } + + rsp.Attribute = deleted + + return rsp, nil } // diff --git a/service/policy/unsafe/unsafe.proto b/service/policy/unsafe/unsafe.proto index 5565cbd05f..ba401b552d 100644 --- a/service/policy/unsafe/unsafe.proto +++ b/service/policy/unsafe/unsafe.proto @@ -76,6 +76,11 @@ message UpdateAttributeRequest { AttributeRuleTypeEnum rule = 3 [ (buf.validate.field).enum.defined_only = true ]; + // WARNING!! + // Unsafe reordering requires the full list of values in the new order they should be stored. Updating the order of values in a HIERARCHY-rule Attribute Definition + // will retroactively alter access to existing TDFs containing those values. Replacing values on an attribute in place is not supported; values can be unsafely deleted + // deleted, created, and unsafely re-ordered as necessary. + repeated string values_order = 4; } message UpdateAttributeResponse { policy.Attribute attribute = 1;