diff --git a/api/openapi/users.yml b/api/openapi/users.yml index 7fee8ffad8..c456929ed7 100644 --- a/api/openapi/users.yml +++ b/api/openapi/users.yml @@ -178,6 +178,32 @@ paths: "500": $ref: "#/components/responses/ServiceError" + delete: + summary: Delete a user + description: | + Delete a specific user that is identifier by the user ID. + tags: + - Users + parameters: + - $ref: "#/components/parameters/UserID" + security: + - bearerAuth: [] + responses: + "204": + description: User deleted. + "400": + description: Failed due to malformed query parameters. + "401": + description: Missing or invalid access token provided. + "404": + description: A non-existent entity request. + "405": + description: Method not allowed. + "422": + description: Database can't process request. + "500": + $ref: "#/components/responses/ServiceError" + /users/{userID}/tags: patch: operationId: updateUserTags diff --git a/auth.pb.go b/auth.pb.go index 9d2e6c45df..6c072e2033 100644 --- a/auth.pb.go +++ b/auth.pb.go @@ -862,53 +862,6 @@ func (x *DeletePolicyFilterReq) GetObjectType() string { return "" } -type DeletePolicyFilterRes struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` -} - -func (x *DeletePolicyFilterRes) Reset() { - *x = DeletePolicyFilterRes{} - if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeletePolicyFilterRes) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeletePolicyFilterRes) ProtoMessage() {} - -func (x *DeletePolicyFilterRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeletePolicyFilterRes.ProtoReflect.Descriptor instead. -func (*DeletePolicyFilterRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{12} -} - -func (x *DeletePolicyFilterRes) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - type DeletePoliciesReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -920,7 +873,7 @@ type DeletePoliciesReq struct { func (x *DeletePoliciesReq) Reset() { *x = DeletePoliciesReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[13] + mi := &file_auth_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -933,7 +886,7 @@ func (x *DeletePoliciesReq) String() string { func (*DeletePoliciesReq) ProtoMessage() {} func (x *DeletePoliciesReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[13] + mi := &file_auth_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -946,7 +899,7 @@ func (x *DeletePoliciesReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePoliciesReq.ProtoReflect.Descriptor instead. func (*DeletePoliciesReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{13} + return file_auth_proto_rawDescGZIP(), []int{12} } func (x *DeletePoliciesReq) GetDeletePoliciesReq() []*DeletePolicyReq { @@ -976,7 +929,7 @@ type DeletePolicyReq struct { func (x *DeletePolicyReq) Reset() { *x = DeletePolicyReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[14] + mi := &file_auth_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -989,7 +942,7 @@ func (x *DeletePolicyReq) String() string { func (*DeletePolicyReq) ProtoMessage() {} func (x *DeletePolicyReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[14] + mi := &file_auth_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1002,7 +955,7 @@ func (x *DeletePolicyReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePolicyReq.ProtoReflect.Descriptor instead. func (*DeletePolicyReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{14} + return file_auth_proto_rawDescGZIP(), []int{13} } func (x *DeletePolicyReq) GetDomain() string { @@ -1075,7 +1028,7 @@ func (x *DeletePolicyReq) GetObjectType() string { return "" } -type DeletePoliciesRes struct { +type DeletePolicyRes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1083,23 +1036,23 @@ type DeletePoliciesRes struct { Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` } -func (x *DeletePoliciesRes) Reset() { - *x = DeletePoliciesRes{} +func (x *DeletePolicyRes) Reset() { + *x = DeletePolicyRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[15] + mi := &file_auth_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeletePoliciesRes) String() string { +func (x *DeletePolicyRes) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeletePoliciesRes) ProtoMessage() {} +func (*DeletePolicyRes) ProtoMessage() {} -func (x *DeletePoliciesRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[15] +func (x *DeletePolicyRes) ProtoReflect() protoreflect.Message { + mi := &file_auth_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1110,12 +1063,12 @@ func (x *DeletePoliciesRes) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeletePoliciesRes.ProtoReflect.Descriptor instead. -func (*DeletePoliciesRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{15} +// Deprecated: Use DeletePolicyRes.ProtoReflect.Descriptor instead. +func (*DeletePolicyRes) Descriptor() ([]byte, []int) { + return file_auth_proto_rawDescGZIP(), []int{14} } -func (x *DeletePoliciesRes) GetDeleted() bool { +func (x *DeletePolicyRes) GetDeleted() bool { if x != nil { return x.Deleted } @@ -1142,7 +1095,7 @@ type ListObjectsReq struct { func (x *ListObjectsReq) Reset() { *x = ListObjectsReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[16] + mi := &file_auth_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1155,7 +1108,7 @@ func (x *ListObjectsReq) String() string { func (*ListObjectsReq) ProtoMessage() {} func (x *ListObjectsReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[16] + mi := &file_auth_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1168,7 +1121,7 @@ func (x *ListObjectsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ListObjectsReq.ProtoReflect.Descriptor instead. func (*ListObjectsReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{16} + return file_auth_proto_rawDescGZIP(), []int{15} } func (x *ListObjectsReq) GetDomain() string { @@ -1253,7 +1206,7 @@ type ListObjectsRes struct { func (x *ListObjectsRes) Reset() { *x = ListObjectsRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[17] + mi := &file_auth_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1266,7 +1219,7 @@ func (x *ListObjectsRes) String() string { func (*ListObjectsRes) ProtoMessage() {} func (x *ListObjectsRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[17] + mi := &file_auth_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1279,7 +1232,7 @@ func (x *ListObjectsRes) ProtoReflect() protoreflect.Message { // Deprecated: Use ListObjectsRes.ProtoReflect.Descriptor instead. func (*ListObjectsRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{17} + return file_auth_proto_rawDescGZIP(), []int{16} } func (x *ListObjectsRes) GetPolicies() []string { @@ -1315,7 +1268,7 @@ type CountObjectsReq struct { func (x *CountObjectsReq) Reset() { *x = CountObjectsReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[18] + mi := &file_auth_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1328,7 +1281,7 @@ func (x *CountObjectsReq) String() string { func (*CountObjectsReq) ProtoMessage() {} func (x *CountObjectsReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[18] + mi := &file_auth_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1341,7 +1294,7 @@ func (x *CountObjectsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CountObjectsReq.ProtoReflect.Descriptor instead. func (*CountObjectsReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{18} + return file_auth_proto_rawDescGZIP(), []int{17} } func (x *CountObjectsReq) GetDomain() string { @@ -1418,7 +1371,7 @@ type CountObjectsRes struct { func (x *CountObjectsRes) Reset() { *x = CountObjectsRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[19] + mi := &file_auth_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1431,7 +1384,7 @@ func (x *CountObjectsRes) String() string { func (*CountObjectsRes) ProtoMessage() {} func (x *CountObjectsRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[19] + mi := &file_auth_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1444,7 +1397,7 @@ func (x *CountObjectsRes) ProtoReflect() protoreflect.Message { // Deprecated: Use CountObjectsRes.ProtoReflect.Descriptor instead. func (*CountObjectsRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{19} + return file_auth_proto_rawDescGZIP(), []int{18} } func (x *CountObjectsRes) GetCount() uint64 { @@ -1474,7 +1427,7 @@ type ListSubjectsReq struct { func (x *ListSubjectsReq) Reset() { *x = ListSubjectsReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[20] + mi := &file_auth_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1487,7 +1440,7 @@ func (x *ListSubjectsReq) String() string { func (*ListSubjectsReq) ProtoMessage() {} func (x *ListSubjectsReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[20] + mi := &file_auth_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1500,7 +1453,7 @@ func (x *ListSubjectsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSubjectsReq.ProtoReflect.Descriptor instead. func (*ListSubjectsReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{20} + return file_auth_proto_rawDescGZIP(), []int{19} } func (x *ListSubjectsReq) GetDomain() string { @@ -1585,7 +1538,7 @@ type ListSubjectsRes struct { func (x *ListSubjectsRes) Reset() { *x = ListSubjectsRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[21] + mi := &file_auth_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1598,7 +1551,7 @@ func (x *ListSubjectsRes) String() string { func (*ListSubjectsRes) ProtoMessage() {} func (x *ListSubjectsRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[21] + mi := &file_auth_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1611,7 +1564,7 @@ func (x *ListSubjectsRes) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSubjectsRes.ProtoReflect.Descriptor instead. func (*ListSubjectsRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{21} + return file_auth_proto_rawDescGZIP(), []int{20} } func (x *ListSubjectsRes) GetPolicies() []string { @@ -1647,7 +1600,7 @@ type CountSubjectsReq struct { func (x *CountSubjectsReq) Reset() { *x = CountSubjectsReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[22] + mi := &file_auth_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1660,7 +1613,7 @@ func (x *CountSubjectsReq) String() string { func (*CountSubjectsReq) ProtoMessage() {} func (x *CountSubjectsReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[22] + mi := &file_auth_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1673,7 +1626,7 @@ func (x *CountSubjectsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CountSubjectsReq.ProtoReflect.Descriptor instead. func (*CountSubjectsReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{22} + return file_auth_proto_rawDescGZIP(), []int{21} } func (x *CountSubjectsReq) GetDomain() string { @@ -1750,7 +1703,7 @@ type CountSubjectsRes struct { func (x *CountSubjectsRes) Reset() { *x = CountSubjectsRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[23] + mi := &file_auth_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1763,7 +1716,7 @@ func (x *CountSubjectsRes) String() string { func (*CountSubjectsRes) ProtoMessage() {} func (x *CountSubjectsRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[23] + mi := &file_auth_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1776,7 +1729,7 @@ func (x *CountSubjectsRes) ProtoReflect() protoreflect.Message { // Deprecated: Use CountSubjectsRes.ProtoReflect.Descriptor instead. func (*CountSubjectsRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{23} + return file_auth_proto_rawDescGZIP(), []int{22} } func (x *CountSubjectsRes) GetCount() uint64 { @@ -1803,7 +1756,7 @@ type ListPermissionsReq struct { func (x *ListPermissionsReq) Reset() { *x = ListPermissionsReq{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[24] + mi := &file_auth_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1816,7 +1769,7 @@ func (x *ListPermissionsReq) String() string { func (*ListPermissionsReq) ProtoMessage() {} func (x *ListPermissionsReq) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[24] + mi := &file_auth_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1829,7 +1782,7 @@ func (x *ListPermissionsReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsReq.ProtoReflect.Descriptor instead. func (*ListPermissionsReq) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{24} + return file_auth_proto_rawDescGZIP(), []int{23} } func (x *ListPermissionsReq) GetDomain() string { @@ -1898,7 +1851,7 @@ type ListPermissionsRes struct { func (x *ListPermissionsRes) Reset() { *x = ListPermissionsRes{} if protoimpl.UnsafeEnabled { - mi := &file_auth_proto_msgTypes[25] + mi := &file_auth_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1911,7 +1864,7 @@ func (x *ListPermissionsRes) String() string { func (*ListPermissionsRes) ProtoMessage() {} func (x *ListPermissionsRes) ProtoReflect() protoreflect.Message { - mi := &file_auth_proto_msgTypes[25] + mi := &file_auth_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1924,7 +1877,7 @@ func (x *ListPermissionsRes) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsRes.ProtoReflect.Descriptor instead. func (*ListPermissionsRes) Descriptor() ([]byte, []int) { - return file_auth_proto_rawDescGZIP(), []int{25} + return file_auth_proto_rawDescGZIP(), []int{24} } func (x *ListPermissionsRes) GetDomain() string { @@ -1976,6 +1929,61 @@ func (x *ListPermissionsRes) GetPermissions() []string { return nil } +type DeleteEntityPoliciesReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EntityType string `protobuf:"bytes,1,opt,name=entity_type,json=entityType,proto3" json:"entity_type,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteEntityPoliciesReq) Reset() { + *x = DeleteEntityPoliciesReq{} + if protoimpl.UnsafeEnabled { + mi := &file_auth_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteEntityPoliciesReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteEntityPoliciesReq) ProtoMessage() {} + +func (x *DeleteEntityPoliciesReq) ProtoReflect() protoreflect.Message { + mi := &file_auth_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteEntityPoliciesReq.ProtoReflect.Descriptor instead. +func (*DeleteEntityPoliciesReq) Descriptor() ([]byte, []int) { + return file_auth_proto_rawDescGZIP(), []int{25} +} + +func (x *DeleteEntityPoliciesReq) GetEntityType() string { + if x != nil { + return x.EntityType + } + return "" +} + +func (x *DeleteEntityPoliciesReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_auth_proto protoreflect.FileDescriptor var file_auth_proto_rawDesc = []byte{ @@ -2083,113 +2091,84 @@ var file_auth_proto_rawDesc = []byte{ 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x31, 0x0a, 0x15, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, - 0x5e, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x12, 0x49, 0x0a, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x52, 0x11, 0x64, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x22, - 0xca, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, - 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x2d, 0x0a, 0x11, + 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x5e, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0xc1, 0x02, 0x0a, 0x0e, - 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, - 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, - 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, - 0x52, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x24, 0x0a, - 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x22, 0xac, 0x02, 0x0a, 0x0f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, - 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x22, 0x27, 0x0a, 0x0f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc2, 0x02, 0x0a, 0x0f, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, - 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, - 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x22, 0x53, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, - 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xad, 0x02, 0x0a, 0x10, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, + 0x71, 0x12, 0x49, 0x0a, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, + 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x22, 0xca, 0x02, 0x0a, + 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, + 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0xc1, 0x02, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, + 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x52, 0x0a, 0x0e, 0x4c, 0x69, + 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, + 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xac, + 0x02, 0x0a, 0x0f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, + 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, + 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x27, 0x0a, + 0x0f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc2, 0x02, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, @@ -2208,27 +2187,15 @@ var file_auth_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x28, 0x0a, 0x10, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0xfc, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x2d, 0x0a, 0x12, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xef, - 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x53, 0x0a, 0x0f, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, + 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0xad, 0x02, 0x0a, 0x10, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, @@ -2236,89 +2203,137 @@ var file_auth_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x32, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x7a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x41, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x2e, - 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, - 0x73, 0x22, 0x00, 0x32, 0xd8, 0x08, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x14, 0x2e, 0x6d, - 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52, - 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x12, 0x16, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, - 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x12, - 0x3e, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x12, 0x17, 0x2e, 0x6d, 0x61, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, + 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0x28, 0x0a, 0x10, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xfc, 0x01, 0x0a, 0x12, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xef, 0x01, 0x0a, 0x12, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4a, 0x0a, 0x17, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x7a, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, + 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, + 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x22, 0x00, 0x32, 0xac, 0x09, 0x0a, 0x0b, 0x41, + 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x49, 0x73, + 0x73, 0x75, 0x65, 0x12, 0x14, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, + 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x12, 0x36, + 0x0a, 0x07, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x6d, 0x61, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, + 0x71, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x79, 0x12, 0x17, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, - 0x61, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, - 0x41, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x2e, 0x6d, - 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, - 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, 0x64, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, - 0x61, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, - 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x5c, - 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, - 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x0e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1d, + 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x65, 0x12, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, + 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x41, 0x64, 0x64, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, + 0x1a, 0x18, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, + 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, + 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6d, 0x61, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x2e, 0x6d, 0x61, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1b, + 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4e, 0x0a, + 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, + 0x1d, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1d, 0x2e, - 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x47, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x2e, - 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, - 0x6c, 0x6c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, - 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, - 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, - 0x4a, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, - 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6d, - 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0f, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1b, + 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x47, 0x0a, + 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6d, + 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, + 0x6c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, + 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, + 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4a, + 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x61, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6d, - 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1e, 0x2e, 0x6d, - 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x0e, - 0x5a, 0x0c, 0x2e, 0x2f, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0f, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1b, 0x2e, + 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x61, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x5a, 0x0a, + 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, + 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6d, 0x61, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6d, + 0x61, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -2335,36 +2350,36 @@ func file_auth_proto_rawDescGZIP() []byte { var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_auth_proto_goTypes = []any{ - (*Token)(nil), // 0: magistrala.Token - (*IdentityReq)(nil), // 1: magistrala.IdentityReq - (*IdentityRes)(nil), // 2: magistrala.IdentityRes - (*IssueReq)(nil), // 3: magistrala.IssueReq - (*RefreshReq)(nil), // 4: magistrala.RefreshReq - (*AuthorizeReq)(nil), // 5: magistrala.AuthorizeReq - (*AuthorizeRes)(nil), // 6: magistrala.AuthorizeRes - (*AddPolicyReq)(nil), // 7: magistrala.AddPolicyReq - (*AddPoliciesReq)(nil), // 8: magistrala.AddPoliciesReq - (*AddPolicyRes)(nil), // 9: magistrala.AddPolicyRes - (*AddPoliciesRes)(nil), // 10: magistrala.AddPoliciesRes - (*DeletePolicyFilterReq)(nil), // 11: magistrala.DeletePolicyFilterReq - (*DeletePolicyFilterRes)(nil), // 12: magistrala.DeletePolicyFilterRes - (*DeletePoliciesReq)(nil), // 13: magistrala.DeletePoliciesReq - (*DeletePolicyReq)(nil), // 14: magistrala.DeletePolicyReq - (*DeletePoliciesRes)(nil), // 15: magistrala.DeletePoliciesRes - (*ListObjectsReq)(nil), // 16: magistrala.ListObjectsReq - (*ListObjectsRes)(nil), // 17: magistrala.ListObjectsRes - (*CountObjectsReq)(nil), // 18: magistrala.CountObjectsReq - (*CountObjectsRes)(nil), // 19: magistrala.CountObjectsRes - (*ListSubjectsReq)(nil), // 20: magistrala.ListSubjectsReq - (*ListSubjectsRes)(nil), // 21: magistrala.ListSubjectsRes - (*CountSubjectsReq)(nil), // 22: magistrala.CountSubjectsReq - (*CountSubjectsRes)(nil), // 23: magistrala.CountSubjectsRes - (*ListPermissionsReq)(nil), // 24: magistrala.ListPermissionsReq - (*ListPermissionsRes)(nil), // 25: magistrala.ListPermissionsRes + (*Token)(nil), // 0: magistrala.Token + (*IdentityReq)(nil), // 1: magistrala.IdentityReq + (*IdentityRes)(nil), // 2: magistrala.IdentityRes + (*IssueReq)(nil), // 3: magistrala.IssueReq + (*RefreshReq)(nil), // 4: magistrala.RefreshReq + (*AuthorizeReq)(nil), // 5: magistrala.AuthorizeReq + (*AuthorizeRes)(nil), // 6: magistrala.AuthorizeRes + (*AddPolicyReq)(nil), // 7: magistrala.AddPolicyReq + (*AddPoliciesReq)(nil), // 8: magistrala.AddPoliciesReq + (*AddPolicyRes)(nil), // 9: magistrala.AddPolicyRes + (*AddPoliciesRes)(nil), // 10: magistrala.AddPoliciesRes + (*DeletePolicyFilterReq)(nil), // 11: magistrala.DeletePolicyFilterReq + (*DeletePoliciesReq)(nil), // 12: magistrala.DeletePoliciesReq + (*DeletePolicyReq)(nil), // 13: magistrala.DeletePolicyReq + (*DeletePolicyRes)(nil), // 14: magistrala.DeletePolicyRes + (*ListObjectsReq)(nil), // 15: magistrala.ListObjectsReq + (*ListObjectsRes)(nil), // 16: magistrala.ListObjectsRes + (*CountObjectsReq)(nil), // 17: magistrala.CountObjectsReq + (*CountObjectsRes)(nil), // 18: magistrala.CountObjectsRes + (*ListSubjectsReq)(nil), // 19: magistrala.ListSubjectsReq + (*ListSubjectsRes)(nil), // 20: magistrala.ListSubjectsRes + (*CountSubjectsReq)(nil), // 21: magistrala.CountSubjectsReq + (*CountSubjectsRes)(nil), // 22: magistrala.CountSubjectsRes + (*ListPermissionsReq)(nil), // 23: magistrala.ListPermissionsReq + (*ListPermissionsRes)(nil), // 24: magistrala.ListPermissionsRes + (*DeleteEntityPoliciesReq)(nil), // 25: magistrala.DeleteEntityPoliciesReq } var file_auth_proto_depIdxs = []int32{ 7, // 0: magistrala.AddPoliciesReq.addPoliciesReq:type_name -> magistrala.AddPolicyReq - 14, // 1: magistrala.DeletePoliciesReq.deletePoliciesReq:type_name -> magistrala.DeletePolicyReq + 13, // 1: magistrala.DeletePoliciesReq.deletePoliciesReq:type_name -> magistrala.DeletePolicyReq 5, // 2: magistrala.AuthzService.Authorize:input_type -> magistrala.AuthorizeReq 3, // 3: magistrala.AuthService.Issue:input_type -> magistrala.IssueReq 4, // 4: magistrala.AuthService.Refresh:input_type -> magistrala.RefreshReq @@ -2373,32 +2388,34 @@ var file_auth_proto_depIdxs = []int32{ 7, // 7: magistrala.AuthService.AddPolicy:input_type -> magistrala.AddPolicyReq 8, // 8: magistrala.AuthService.AddPolicies:input_type -> magistrala.AddPoliciesReq 11, // 9: magistrala.AuthService.DeletePolicyFilter:input_type -> magistrala.DeletePolicyFilterReq - 13, // 10: magistrala.AuthService.DeletePolicies:input_type -> magistrala.DeletePoliciesReq - 16, // 11: magistrala.AuthService.ListObjects:input_type -> magistrala.ListObjectsReq - 16, // 12: magistrala.AuthService.ListAllObjects:input_type -> magistrala.ListObjectsReq - 18, // 13: magistrala.AuthService.CountObjects:input_type -> magistrala.CountObjectsReq - 20, // 14: magistrala.AuthService.ListSubjects:input_type -> magistrala.ListSubjectsReq - 20, // 15: magistrala.AuthService.ListAllSubjects:input_type -> magistrala.ListSubjectsReq - 22, // 16: magistrala.AuthService.CountSubjects:input_type -> magistrala.CountSubjectsReq - 24, // 17: magistrala.AuthService.ListPermissions:input_type -> magistrala.ListPermissionsReq - 6, // 18: magistrala.AuthzService.Authorize:output_type -> magistrala.AuthorizeRes - 0, // 19: magistrala.AuthService.Issue:output_type -> magistrala.Token - 0, // 20: magistrala.AuthService.Refresh:output_type -> magistrala.Token - 2, // 21: magistrala.AuthService.Identify:output_type -> magistrala.IdentityRes - 6, // 22: magistrala.AuthService.Authorize:output_type -> magistrala.AuthorizeRes - 9, // 23: magistrala.AuthService.AddPolicy:output_type -> magistrala.AddPolicyRes - 10, // 24: magistrala.AuthService.AddPolicies:output_type -> magistrala.AddPoliciesRes - 12, // 25: magistrala.AuthService.DeletePolicyFilter:output_type -> magistrala.DeletePolicyFilterRes - 15, // 26: magistrala.AuthService.DeletePolicies:output_type -> magistrala.DeletePoliciesRes - 17, // 27: magistrala.AuthService.ListObjects:output_type -> magistrala.ListObjectsRes - 17, // 28: magistrala.AuthService.ListAllObjects:output_type -> magistrala.ListObjectsRes - 19, // 29: magistrala.AuthService.CountObjects:output_type -> magistrala.CountObjectsRes - 21, // 30: magistrala.AuthService.ListSubjects:output_type -> magistrala.ListSubjectsRes - 21, // 31: magistrala.AuthService.ListAllSubjects:output_type -> magistrala.ListSubjectsRes - 23, // 32: magistrala.AuthService.CountSubjects:output_type -> magistrala.CountSubjectsRes - 25, // 33: magistrala.AuthService.ListPermissions:output_type -> magistrala.ListPermissionsRes - 18, // [18:34] is the sub-list for method output_type - 2, // [2:18] is the sub-list for method input_type + 12, // 10: magistrala.AuthService.DeletePolicies:input_type -> magistrala.DeletePoliciesReq + 15, // 11: magistrala.AuthService.ListObjects:input_type -> magistrala.ListObjectsReq + 15, // 12: magistrala.AuthService.ListAllObjects:input_type -> magistrala.ListObjectsReq + 17, // 13: magistrala.AuthService.CountObjects:input_type -> magistrala.CountObjectsReq + 19, // 14: magistrala.AuthService.ListSubjects:input_type -> magistrala.ListSubjectsReq + 19, // 15: magistrala.AuthService.ListAllSubjects:input_type -> magistrala.ListSubjectsReq + 21, // 16: magistrala.AuthService.CountSubjects:input_type -> magistrala.CountSubjectsReq + 23, // 17: magistrala.AuthService.ListPermissions:input_type -> magistrala.ListPermissionsReq + 25, // 18: magistrala.AuthService.DeleteEntityPolicies:input_type -> magistrala.DeleteEntityPoliciesReq + 6, // 19: magistrala.AuthzService.Authorize:output_type -> magistrala.AuthorizeRes + 0, // 20: magistrala.AuthService.Issue:output_type -> magistrala.Token + 0, // 21: magistrala.AuthService.Refresh:output_type -> magistrala.Token + 2, // 22: magistrala.AuthService.Identify:output_type -> magistrala.IdentityRes + 6, // 23: magistrala.AuthService.Authorize:output_type -> magistrala.AuthorizeRes + 9, // 24: magistrala.AuthService.AddPolicy:output_type -> magistrala.AddPolicyRes + 10, // 25: magistrala.AuthService.AddPolicies:output_type -> magistrala.AddPoliciesRes + 14, // 26: magistrala.AuthService.DeletePolicyFilter:output_type -> magistrala.DeletePolicyRes + 14, // 27: magistrala.AuthService.DeletePolicies:output_type -> magistrala.DeletePolicyRes + 16, // 28: magistrala.AuthService.ListObjects:output_type -> magistrala.ListObjectsRes + 16, // 29: magistrala.AuthService.ListAllObjects:output_type -> magistrala.ListObjectsRes + 18, // 30: magistrala.AuthService.CountObjects:output_type -> magistrala.CountObjectsRes + 20, // 31: magistrala.AuthService.ListSubjects:output_type -> magistrala.ListSubjectsRes + 20, // 32: magistrala.AuthService.ListAllSubjects:output_type -> magistrala.ListSubjectsRes + 22, // 33: magistrala.AuthService.CountSubjects:output_type -> magistrala.CountSubjectsRes + 24, // 34: magistrala.AuthService.ListPermissions:output_type -> magistrala.ListPermissionsRes + 14, // 35: magistrala.AuthService.DeleteEntityPolicies:output_type -> magistrala.DeletePolicyRes + 19, // [19:36] is the sub-list for method output_type + 2, // [2:19] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -2555,7 +2572,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*DeletePolicyFilterRes); i { + switch v := v.(*DeletePoliciesReq); i { case 0: return &v.state case 1: @@ -2567,7 +2584,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*DeletePoliciesReq); i { + switch v := v.(*DeletePolicyReq); i { case 0: return &v.state case 1: @@ -2579,7 +2596,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*DeletePolicyReq); i { + switch v := v.(*DeletePolicyRes); i { case 0: return &v.state case 1: @@ -2591,7 +2608,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*DeletePoliciesRes); i { + switch v := v.(*ListObjectsReq); i { case 0: return &v.state case 1: @@ -2603,7 +2620,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*ListObjectsReq); i { + switch v := v.(*ListObjectsRes); i { case 0: return &v.state case 1: @@ -2615,7 +2632,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*ListObjectsRes); i { + switch v := v.(*CountObjectsReq); i { case 0: return &v.state case 1: @@ -2627,7 +2644,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*CountObjectsReq); i { + switch v := v.(*CountObjectsRes); i { case 0: return &v.state case 1: @@ -2639,7 +2656,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[19].Exporter = func(v any, i int) any { - switch v := v.(*CountObjectsRes); i { + switch v := v.(*ListSubjectsReq); i { case 0: return &v.state case 1: @@ -2651,7 +2668,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[20].Exporter = func(v any, i int) any { - switch v := v.(*ListSubjectsReq); i { + switch v := v.(*ListSubjectsRes); i { case 0: return &v.state case 1: @@ -2663,7 +2680,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[21].Exporter = func(v any, i int) any { - switch v := v.(*ListSubjectsRes); i { + switch v := v.(*CountSubjectsReq); i { case 0: return &v.state case 1: @@ -2675,7 +2692,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[22].Exporter = func(v any, i int) any { - switch v := v.(*CountSubjectsReq); i { + switch v := v.(*CountSubjectsRes); i { case 0: return &v.state case 1: @@ -2687,7 +2704,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[23].Exporter = func(v any, i int) any { - switch v := v.(*CountSubjectsRes); i { + switch v := v.(*ListPermissionsReq); i { case 0: return &v.state case 1: @@ -2699,7 +2716,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[24].Exporter = func(v any, i int) any { - switch v := v.(*ListPermissionsReq); i { + switch v := v.(*ListPermissionsRes); i { case 0: return &v.state case 1: @@ -2711,7 +2728,7 @@ func file_auth_proto_init() { } } file_auth_proto_msgTypes[25].Exporter = func(v any, i int) any { - switch v := v.(*ListPermissionsRes); i { + switch v := v.(*DeleteEntityPoliciesReq); i { case 0: return &v.state case 1: diff --git a/auth.proto b/auth.proto index 9fd2d5bda2..9f937cba8e 100644 --- a/auth.proto +++ b/auth.proto @@ -23,8 +23,8 @@ service AuthService { rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {} rpc AddPolicy(AddPolicyReq) returns (AddPolicyRes) {} rpc AddPolicies(AddPoliciesReq) returns (AddPoliciesRes) {} - rpc DeletePolicyFilter(DeletePolicyFilterReq) returns (DeletePolicyFilterRes) {} - rpc DeletePolicies(DeletePoliciesReq) returns (DeletePoliciesRes) {} + rpc DeletePolicyFilter(DeletePolicyFilterReq) returns (DeletePolicyRes) {} + rpc DeletePolicies(DeletePoliciesReq) returns (DeletePolicyRes) {} rpc ListObjects(ListObjectsReq) returns (ListObjectsRes) {} rpc ListAllObjects(ListObjectsReq) returns (ListObjectsRes) {} rpc CountObjects(CountObjectsReq) returns (CountObjectsRes) {} @@ -32,6 +32,7 @@ service AuthService { rpc ListAllSubjects(ListSubjectsReq) returns (ListSubjectsRes) {} rpc CountSubjects(CountSubjectsReq) returns (CountSubjectsRes) {} rpc ListPermissions(ListPermissionsReq) returns (ListPermissionsRes) {} + rpc DeleteEntityPolicies(DeleteEntityPoliciesReq) returns (DeletePolicyRes) {} } // If a token is not carrying any information itself, the type @@ -115,8 +116,6 @@ message DeletePolicyFilterReq { string object_type = 10; } -message DeletePolicyFilterRes { bool deleted = 1; } - message DeletePoliciesReq { repeated DeletePolicyReq deletePoliciesReq = 1; } @@ -134,7 +133,7 @@ message DeletePolicyReq { string object_type = 10; } -message DeletePoliciesRes { bool deleted = 1; } +message DeletePolicyRes { bool deleted = 1; } message ListObjectsReq { string domain = 1; @@ -219,3 +218,8 @@ message ListPermissionsRes { string object_type = 6; repeated string permissions = 7; } + +message DeleteEntityPoliciesReq{ + string entity_type = 1; + string id = 2; +} diff --git a/auth/api/grpc/client.go b/auth/api/grpc/client.go index 7945ddef61..c81d605c88 100644 --- a/auth/api/grpc/client.go +++ b/auth/api/grpc/client.go @@ -24,22 +24,23 @@ const svcName = "magistrala.AuthService" var _ magistrala.AuthServiceClient = (*grpcClient)(nil) type grpcClient struct { - issue endpoint.Endpoint - refresh endpoint.Endpoint - identify endpoint.Endpoint - authorize endpoint.Endpoint - addPolicy endpoint.Endpoint - addPolicies endpoint.Endpoint - deletePolicyFilter endpoint.Endpoint - deletePolicies endpoint.Endpoint - listObjects endpoint.Endpoint - listAllObjects endpoint.Endpoint - countObjects endpoint.Endpoint - listSubjects endpoint.Endpoint - listAllSubjects endpoint.Endpoint - countSubjects endpoint.Endpoint - listPermissions endpoint.Endpoint - timeout time.Duration + issue endpoint.Endpoint + refresh endpoint.Endpoint + identify endpoint.Endpoint + authorize endpoint.Endpoint + addPolicy endpoint.Endpoint + addPolicies endpoint.Endpoint + deletePolicyFilter endpoint.Endpoint + deletePolicies endpoint.Endpoint + listObjects endpoint.Endpoint + listAllObjects endpoint.Endpoint + countObjects endpoint.Endpoint + listSubjects endpoint.Endpoint + listAllSubjects endpoint.Endpoint + countSubjects endpoint.Endpoint + listPermissions endpoint.Endpoint + deleteEntityPolicies endpoint.Endpoint + timeout time.Duration } // NewClient returns new gRPC client instance. @@ -99,7 +100,7 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ "DeletePolicyFilter", encodeDeletePolicyFilterRequest, decodeDeletePolicyFilterResponse, - magistrala.DeletePolicyFilterRes{}, + magistrala.DeletePolicyRes{}, ).Endpoint(), deletePolicies: kitgrpc.NewClient( conn, @@ -107,7 +108,7 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ "DeletePolicies", encodeDeletePoliciesRequest, decodeDeletePoliciesResponse, - magistrala.DeletePoliciesRes{}, + magistrala.DeletePolicyRes{}, ).Endpoint(), listObjects: kitgrpc.NewClient( conn, @@ -165,6 +166,14 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ decodeListPermissionsResponse, magistrala.ListPermissionsRes{}, ).Endpoint(), + deleteEntityPolicies: kitgrpc.NewClient( + conn, + svcName, + "DeleteEntityPolicies", + encodeDeleteEntityPoliciesRequest, + decodeDeleteEntityPoliciesResponse, + magistrala.DeletePolicyRes{}, + ).Endpoint(), timeout: timeout, } @@ -379,7 +388,7 @@ func encodeAddPoliciesRequest(_ context.Context, grpcReq interface{}) (interface return &magistrala.AddPoliciesReq{AddPoliciesReq: addPolicies}, nil } -func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) { +func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { ctx, cancel := context.WithTimeout(ctx, client.timeout) defer cancel() @@ -395,16 +404,16 @@ func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala. Object: in.GetObject(), }) if err != nil { - return &magistrala.DeletePolicyFilterRes{}, decodeError(err) + return &magistrala.DeletePolicyRes{}, decodeError(err) } - dpr := res.(deletePolicyFilterRes) - return &magistrala.DeletePolicyFilterRes{Deleted: dpr.deleted}, nil + dpr := res.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil } func decodeDeletePolicyFilterResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { - res := grpcRes.(*magistrala.DeletePolicyFilterRes) - return deletePolicyFilterRes{deleted: res.GetDeleted()}, nil + res := grpcRes.(*magistrala.DeletePolicyRes) + return deletePolicyRes{deleted: res.GetDeleted()}, nil } func encodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { @@ -422,7 +431,7 @@ func encodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (in }, nil } -func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) { +func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { ctx, cancel := context.WithTimeout(ctx, client.timeout) defer cancel() r := policiesReq{} @@ -444,16 +453,16 @@ func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.Dele } res, err := client.deletePolicies(ctx, r) if err != nil { - return &magistrala.DeletePoliciesRes{}, decodeError(err) + return &magistrala.DeletePolicyRes{}, decodeError(err) } - dpr := res.(deletePoliciesRes) - return &magistrala.DeletePoliciesRes{Deleted: dpr.deleted}, nil + dpr := res.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil } func decodeDeletePoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { - res := grpcRes.(*magistrala.DeletePoliciesRes) - return deletePoliciesRes{deleted: res.GetDeleted()}, nil + res := grpcRes.(*magistrala.DeletePolicyRes) + return deletePolicyRes{deleted: res.GetDeleted()}, nil } func encodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { @@ -730,6 +739,35 @@ func encodeListPermissionsRequest(_ context.Context, grpcReq interface{}) (inter }, nil } +func (client grpcClient) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { + ctx, cancel := context.WithTimeout(ctx, client.timeout) + defer cancel() + + res, err := client.deleteEntityPolicies(ctx, deleteEntityPoliciesReq{ + EntityType: in.GetEntityType(), + ID: in.GetId(), + }) + if err != nil { + return &magistrala.DeletePolicyRes{}, decodeError(err) + } + + dpr := res.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil +} + +func decodeDeleteEntityPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { + res := grpcRes.(*magistrala.DeletePolicyRes) + return deletePolicyRes{deleted: res.GetDeleted()}, nil +} + +func encodeDeleteEntityPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { + req := grpcReq.(deleteEntityPoliciesReq) + return &magistrala.DeleteEntityPoliciesReq{ + EntityType: req.EntityType, + Id: req.ID, + }, nil +} + func decodeError(err error) error { if st, ok := status.FromError(err); ok { switch st.Code() { diff --git a/auth/api/grpc/endpoint.go b/auth/api/grpc/endpoint.go index 6aa356a215..47e9d78f42 100644 --- a/auth/api/grpc/endpoint.go +++ b/auth/api/grpc/endpoint.go @@ -155,7 +155,7 @@ func deletePolicyFilterEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(policyReq) if err := req.validate(); err != nil { - return deletePolicyFilterRes{}, err + return deletePolicyRes{}, err } err := svc.DeletePolicyFilter(ctx, auth.PolicyReq{ @@ -170,9 +170,9 @@ func deletePolicyFilterEndpoint(svc auth.Service) endpoint.Endpoint { Object: req.Object, }) if err != nil { - return deletePolicyFilterRes{}, err + return deletePolicyRes{}, err } - return deletePolicyFilterRes{deleted: true}, nil + return deletePolicyRes{deleted: true}, nil } } @@ -180,7 +180,7 @@ func deletePoliciesEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { reqs := request.(policiesReq) if err := reqs.validate(); err != nil { - return deletePoliciesRes{}, err + return deletePolicyRes{}, err } prs := []auth.PolicyReq{} @@ -200,9 +200,9 @@ func deletePoliciesEndpoint(svc auth.Service) endpoint.Endpoint { } if err := svc.DeletePolicies(ctx, prs); err != nil { - return deletePoliciesRes{}, err + return deletePolicyRes{}, err } - return deletePoliciesRes{deleted: true}, nil + return deletePolicyRes{deleted: true}, nil } } @@ -349,3 +349,18 @@ func listPermissionsEndpoint(svc auth.Service) endpoint.Endpoint { }, nil } } + +func deleteEntityPoliciesEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(deleteEntityPoliciesReq) + if err := req.validate(); err != nil { + return deletePolicyRes{}, err + } + + if err := svc.DeleteEntityPolicies(ctx, req.EntityType, req.ID); err != nil { + return deletePolicyRes{}, err + } + + return deletePolicyRes{deleted: true}, nil + } +} diff --git a/auth/api/grpc/endpoint_test.go b/auth/api/grpc/endpoint_test.go index cabb519717..aff4f0bcfa 100644 --- a/auth/api/grpc/endpoint_test.go +++ b/auth/api/grpc/endpoint_test.go @@ -471,7 +471,7 @@ func TestDeletePolicyFilter(t *testing.T) { desc string token string deletePolicyFilterReq *magistrala.DeletePolicyFilterReq - deletePolicyFilterRes *magistrala.DeletePolicyFilterRes + deletePolicyFilterRes *magistrala.DeletePolicyRes err error }{ { @@ -485,7 +485,7 @@ func TestDeletePolicyFilter(t *testing.T) { Relation: readRelation, Permission: readRelation, }, - deletePolicyFilterRes: &magistrala.DeletePolicyFilterRes{Deleted: true}, + deletePolicyFilterRes: &magistrala.DeletePolicyRes{Deleted: true}, err: nil, }, { @@ -499,7 +499,7 @@ func TestDeletePolicyFilter(t *testing.T) { Relation: readRelation, Permission: readRelation, }, - deletePolicyFilterRes: &magistrala.DeletePolicyFilterRes{Deleted: false}, + deletePolicyFilterRes: &magistrala.DeletePolicyRes{Deleted: false}, err: svcerr.ErrAuthorization, }, } @@ -524,7 +524,7 @@ func TestDeletePolicies(t *testing.T) { desc string token string deletePoliciesReq *magistrala.DeletePoliciesReq - deletePoliciesRes *magistrala.DeletePoliciesRes + deletePoliciesRes *magistrala.DeletePolicyRes err error }{ { @@ -542,7 +542,7 @@ func TestDeletePolicies(t *testing.T) { }, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesRes: &magistrala.DeletePolicyRes{Deleted: true}, err: nil, }, { @@ -560,7 +560,7 @@ func TestDeletePolicies(t *testing.T) { }, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{Deleted: false}, + deletePoliciesRes: &magistrala.DeletePolicyRes{Deleted: false}, err: svcerr.ErrAuthorization, }, } @@ -1010,3 +1010,53 @@ func TestListPermissions(t *testing.T) { svcCall.Unset() } } + +func TestDeleteEntityPolicies(t *testing.T) { + conn, err := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err)) + client := grpcapi.NewClient(conn, time.Second) + + cases := []struct { + desc string + token string + deleteEntityPoliciesReq *magistrala.DeleteEntityPoliciesReq + deletePolicyRes *magistrala.DeletePolicyRes + err error + }{ + { + desc: "delete valid req", + token: validToken, + deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{ + Id: id, + EntityType: usersType, + }, + deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: true}, + err: nil, + }, + { + desc: "delete invalid req with invalid token", + token: inValidToken, + deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{ + EntityType: usersType, + }, + deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false}, + err: apiutil.ErrMissingID, + }, + { + desc: "delete invalid req with invalid token", + token: inValidToken, + deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{ + Id: id, + }, + deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false}, + err: apiutil.ErrMissingPolicyEntityType, + }, + } + for _, tc := range cases { + repoCall := svc.On("DeleteEntityPolicies", mock.Anything, tc.deleteEntityPoliciesReq.EntityType, tc.deleteEntityPoliciesReq.Id).Return(tc.err) + dpr, err := client.DeleteEntityPolicies(context.Background(), tc.deleteEntityPoliciesReq) + assert.Equal(t, tc.deletePolicyRes.GetDeleted(), dpr.GetDeleted(), fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.deletePolicyRes.GetDeleted(), dpr.GetDeleted())) + assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) + repoCall.Unset() + } +} diff --git a/auth/api/grpc/requests.go b/auth/api/grpc/requests.go index 3b3479aae6..ab6fd6e286 100644 --- a/auth/api/grpc/requests.go +++ b/auth/api/grpc/requests.go @@ -158,3 +158,19 @@ type listPermissionsReq struct { Object string FilterPermissions []string } + +type deleteEntityPoliciesReq struct { + EntityType string + ID string +} + +func (req deleteEntityPoliciesReq) validate() error { + if req.ID == "" { + return apiutil.ErrMissingID + } + if req.EntityType == "" { + return apiutil.ErrMissingPolicyEntityType + } + + return nil +} diff --git a/auth/api/grpc/responses.go b/auth/api/grpc/responses.go index 20ab65f793..8685826e1c 100644 --- a/auth/api/grpc/responses.go +++ b/auth/api/grpc/responses.go @@ -27,11 +27,7 @@ type addPoliciesRes struct { added bool } -type deletePolicyFilterRes struct { - deleted bool -} - -type deletePoliciesRes struct { +type deletePolicyRes struct { deleted bool } diff --git a/auth/api/grpc/server.go b/auth/api/grpc/server.go index a4198cca77..1c23473f25 100644 --- a/auth/api/grpc/server.go +++ b/auth/api/grpc/server.go @@ -20,21 +20,22 @@ var _ magistrala.AuthServiceServer = (*grpcServer)(nil) type grpcServer struct { magistrala.UnimplementedAuthServiceServer - issue kitgrpc.Handler - refresh kitgrpc.Handler - identify kitgrpc.Handler - authorize kitgrpc.Handler - addPolicy kitgrpc.Handler - addPolicies kitgrpc.Handler - deletePolicyFilter kitgrpc.Handler - deletePolicies kitgrpc.Handler - listObjects kitgrpc.Handler - listAllObjects kitgrpc.Handler - countObjects kitgrpc.Handler - listSubjects kitgrpc.Handler - listAllSubjects kitgrpc.Handler - countSubjects kitgrpc.Handler - listPermissions kitgrpc.Handler + issue kitgrpc.Handler + refresh kitgrpc.Handler + identify kitgrpc.Handler + authorize kitgrpc.Handler + addPolicy kitgrpc.Handler + addPolicies kitgrpc.Handler + deletePolicyFilter kitgrpc.Handler + deletePolicies kitgrpc.Handler + listObjects kitgrpc.Handler + listAllObjects kitgrpc.Handler + countObjects kitgrpc.Handler + listSubjects kitgrpc.Handler + listAllSubjects kitgrpc.Handler + countSubjects kitgrpc.Handler + listPermissions kitgrpc.Handler + deleteEntityPolicies kitgrpc.Handler } // NewServer returns new AuthServiceServer instance. @@ -115,6 +116,11 @@ func NewServer(svc auth.Service) magistrala.AuthServiceServer { decodeListPermissionsRequest, encodeListPermissionsResponse, ), + deleteEntityPolicies: kitgrpc.NewServer( + (deleteEntityPoliciesEndpoint(svc)), + decodeDeleteEntityPoliciesRequest, + encodeDeleteEntityPoliciesResponse, + ), } } @@ -166,20 +172,20 @@ func (s *grpcServer) AddPolicies(ctx context.Context, req *magistrala.AddPolicie return res.(*magistrala.AddPoliciesRes), nil } -func (s *grpcServer) DeletePolicyFilter(ctx context.Context, req *magistrala.DeletePolicyFilterReq) (*magistrala.DeletePolicyFilterRes, error) { +func (s *grpcServer) DeletePolicyFilter(ctx context.Context, req *magistrala.DeletePolicyFilterReq) (*magistrala.DeletePolicyRes, error) { _, res, err := s.deletePolicyFilter.ServeGRPC(ctx, req) if err != nil { return nil, encodeError(err) } - return res.(*magistrala.DeletePolicyFilterRes), nil + return res.(*magistrala.DeletePolicyRes), nil } -func (s *grpcServer) DeletePolicies(ctx context.Context, req *magistrala.DeletePoliciesReq) (*magistrala.DeletePoliciesRes, error) { +func (s *grpcServer) DeletePolicies(ctx context.Context, req *magistrala.DeletePoliciesReq) (*magistrala.DeletePolicyRes, error) { _, res, err := s.deletePolicies.ServeGRPC(ctx, req) if err != nil { return nil, encodeError(err) } - return res.(*magistrala.DeletePoliciesRes), nil + return res.(*magistrala.DeletePolicyRes), nil } func (s *grpcServer) ListObjects(ctx context.Context, req *magistrala.ListObjectsReq) (*magistrala.ListObjectsRes, error) { @@ -238,6 +244,14 @@ func (s *grpcServer) ListPermissions(ctx context.Context, req *magistrala.ListPe return res.(*magistrala.ListPermissionsRes), nil } +func (s *grpcServer) DeleteEntityPolicies(ctx context.Context, req *magistrala.DeleteEntityPoliciesReq) (*magistrala.DeletePolicyRes, error) { + _, res, err := s.deleteEntityPolicies.ServeGRPC(ctx, req) + if err != nil { + return nil, encodeError(err) + } + return res.(*magistrala.DeletePolicyRes), nil +} + func decodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { req := grpcReq.(*magistrala.IssueReq) return issueReq{ @@ -351,8 +365,8 @@ func decodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (in } func encodeDeletePolicyFilterResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { - res := grpcRes.(deletePolicyFilterRes) - return &magistrala.DeletePolicyFilterRes{Deleted: res.deleted}, nil + res := grpcRes.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil } func decodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { @@ -375,8 +389,8 @@ func decodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interf } func encodeDeletePoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { - res := grpcRes.(deletePoliciesRes) - return &magistrala.DeletePoliciesRes{Deleted: res.deleted}, nil + res := grpcRes.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil } func decodeListObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { @@ -479,6 +493,19 @@ func encodeListPermissionsResponse(_ context.Context, grpcRes interface{}) (inte }, nil } +func decodeDeleteEntityPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { + req := grpcReq.(*magistrala.DeleteEntityPoliciesReq) + return deleteEntityPoliciesReq{ + EntityType: req.GetEntityType(), + ID: req.GetId(), + }, nil +} + +func encodeDeleteEntityPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { + res := grpcRes.(deletePolicyRes) + return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil +} + func encodeError(err error) error { switch { case errors.Contains(err, nil): diff --git a/auth/api/logging.go b/auth/api/logging.go index b575e4627a..5ab6dacb26 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -490,3 +490,20 @@ func (lm *loggingMiddleware) ListUserDomains(ctx context.Context, token, userID }(time.Now()) return lm.svc.ListUserDomains(ctx, token, userID, page) } + +func (lm *loggingMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("entity_type", entityType), + slog.String("id", id), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Delete entity policies failed to complete successfully", args...) + return + } + lm.logger.Info("Delete entity policies completed successfully", args...) + }(time.Now()) + return lm.svc.DeleteEntityPolicies(ctx, entityType, id) +} diff --git a/auth/api/metrics.go b/auth/api/metrics.go index 0dc089ed02..e1309b8283 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -239,3 +239,11 @@ func (ms *metricsMiddleware) ListUserDomains(ctx context.Context, token, userID }(time.Now()) return ms.svc.ListUserDomains(ctx, token, userID, page) } + +func (ms *metricsMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) error { + defer func(begin time.Time) { + ms.counter.With("method", "delete_entity_policies").Add(1) + ms.latency.With("method", "delete_entity_policies").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.DeleteEntityPolicies(ctx, entityType, id) +} diff --git a/auth/domains.go b/auth/domains.go index b6b638d384..6474470459 100644 --- a/auth/domains.go +++ b/auth/domains.go @@ -199,4 +199,7 @@ type DomainsRepository interface { // CheckPolicy check policy in domains database. CheckPolicy(ctx context.Context, pc Policy) error + + // DeleteUserPolicies deletes user policies from domains database. + DeleteUserPolicies(ctx context.Context, id string) (err error) } diff --git a/auth/events/streams.go b/auth/events/streams.go index 4839ad6463..52da7bf715 100644 --- a/auth/events/streams.go +++ b/auth/events/streams.go @@ -227,6 +227,10 @@ func (es *eventStore) DeletePolicyFilter(ctx context.Context, pr auth.PolicyReq) return es.svc.DeletePolicyFilter(ctx, pr) } +func (es *eventStore) DeleteEntityPolicies(ctx context.Context, entityType, id string) error { + return es.svc.DeleteEntityPolicies(ctx, entityType, id) +} + func (es *eventStore) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error { return es.svc.DeletePolicies(ctx, prs) } diff --git a/auth/mocks/auth_client.go b/auth/mocks/auth_client.go index 6326855b9a..981edecbfa 100644 --- a/auth/mocks/auth_client.go +++ b/auth/mocks/auth_client.go @@ -71,16 +71,16 @@ func (m *AuthClient) AddPolicies(ctx context.Context, in *magistrala.AddPolicies return ret.Get(0).(*magistrala.AddPoliciesRes), ret.Error(1) } -func (m *AuthClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) { +func (m *AuthClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { ret := m.Called(ctx, in) - return ret.Get(0).(*magistrala.DeletePolicyFilterRes), ret.Error(1) + return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1) } -func (m *AuthClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) { +func (m *AuthClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { ret := m.Called(ctx, in) - return ret.Get(0).(*magistrala.DeletePoliciesRes), ret.Error(1) + return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1) } func (m *AuthClient) ListObjects(ctx context.Context, in *magistrala.ListObjectsReq, opts ...grpc.CallOption) (*magistrala.ListObjectsRes, error) { @@ -124,3 +124,9 @@ func (m *AuthClient) ListPermissions(ctx context.Context, in *magistrala.ListPer return ret.Get(0).(*magistrala.ListPermissionsRes), ret.Error(1) } + +func (m *AuthClient) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { + ret := m.Called(ctx, in) + + return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1) +} diff --git a/auth/mocks/authz.go b/auth/mocks/authz.go index b709005c79..0e3991597e 100644 --- a/auth/mocks/authz.go +++ b/auth/mocks/authz.go @@ -127,6 +127,24 @@ func (_m *Authz) CountSubjects(ctx context.Context, pr auth.PolicyReq) (uint64, return r0, r1 } +// DeleteEntityPolicies provides a mock function with given fields: ctx, entityType, id +func (_m *Authz) DeleteEntityPolicies(ctx context.Context, entityType string, id string) error { + ret := _m.Called(ctx, entityType, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteEntityPolicies") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, entityType, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DeletePolicies provides a mock function with given fields: ctx, prs func (_m *Authz) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error { ret := _m.Called(ctx, prs) diff --git a/auth/mocks/domains.go b/auth/mocks/domains.go index 53d2e1674e..c9bc09c95f 100644 --- a/auth/mocks/domains.go +++ b/auth/mocks/domains.go @@ -78,6 +78,24 @@ func (_m *DomainsRepository) DeletePolicies(ctx context.Context, pcs ...auth.Pol return r0 } +// DeleteUserPolicies provides a mock function with given fields: ctx, id +func (_m *DomainsRepository) DeleteUserPolicies(ctx context.Context, id string) error { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteUserPolicies") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // ListDomains provides a mock function with given fields: ctx, pm func (_m *DomainsRepository) ListDomains(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) { ret := _m.Called(ctx, pm) diff --git a/auth/mocks/service.go b/auth/mocks/service.go index 885dc06f15..b5be1bb0fb 100644 --- a/auth/mocks/service.go +++ b/auth/mocks/service.go @@ -201,6 +201,24 @@ func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain return r0, r1 } +// DeleteEntityPolicies provides a mock function with given fields: ctx, entityType, id +func (_m *Service) DeleteEntityPolicies(ctx context.Context, entityType string, id string) error { + ret := _m.Called(ctx, entityType, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteEntityPolicies") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, entityType, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DeletePolicies provides a mock function with given fields: ctx, prs func (_m *Service) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error { ret := _m.Called(ctx, prs) diff --git a/auth/policies.go b/auth/policies.go index 245cf3de7a..cc89e5bb09 100644 --- a/auth/policies.go +++ b/auth/policies.go @@ -170,6 +170,9 @@ type Authz interface { // ListPermissions lists permission betweeen given subject and object . ListPermissions(ctx context.Context, pr PolicyReq, filterPermission []string) (Permissions, error) + + // DeleteEntityPolicies deletes all policies for the given entity. + DeleteEntityPolicies(ctx context.Context, entityType, id string) error } // PolicyAgent facilitates the communication to authorization diff --git a/auth/postgres/domains.go b/auth/postgres/domains.go index 1d4af9e2ef..9952492e36 100644 --- a/auth/postgres/domains.go +++ b/auth/postgres/domains.go @@ -390,6 +390,16 @@ func (repo domainRepo) DeletePolicies(ctx context.Context, pcs ...auth.Policy) ( return tx.Commit() } +func (repo domainRepo) DeleteUserPolicies(ctx context.Context, id string) (err error) { + q := "DELETE FROM policies WHERE subject_id = $1;" + + if _, err := repo.db.ExecContext(ctx, q, id); err != nil { + return postgres.HandleError(repoerr.ErrRemoveEntity, err) + } + + return nil +} + func (repo domainRepo) processRows(rows *sqlx.Rows) ([]auth.Domain, error) { var items []auth.Domain for rows.Next() { diff --git a/auth/postgres/domains_test.go b/auth/postgres/domains_test.go index 0116683cbd..b6053d776e 100644 --- a/auth/postgres/domains_test.go +++ b/auth/postgres/domains_test.go @@ -1091,3 +1091,58 @@ func TestCheckPolicy(t *testing.T) { assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err)) } } + +func TestDeleteUserPolicies(t *testing.T) { + repo := postgres.NewDomainRepository(database) + + domain := auth.Domain{ + ID: domainID, + Name: "test", + Alias: "test", + Tags: []string{"test"}, + Metadata: map[string]interface{}{ + "test": "test", + }, + CreatedBy: userID, + UpdatedBy: userID, + Status: auth.EnabledStatus, + Permission: "admin", + } + + policy := auth.Policy{ + SubjectType: auth.UserType, + SubjectID: userID, + SubjectRelation: "admin", + Relation: "admin", + ObjectType: auth.DomainType, + ObjectID: domainID, + } + + _, err := repo.Save(context.Background(), domain) + require.Nil(t, err, fmt.Sprintf("failed to save domain %s", domain.ID)) + + err = repo.SavePolicies(context.Background(), policy) + require.Nil(t, err, fmt.Sprintf("failed to save policy %s", policy.SubjectID)) + + cases := []struct { + desc string + id string + err error + }{ + { + desc: "delete valid user policy", + id: userID, + err: nil, + }, + { + desc: "delete invalid user policy", + id: inValid, + err: nil, + }, + } + + for _, tc := range cases { + err := repo.DeleteUserPolicies(context.Background(), tc.id) + assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err)) + } +} diff --git a/auth/service.go b/auth/service.go index c4bc430558..c52f1b9f06 100644 --- a/auth/service.go +++ b/auth/service.go @@ -14,7 +14,10 @@ import ( svcerr "github.com/absmach/magistrala/pkg/errors/service" ) -const recoveryDuration = 5 * time.Minute +const ( + recoveryDuration = 5 * time.Minute + defLimit = 100 +) var ( // ErrExpiry indicates that the token is expired. @@ -32,6 +35,8 @@ var ( errRollbackPolicy = errors.New("failed to rollback policy") errRemoveLocalPolicy = errors.New("failed to remove from local policy copy") errRemovePolicyEngine = errors.New("failed to remove from policy engine") + // errInvalidEntityType indicates invalid entity type. + errInvalidEntityType = errors.New("invalid entity type") ) var ( @@ -999,3 +1004,71 @@ func DecodeDomainUserID(domainUserID string) (string, string) { return "", "" } } + +func (svc service) DeleteEntityPolicies(ctx context.Context, entityType, id string) (err error) { + switch entityType { + case ThingType: + req := PolicyReq{ + Object: id, + ObjectType: ThingType, + } + + return svc.DeletePolicyFilter(ctx, req) + case UserType: + domainsPage, err := svc.domains.ListDomains(ctx, Page{SubjectID: id, Limit: defLimit}) + if err != nil { + return err + } + + if domainsPage.Total > defLimit { + for i := defLimit; i < int(domainsPage.Total); i += defLimit { + page := Page{SubjectID: id, Offset: uint64(i), Limit: defLimit} + dp, err := svc.domains.ListDomains(ctx, page) + if err != nil { + return err + } + domainsPage.Domains = append(domainsPage.Domains, dp.Domains...) + } + } + + for _, domain := range domainsPage.Domains { + policy := PolicyReq{ + Subject: EncodeDomainUserID(domain.ID, id), + SubjectType: UserType, + } + if err := svc.agent.DeletePolicyFilter(ctx, policy); err != nil { + return err + } + } + + req := PolicyReq{ + Subject: id, + SubjectType: UserType, + } + if err := svc.agent.DeletePolicyFilter(ctx, req); err != nil { + return err + } + + if err := svc.domains.DeleteUserPolicies(ctx, id); err != nil { + return err + } + + return nil + case GroupType: + req := PolicyReq{ + SubjectType: GroupType, + Subject: id, + } + if err := svc.DeletePolicyFilter(ctx, req); err != nil { + return err + } + + req = PolicyReq{ + Object: id, + ObjectType: GroupType, + } + return svc.DeletePolicyFilter(ctx, req) + default: + return errInvalidEntityType + } +} diff --git a/auth/spicedb/policies.go b/auth/spicedb/policies.go index f950fc908b..99f5118790 100644 --- a/auth/spicedb/policies.go +++ b/auth/spicedb/policies.go @@ -158,16 +158,27 @@ func (pa *policyAgent) DeletePolicyFilter(ctx context.Context, pr auth.PolicyReq RelationshipFilter: &v1.RelationshipFilter{ ResourceType: pr.ObjectType, OptionalResourceId: pr.Object, - OptionalRelation: pr.Relation, - OptionalSubjectFilter: &v1.SubjectFilter{ - OptionalSubjectId: pr.Subject, - SubjectType: pr.SubjectType, - OptionalRelation: &v1.SubjectFilter_RelationFilter{ - Relation: pr.SubjectRelation, - }, - }, }, } + + if pr.Relation != "" { + req.RelationshipFilter.OptionalRelation = pr.Relation + } + + if pr.SubjectType != "" { + req.RelationshipFilter.OptionalSubjectFilter = &v1.SubjectFilter{ + SubjectType: pr.SubjectType, + } + if pr.Subject != "" { + req.RelationshipFilter.OptionalSubjectFilter.OptionalSubjectId = pr.Subject + } + if pr.SubjectRelation != "" { + req.RelationshipFilter.OptionalSubjectFilter.OptionalRelation = &v1.SubjectFilter_RelationFilter{ + Relation: pr.SubjectRelation, + } + } + } + if _, err := pa.permissionClient.DeleteRelationships(ctx, req); err != nil { return errors.Wrap(errRemovePolicies, handleSpicedbError(err)) } diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 9c5cce788b..74152aa646 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -303,3 +303,12 @@ func (tm *tracingMiddleware) ListUserDomains(ctx context.Context, token, userID defer span.End() return tm.svc.ListUserDomains(ctx, token, userID, p) } + +func (tm *tracingMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) error { + ctx, span := tm.tracer.Start(ctx, "delete_entity_policies", trace.WithAttributes( + attribute.String("entity_type", entityType), + attribute.String("id", id), + )) + defer span.End() + return tm.svc.DeleteEntityPolicies(ctx, entityType, id) +} diff --git a/auth_grpc.pb.go b/auth_grpc.pb.go index 01d772b912..c6350d3a48 100644 --- a/auth_grpc.pb.go +++ b/auth_grpc.pb.go @@ -123,21 +123,22 @@ var AuthzService_ServiceDesc = grpc.ServiceDesc{ } const ( - AuthService_Issue_FullMethodName = "/magistrala.AuthService/Issue" - AuthService_Refresh_FullMethodName = "/magistrala.AuthService/Refresh" - AuthService_Identify_FullMethodName = "/magistrala.AuthService/Identify" - AuthService_Authorize_FullMethodName = "/magistrala.AuthService/Authorize" - AuthService_AddPolicy_FullMethodName = "/magistrala.AuthService/AddPolicy" - AuthService_AddPolicies_FullMethodName = "/magistrala.AuthService/AddPolicies" - AuthService_DeletePolicyFilter_FullMethodName = "/magistrala.AuthService/DeletePolicyFilter" - AuthService_DeletePolicies_FullMethodName = "/magistrala.AuthService/DeletePolicies" - AuthService_ListObjects_FullMethodName = "/magistrala.AuthService/ListObjects" - AuthService_ListAllObjects_FullMethodName = "/magistrala.AuthService/ListAllObjects" - AuthService_CountObjects_FullMethodName = "/magistrala.AuthService/CountObjects" - AuthService_ListSubjects_FullMethodName = "/magistrala.AuthService/ListSubjects" - AuthService_ListAllSubjects_FullMethodName = "/magistrala.AuthService/ListAllSubjects" - AuthService_CountSubjects_FullMethodName = "/magistrala.AuthService/CountSubjects" - AuthService_ListPermissions_FullMethodName = "/magistrala.AuthService/ListPermissions" + AuthService_Issue_FullMethodName = "/magistrala.AuthService/Issue" + AuthService_Refresh_FullMethodName = "/magistrala.AuthService/Refresh" + AuthService_Identify_FullMethodName = "/magistrala.AuthService/Identify" + AuthService_Authorize_FullMethodName = "/magistrala.AuthService/Authorize" + AuthService_AddPolicy_FullMethodName = "/magistrala.AuthService/AddPolicy" + AuthService_AddPolicies_FullMethodName = "/magistrala.AuthService/AddPolicies" + AuthService_DeletePolicyFilter_FullMethodName = "/magistrala.AuthService/DeletePolicyFilter" + AuthService_DeletePolicies_FullMethodName = "/magistrala.AuthService/DeletePolicies" + AuthService_ListObjects_FullMethodName = "/magistrala.AuthService/ListObjects" + AuthService_ListAllObjects_FullMethodName = "/magistrala.AuthService/ListAllObjects" + AuthService_CountObjects_FullMethodName = "/magistrala.AuthService/CountObjects" + AuthService_ListSubjects_FullMethodName = "/magistrala.AuthService/ListSubjects" + AuthService_ListAllSubjects_FullMethodName = "/magistrala.AuthService/ListAllSubjects" + AuthService_CountSubjects_FullMethodName = "/magistrala.AuthService/CountSubjects" + AuthService_ListPermissions_FullMethodName = "/magistrala.AuthService/ListPermissions" + AuthService_DeleteEntityPolicies_FullMethodName = "/magistrala.AuthService/DeleteEntityPolicies" ) // AuthServiceClient is the client API for AuthService service. @@ -153,8 +154,8 @@ type AuthServiceClient interface { Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error) AddPolicy(ctx context.Context, in *AddPolicyReq, opts ...grpc.CallOption) (*AddPolicyRes, error) AddPolicies(ctx context.Context, in *AddPoliciesReq, opts ...grpc.CallOption) (*AddPoliciesRes, error) - DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyFilterRes, error) - DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePoliciesRes, error) + DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) + DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) ListObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error) ListAllObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error) CountObjects(ctx context.Context, in *CountObjectsReq, opts ...grpc.CallOption) (*CountObjectsRes, error) @@ -162,6 +163,7 @@ type AuthServiceClient interface { ListAllSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error) CountSubjects(ctx context.Context, in *CountSubjectsReq, opts ...grpc.CallOption) (*CountSubjectsRes, error) ListPermissions(ctx context.Context, in *ListPermissionsReq, opts ...grpc.CallOption) (*ListPermissionsRes, error) + DeleteEntityPolicies(ctx context.Context, in *DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) } type authServiceClient struct { @@ -232,9 +234,9 @@ func (c *authServiceClient) AddPolicies(ctx context.Context, in *AddPoliciesReq, return out, nil } -func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyFilterRes, error) { +func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeletePolicyFilterRes) + out := new(DeletePolicyRes) err := c.cc.Invoke(ctx, AuthService_DeletePolicyFilter_FullMethodName, in, out, cOpts...) if err != nil { return nil, err @@ -242,9 +244,9 @@ func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePo return out, nil } -func (c *authServiceClient) DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePoliciesRes, error) { +func (c *authServiceClient) DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeletePoliciesRes) + out := new(DeletePolicyRes) err := c.cc.Invoke(ctx, AuthService_DeletePolicies_FullMethodName, in, out, cOpts...) if err != nil { return nil, err @@ -322,6 +324,16 @@ func (c *authServiceClient) ListPermissions(ctx context.Context, in *ListPermiss return out, nil } +func (c *authServiceClient) DeleteEntityPolicies(ctx context.Context, in *DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeletePolicyRes) + err := c.cc.Invoke(ctx, AuthService_DeleteEntityPolicies_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer // for forward compatibility @@ -335,8 +347,8 @@ type AuthServiceServer interface { Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error) AddPolicy(context.Context, *AddPolicyReq) (*AddPolicyRes, error) AddPolicies(context.Context, *AddPoliciesReq) (*AddPoliciesRes, error) - DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyFilterRes, error) - DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePoliciesRes, error) + DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyRes, error) + DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePolicyRes, error) ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) ListAllObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) CountObjects(context.Context, *CountObjectsReq) (*CountObjectsRes, error) @@ -344,6 +356,7 @@ type AuthServiceServer interface { ListAllSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error) CountSubjects(context.Context, *CountSubjectsReq) (*CountSubjectsRes, error) ListPermissions(context.Context, *ListPermissionsReq) (*ListPermissionsRes, error) + DeleteEntityPolicies(context.Context, *DeleteEntityPoliciesReq) (*DeletePolicyRes, error) mustEmbedUnimplementedAuthServiceServer() } @@ -369,10 +382,10 @@ func (UnimplementedAuthServiceServer) AddPolicy(context.Context, *AddPolicyReq) func (UnimplementedAuthServiceServer) AddPolicies(context.Context, *AddPoliciesReq) (*AddPoliciesRes, error) { return nil, status.Errorf(codes.Unimplemented, "method AddPolicies not implemented") } -func (UnimplementedAuthServiceServer) DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyFilterRes, error) { +func (UnimplementedAuthServiceServer) DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeletePolicyFilter not implemented") } -func (UnimplementedAuthServiceServer) DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePoliciesRes, error) { +func (UnimplementedAuthServiceServer) DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePolicyRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeletePolicies not implemented") } func (UnimplementedAuthServiceServer) ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) { @@ -396,6 +409,9 @@ func (UnimplementedAuthServiceServer) CountSubjects(context.Context, *CountSubje func (UnimplementedAuthServiceServer) ListPermissions(context.Context, *ListPermissionsReq) (*ListPermissionsRes, error) { return nil, status.Errorf(codes.Unimplemented, "method ListPermissions not implemented") } +func (UnimplementedAuthServiceServer) DeleteEntityPolicies(context.Context, *DeleteEntityPoliciesReq) (*DeletePolicyRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteEntityPolicies not implemented") +} func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. @@ -679,6 +695,24 @@ func _AuthService_ListPermissions_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _AuthService_DeleteEntityPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteEntityPoliciesReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).DeleteEntityPolicies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_DeleteEntityPolicies_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).DeleteEntityPolicies(ctx, req.(*DeleteEntityPoliciesReq)) + } + return interceptor(ctx, in, info, handler) +} + // AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -746,6 +780,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListPermissions", Handler: _AuthService_ListPermissions_Handler, }, + { + MethodName: "DeleteEntityPolicies", + Handler: _AuthService_DeleteEntityPolicies_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "auth.proto", diff --git a/cli/config.go b/cli/config.go index bf9590d83c..48317663ec 100644 --- a/cli/config.go +++ b/cli/config.go @@ -45,6 +45,7 @@ type remotes struct { CertsURL string `toml:"certs_url"` InvitationsURL string `toml:"invitations_url"` JournalURL string `toml:"journal_url"` + HostURL string `toml:"host_url"` TLSVerification bool `toml:"tls_verification"` } @@ -115,6 +116,7 @@ func ParseConfig(sdkConf mgxsdk.Config) (mgxsdk.Config, error) { CertsURL: defCertsURL, InvitationsURL: defInvitationsURL, JournalURL: defJournalURL, + HostURL: defURL, TLSVerification: defTLSVerification, }, Filter: filter{ @@ -205,6 +207,10 @@ func ParseConfig(sdkConf mgxsdk.Config) (mgxsdk.Config, error) { sdkConf.JournalURL = config.Remotes.JournalURL } + if sdkConf.HostURL == "" && config.Remotes.HostURL != "" { + sdkConf.HostURL = config.Remotes.HostURL + } + sdkConf.TLSVerification = config.Remotes.TLSVerification || sdkConf.TLSVerification return sdkConf, nil diff --git a/cli/users.go b/cli/users.go index e82c991317..82c4ffcf54 100644 --- a/cli/users.go +++ b/cli/users.go @@ -338,7 +338,24 @@ var cmdUsers = []cobra.Command{ logJSON(user) }, }, - + { + Use: "delete ", + Short: "Delete user", + Long: "Delete user by id\n" + + "Usage:\n" + + "\tmagistrala-cli users delete $USERTOKEN - delete user with \n", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 2 { + logUsage(cmd.Use) + return + } + if err := sdk.DeleteUser(args[0], args[1]); err != nil { + logError(err) + return + } + logOK() + }, + }, { Use: "channels ", Short: "List channels", @@ -451,7 +468,7 @@ var cmdUsers = []cobra.Command{ // NewUsersCmd returns users command. func NewUsersCmd() *cobra.Command { cmd := cobra.Command{ - Use: "users [create | get | update | token | password | enable | disable | channels | things | groups]", + Use: "users [create | get | update | token | password | enable | disable | delete | channels | things | groups]", Short: "Users management", Long: `Users management: create accounts and tokens"`, } diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 245c8205bb..7ed42dfbb5 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -12,13 +12,10 @@ import ( "github.com/spf13/cobra" ) -const defURL string = "http://localhost" - func main() { msgContentType := string(sdk.CTJSONSenML) sdkConf := sdk.Config{ MsgContentType: sdk.ContentType(msgContentType), - HostURL: defURL, } // Root diff --git a/cmd/users/main.go b/cmd/users/main.go index e7b7659f66..f1a94f7e3e 100644 --- a/cmd/users/main.go +++ b/cmd/users/main.go @@ -64,19 +64,21 @@ const ( ) type config struct { - LogLevel string `env:"MG_USERS_LOG_LEVEL" envDefault:"info"` - AdminEmail string `env:"MG_USERS_ADMIN_EMAIL" envDefault:"admin@example.com"` - AdminPassword string `env:"MG_USERS_ADMIN_PASSWORD" envDefault:"12345678"` - PassRegexText string `env:"MG_USERS_PASS_REGEX" envDefault:"^.{8,}$"` - ResetURL string `env:"MG_TOKEN_RESET_ENDPOINT" envDefault:"/reset-request"` - JaegerURL url.URL `env:"MG_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"` - SendTelemetry bool `env:"MG_SEND_TELEMETRY" envDefault:"true"` - InstanceID string `env:"MG_USERS_INSTANCE_ID" envDefault:""` - ESURL string `env:"MG_ES_URL" envDefault:"nats://localhost:4222"` - TraceRatio float64 `env:"MG_JAEGER_TRACE_RATIO" envDefault:"1.0"` - SelfRegister bool `env:"MG_USERS_ALLOW_SELF_REGISTER" envDefault:"false"` - OAuthUIRedirectURL string `env:"MG_OAUTH_UI_REDIRECT_URL" envDefault:"http://localhost:9095/domains"` - OAuthUIErrorURL string `env:"MG_OAUTH_UI_ERROR_URL" envDefault:"http://localhost:9095/error"` + LogLevel string `env:"MG_USERS_LOG_LEVEL" envDefault:"info"` + AdminEmail string `env:"MG_USERS_ADMIN_EMAIL" envDefault:"admin@example.com"` + AdminPassword string `env:"MG_USERS_ADMIN_PASSWORD" envDefault:"12345678"` + PassRegexText string `env:"MG_USERS_PASS_REGEX" envDefault:"^.{8,}$"` + ResetURL string `env:"MG_TOKEN_RESET_ENDPOINT" envDefault:"/reset-request"` + JaegerURL url.URL `env:"MG_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"` + SendTelemetry bool `env:"MG_SEND_TELEMETRY" envDefault:"true"` + InstanceID string `env:"MG_USERS_INSTANCE_ID" envDefault:""` + ESURL string `env:"MG_ES_URL" envDefault:"nats://localhost:4222"` + TraceRatio float64 `env:"MG_JAEGER_TRACE_RATIO" envDefault:"1.0"` + SelfRegister bool `env:"MG_USERS_ALLOW_SELF_REGISTER" envDefault:"false"` + OAuthUIRedirectURL string `env:"MG_OAUTH_UI_REDIRECT_URL" envDefault:"http://localhost:9095/domains"` + OAuthUIErrorURL string `env:"MG_OAUTH_UI_ERROR_URL" envDefault:"http://localhost:9095/error"` + DeleteInterval time.Duration `env:"MG_USERS_DELETE_INTERVAL" envDefault:"24h"` + DeleteAfter time.Duration `env:"MG_USERS_DELETE_AFTER" envDefault:"720h"` PassRegex *regexp.Regexp } @@ -248,6 +250,9 @@ func newService(ctx context.Context, authClient magistrala.AuthServiceClient, db if err := createAdminPolicy(ctx, clientID, authClient); err != nil { return nil, nil, err } + + users.NewDeleteHandler(ctx, cRepo, authClient, c.DeleteInterval, c.DeleteAfter, logger) + return csvc, gsvc, err } diff --git a/config.toml b/config.toml index cfe2eb8ea8..0745847311 100644 --- a/config.toml +++ b/config.toml @@ -14,6 +14,7 @@ user_token = "" bootstrap_url = "http://localhost:9013" certs_url = "http://localhost:9019" domains_url = "http://localhost:8189" + host_url = "http://localhost" http_adapter_url = "http://localhost:8008" invitations_url = "http://localhost:9020" reader_url = "http://localhost:9011" diff --git a/docker/.env b/docker/.env index dce160fd70..4e830f9afb 100644 --- a/docker/.env +++ b/docker/.env @@ -193,6 +193,8 @@ MG_USERS_INSTANCE_ID= MG_USERS_ALLOW_SELF_REGISTER=true MG_OAUTH_UI_REDIRECT_URL=http://localhost:9095${MG_UI_PATH_PREFIX}/tokens/secure MG_OAUTH_UI_ERROR_URL=http://localhost:9095${MG_UI_PATH_PREFIX}/error +MG_USERS_DELETE_INTERVAL=24h +MG_USERS_DELETE_AFTER=720h ### Email utility MG_EMAIL_HOST=smtp.mailtrap.io diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 93cd7b01c6..c206e7529c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -449,6 +449,8 @@ services: MG_GOOGLE_STATE: ${MG_GOOGLE_STATE} MG_OAUTH_UI_REDIRECT_URL: ${MG_OAUTH_UI_REDIRECT_URL} MG_OAUTH_UI_ERROR_URL: ${MG_OAUTH_UI_ERROR_URL} + MG_USERS_DELETE_INTERVAL: ${MG_USERS_DELETE_INTERVAL} + MG_USERS_DELETE_AFTER: ${MG_USERS_DELETE_AFTER} ports: - ${MG_USERS_HTTP_PORT}:${MG_USERS_HTTP_PORT} networks: diff --git a/internal/apiutil/errors.go b/internal/apiutil/errors.go index ad19b6d67d..833f8ecbd8 100644 --- a/internal/apiutil/errors.go +++ b/internal/apiutil/errors.go @@ -66,6 +66,9 @@ var ( // ErrMalformedPolicyAct indicates missing policies action. ErrMalformedPolicyAct = errors.New("malformed policy action") + // ErrMissingPolicyEntityType indicates missing policies entity type. + ErrMissingPolicyEntityType = errors.New("missing policy entity type") + // ErrMalformedPolicyPer indicates missing policies relation. ErrMalformedPolicyPer = errors.New("malformed policy permission") diff --git a/internal/groups/service.go b/internal/groups/service.go index 252baf51cf..8be3d343d3 100644 --- a/internal/groups/service.go +++ b/internal/groups/service.go @@ -581,54 +581,28 @@ func (svc service) Unassign(ctx context.Context, token, groupID, relation, membe return nil } -func (svc service) DeleteGroup(ctx context.Context, token, groupID string) error { +func (svc service) DeleteGroup(ctx context.Context, token, id string) error { res, err := svc.identify(ctx, token) if err != nil { return err } - if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.GroupType, groupID); err != nil { + if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.GroupType, id); err != nil { return err } - // Remove policy of child groups - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.GroupType, - Subject: groupID, - ObjectType: auth.GroupType, - }); err != nil { - return errors.Wrap(svcerr.ErrDeletePolicies, err) - } - - // Remove policy of things - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.GroupType, - Subject: groupID, - ObjectType: auth.ThingType, - }); err != nil { - return errors.Wrap(svcerr.ErrDeletePolicies, err) - } - - // Remove policy from domain - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.DomainType, - Object: groupID, - ObjectType: auth.GroupType, - }); err != nil { + deleteRes, err := svc.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{ + EntityType: auth.GroupType, + Id: id, + }) + if err != nil { return errors.Wrap(svcerr.ErrDeletePolicies, err) } - - // Remove group from database - if err := svc.groups.Delete(ctx, groupID); err != nil { - return errors.Wrap(svcerr.ErrRemoveEntity, err) + if !deleteRes.Deleted { + return svcerr.ErrAuthorization } - // Remove policy of users - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.UserType, - Object: groupID, - ObjectType: auth.GroupType, - }); err != nil { - return errors.Wrap(svcerr.ErrDeletePolicies, err) + if err := svc.groups.Delete(ctx, id); err != nil { + return err } return nil diff --git a/internal/groups/service_test.go b/internal/groups/service_test.go index 97fe88baa9..2869537c9b 100644 --- a/internal/groups/service_test.go +++ b/internal/groups/service_test.go @@ -66,7 +66,7 @@ func TestCreateGroup(t *testing.T) { repoErr error addPolResp *magistrala.AddPoliciesRes addPolErr error - deletePolResp *magistrala.DeletePoliciesRes + deletePolResp *magistrala.DeletePolicyRes deletePolErr error err error }{ @@ -1666,7 +1666,7 @@ func TestAssign(t *testing.T) { repoErr error addParentPoliciesRes *magistrala.AddPoliciesRes addParentPoliciesErr error - deleteParentPoliciesRes *magistrala.DeletePoliciesRes + deleteParentPoliciesRes *magistrala.DeletePolicyRes deleteParentPoliciesErr error repoParentGroupErr error err error @@ -1890,7 +1890,7 @@ func TestAssign(t *testing.T) { addPoliciesRes: &magistrala.AddPoliciesRes{ Added: true, }, - deleteParentPoliciesRes: &magistrala.DeletePoliciesRes{ + deleteParentPoliciesRes: &magistrala.DeletePolicyRes{ Deleted: false, }, deleteParentPoliciesErr: svcerr.ErrAuthorization, @@ -2069,13 +2069,13 @@ func TestUnassign(t *testing.T) { idErr error authzResp *magistrala.AuthorizeRes authzErr error - deletePoliciesRes *magistrala.DeletePoliciesRes + deletePoliciesRes *magistrala.DeletePolicyRes deletePoliciesErr error repoResp mggroups.Page repoErr error addParentPoliciesRes *magistrala.AddPoliciesRes addParentPoliciesErr error - deleteParentPoliciesRes *magistrala.DeletePoliciesRes + deleteParentPoliciesRes *magistrala.DeletePolicyRes deleteParentPoliciesErr error repoParentGroupErr error err error @@ -2094,7 +2094,7 @@ func TestUnassign(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, }, @@ -2112,7 +2112,7 @@ func TestUnassign(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, }, @@ -2137,7 +2137,7 @@ func TestUnassign(t *testing.T) { validGroup, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, repoParentGroupErr: nil, @@ -2156,7 +2156,7 @@ func TestUnassign(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, }, @@ -2242,7 +2242,7 @@ func TestUnassign(t *testing.T) { validGroup, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: false, }, deletePoliciesErr: svcerr.ErrAuthorization, @@ -2269,7 +2269,7 @@ func TestUnassign(t *testing.T) { validGroup, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, repoParentGroupErr: repoerr.ErrConflict, @@ -2296,7 +2296,7 @@ func TestUnassign(t *testing.T) { validGroup, }, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, repoParentGroupErr: repoerr.ErrConflict, @@ -2364,7 +2364,7 @@ func TestUnassign(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deletePoliciesRes: &magistrala.DeletePoliciesRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: false, }, deletePoliciesErr: svcerr.ErrAuthorization, @@ -2468,23 +2468,17 @@ func TestDeleteGroup(t *testing.T) { svc := groups.NewService(repo, idProvider, authsvc) cases := []struct { - desc string - token string - groupID string - idResp *magistrala.IdentityRes - idErr error - authzResp *magistrala.AuthorizeRes - authzErr error - deleteChildPoliciesRes *magistrala.DeletePolicyFilterRes - deleteChildPoliciesErr error - deleteThingsPoliciesRes *magistrala.DeletePolicyFilterRes - deleteThingsPoliciesErr error - deleteDomainsPoliciesRes *magistrala.DeletePolicyFilterRes - deleteDomainsPoliciesErr error - deleteUsersPoliciesRes *magistrala.DeletePolicyFilterRes - deleteUsersPoliciesErr error - repoErr error - err error + desc string + token string + groupID string + idResp *magistrala.IdentityRes + idErr error + authzResp *magistrala.AuthorizeRes + authzErr error + deletePoliciesRes *magistrala.DeletePolicyRes + deletePoliciesErr error + repoErr error + err error }{ { desc: "successfully", @@ -2497,26 +2491,18 @@ func TestDeleteGroup(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteUsersPoliciesRes: &magistrala.DeletePolicyFilterRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, }, { - desc: "unsuccessfully with invalid token", - token: token, - groupID: testsutil.GenerateUUID(t), - idResp: &magistrala.IdentityRes{}, - idErr: svcerr.ErrAuthentication, - err: svcerr.ErrAuthentication, + desc: "unsuccessfully with invalid token", + token: token, + groupID: testsutil.GenerateUUID(t), + idResp: &magistrala.IdentityRes{}, + deletePoliciesRes: &magistrala.DeletePolicyRes{}, + idErr: svcerr.ErrAuthentication, + err: svcerr.ErrAuthentication, }, { desc: "unsuccessfully with authorization error", @@ -2529,71 +2515,12 @@ func TestDeleteGroup(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: false, }, - authzErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, - }, - { - desc: "unsuccessfully with failed to remove child group policy", - token: token, - groupID: testsutil.GenerateUUID(t), - idResp: &magistrala.IdentityRes{ - Id: testsutil.GenerateUUID(t), - DomainId: testsutil.GenerateUUID(t), - }, - authzResp: &magistrala.AuthorizeRes{ - Authorized: true, - }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: false, - }, - deleteChildPoliciesErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, - }, - { - desc: "unsuccessfully with failed to remove things policy", - token: token, - groupID: testsutil.GenerateUUID(t), - idResp: &magistrala.IdentityRes{ - Id: testsutil.GenerateUUID(t), - DomainId: testsutil.GenerateUUID(t), - }, - authzResp: &magistrala.AuthorizeRes{ - Authorized: true, - }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: false, - }, - deleteThingsPoliciesErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, - }, - { - desc: "unsuccessfully with failed to remove domain policy", - token: token, - groupID: testsutil.GenerateUUID(t), - idResp: &magistrala.IdentityRes{ - Id: testsutil.GenerateUUID(t), - DomainId: testsutil.GenerateUUID(t), - }, - authzResp: &magistrala.AuthorizeRes{ - Authorized: true, - }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: false, - }, - deleteDomainsPoliciesErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, + deletePoliciesRes: &magistrala.DeletePolicyRes{}, + authzErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, }, { - desc: "unsuccessfully with failed to remove user policy", + desc: "unsuccessfully with failed to remove policy", token: token, groupID: testsutil.GenerateUUID(t), idResp: &magistrala.IdentityRes{ @@ -2603,20 +2530,11 @@ func TestDeleteGroup(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteUsersPoliciesRes: &magistrala.DeletePolicyFilterRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: false, }, - deleteUsersPoliciesErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, + deletePoliciesErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, }, { desc: "unsuccessfully with repo err", @@ -2629,13 +2547,7 @@ func TestDeleteGroup(t *testing.T) { authzResp: &magistrala.AuthorizeRes{ Authorized: true, }, - deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{ - Deleted: true, - }, - deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{ + deletePoliciesRes: &magistrala.DeletePolicyRes{ Deleted: true, }, repoErr: repoerr.ErrNotFound, @@ -2655,36 +2567,17 @@ func TestDeleteGroup(t *testing.T) { Object: tc.groupID, ObjectType: auth.GroupType, }).Return(tc.authzResp, tc.authzErr) - authcall2 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.GroupType, - Subject: tc.groupID, - ObjectType: auth.GroupType, - }).Return(tc.deleteChildPoliciesRes, tc.deleteChildPoliciesErr) - authcall3 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.GroupType, - Subject: tc.groupID, - ObjectType: auth.ThingType, - }).Return(tc.deleteThingsPoliciesRes, tc.deleteThingsPoliciesErr) - authcall4 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.DomainType, - Object: tc.groupID, - ObjectType: auth.GroupType, - }).Return(tc.deleteDomainsPoliciesRes, tc.deleteDomainsPoliciesErr) - authcall5 := repo.On("Delete", context.Background(), tc.groupID).Return(tc.repoErr) - authcall6 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.UserType, - Object: tc.groupID, - ObjectType: auth.GroupType, - }).Return(tc.deleteUsersPoliciesRes, tc.deleteUsersPoliciesErr) + authcall2 := authsvc.On("DeleteEntityPolicies", context.Background(), &magistrala.DeleteEntityPoliciesReq{ + EntityType: auth.GroupType, + Id: tc.groupID, + }).Return(tc.deletePoliciesRes, tc.deletePoliciesErr) + repocall := repo.On("Delete", context.Background(), tc.groupID).Return(tc.repoErr) err := svc.DeleteGroup(context.Background(), tc.token, tc.groupID) assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("expected error %v to contain %v", err, tc.err)) authcall.Unset() authcall1.Unset() authcall2.Unset() - authcall3.Unset() - authcall4.Unset() - authcall5.Unset() - authcall6.Unset() + repocall.Unset() }) } } diff --git a/pkg/clients/clients.go b/pkg/clients/clients.go index 8d16fe9130..aa7202d9c8 100644 --- a/pkg/clients/clients.go +++ b/pkg/clients/clients.go @@ -101,6 +101,9 @@ type Repository interface { // ChangeStatus changes client status to enabled or disabled ChangeStatus(ctx context.Context, client Client) (Client, error) + + // Delete deletes client with given id + Delete(ctx context.Context, id string) error } // Validate returns an error if client representation is invalid. diff --git a/pkg/clients/postgres/clients.go b/pkg/clients/postgres/clients.go index 1ada83f485..ea6b694797 100644 --- a/pkg/clients/postgres/clients.go +++ b/pkg/clients/postgres/clients.go @@ -320,6 +320,20 @@ func (repo *Repository) update(ctx context.Context, client clients.Client, query return clients.Client{}, repoerr.ErrNotFound } +func (repo *Repository) Delete(ctx context.Context, id string) error { + q := "DELETE FROM clients AS c WHERE c.id = $1 ;" + + result, err := repo.DB.ExecContext(ctx, q, id) + if err != nil { + return postgres.HandleError(repoerr.ErrRemoveEntity, err) + } + if rows, _ := result.RowsAffected(); rows == 0 { + return repoerr.ErrNotFound + } + + return nil +} + type DBClient struct { ID string `db:"id"` Name string `db:"name,omitempty"` diff --git a/pkg/clients/postgres/clients_test.go b/pkg/clients/postgres/clients_test.go index aeb855b001..82fa35567e 100644 --- a/pkg/clients/postgres/clients_test.go +++ b/pkg/clients/postgres/clients_test.go @@ -1798,6 +1798,43 @@ func TestUpdateRole(t *testing.T) { } } +func TestDelete(t *testing.T) { + t.Cleanup(func() { + _, err := db.Exec("DELETE FROM clients") + require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err)) + }) + repo := &postgres.Repository{database} + + client := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo) + + cases := []struct { + desc string + id string + err error + }{ + { + desc: "delete client successfully", + id: client.ID, + err: nil, + }, + { + desc: "delete client with invalid id", + id: testsutil.GenerateUUID(t), + err: repoerr.ErrNotFound, + }, + { + desc: "delete client with empty id", + id: "", + err: repoerr.ErrNotFound, + }, + } + + for _, tc := range cases { + err := repo.Delete(context.Background(), tc.id) + assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) + } +} + func findClients(clis []mgclients.Client, query string, offset, limit uint64) []mgclients.Client { rclients := []mgclients.Client{} for _, client := range clis { diff --git a/pkg/clients/status.go b/pkg/clients/status.go index 7d9ffcb970..9d45db3e06 100644 --- a/pkg/clients/status.go +++ b/pkg/clients/status.go @@ -19,6 +19,8 @@ const ( EnabledStatus Status = iota // DisabledStatus represents disabled Client. DisabledStatus + // DeletedStatus represents a client that will be deleted. + DeletedStatus // AllStatus is used for querying purposes to list clients irrespective // of their status - both enabled and disabled. It is never stored in the @@ -31,6 +33,7 @@ const ( const ( Disabled = "disabled" Enabled = "enabled" + Deleted = "deleted" All = "all" Unknown = "unknown" ) @@ -42,6 +45,8 @@ func (s Status) String() string { return Disabled case EnabledStatus: return Enabled + case DeletedStatus: + return Deleted case AllStatus: return All default: @@ -56,6 +61,8 @@ func ToStatus(status string) (Status, error) { return EnabledStatus, nil case Disabled: return DisabledStatus, nil + case Deleted: + return DeletedStatus, nil case All: return AllStatus, nil } diff --git a/pkg/clients/status_test.go b/pkg/clients/status_test.go index 3e9540a1f5..13dd89059c 100644 --- a/pkg/clients/status_test.go +++ b/pkg/clients/status_test.go @@ -27,6 +27,11 @@ func TestStatusString(t *testing.T) { status: clients.DisabledStatus, expected: "disabled", }, + { + desc: "Deleted", + status: clients.DeletedStatus, + expected: "deleted", + }, { desc: "All", status: clients.AllStatus, @@ -66,6 +71,12 @@ func TestToStatus(t *testing.T) { expetcted: clients.DisabledStatus, err: nil, }, + { + desc: "Deleted", + status: "deleted", + expetcted: clients.DeletedStatus, + err: nil, + }, { desc: "All", status: "all", @@ -108,6 +119,12 @@ func TestStatusMarshalJSON(t *testing.T) { status: clients.DisabledStatus, err: nil, }, + { + desc: "Deleted", + expected: []byte(`"deleted"`), + status: clients.DeletedStatus, + err: nil, + }, { desc: "All", expected: []byte(`"all"`), @@ -150,6 +167,12 @@ func TestStatusUnmarshalJSON(t *testing.T) { status: []byte(`"disabled"`), err: nil, }, + { + desc: "Deleted", + expected: clients.DeletedStatus, + status: []byte(`"deleted"`), + err: nil, + }, { desc: "All", expected: clients.AllStatus, @@ -193,6 +216,12 @@ func TestUserMarshalJSON(t *testing.T) { user: clients.Client{Status: clients.DisabledStatus}, err: nil, }, + { + desc: "Deleted", + expected: []byte(`{"id":"","credentials":{},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","status":"deleted"}`), + user: clients.Client{Status: clients.DeletedStatus}, + err: nil, + }, { desc: "All", expected: []byte(`{"id":"","credentials":{},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","status":"all"}`), diff --git a/pkg/sdk/go/channels_test.go b/pkg/sdk/go/channels_test.go index 13f3e12dc2..c53ab7d8dd 100644 --- a/pkg/sdk/go/channels_test.go +++ b/pkg/sdk/go/channels_test.go @@ -140,7 +140,7 @@ func TestCreateChannel(t *testing.T) { authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil) authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil) + authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil) repoCall := grepo.On("Save", mock.Anything, mock.Anything).Return(convertChannel(sdk.Channel{}), tc.err) rChannel, err := mgsdk.CreateChannel(tc.channel, validToken) assert.Equal(t, tc.err, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err)) @@ -771,7 +771,7 @@ func TestDeleteChannel(t *testing.T) { authCall1.Unset() repoCall.Unset() - authCall = auth.On("DeletePolicyFilter", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil) + authCall = auth.On("DeleteEntityPolicies", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil) authCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) repoCall = grepo.On("Delete", mock.Anything, mock.Anything).Return(nil) diff --git a/pkg/sdk/go/sdk.go b/pkg/sdk/go/sdk.go index 7b2e6b1714..788a425d19 100644 --- a/pkg/sdk/go/sdk.go +++ b/pkg/sdk/go/sdk.go @@ -260,6 +260,13 @@ type SDK interface { // fmt.Println(user) DisableUser(id, token string) (User, errors.SDKError) + // DeleteUser deletes a user with the given id. + // + // example: + // err := sdk.DeleteUser("userID", "token") + // fmt.Println(err) + DeleteUser(id, token string) errors.SDKError + // CreateToken receives credentials and returns user token. // // example: diff --git a/pkg/sdk/go/setup_test.go b/pkg/sdk/go/setup_test.go index 7ddff4c237..a3859540f4 100644 --- a/pkg/sdk/go/setup_test.go +++ b/pkg/sdk/go/setup_test.go @@ -65,12 +65,6 @@ func generateUUID(t *testing.T) string { return ulid } -func convertClientsPage(cp sdk.UsersPage) mgclients.ClientsPage { - return mgclients.ClientsPage{ - Clients: convertClients(cp.Users), - } -} - func convertThingsPage(cp sdk.ThingsPage) mgclients.ClientsPage { return mgclients.ClientsPage{ Clients: convertThings(cp.Things...), diff --git a/pkg/sdk/go/things_test.go b/pkg/sdk/go/things_test.go index 1edae693e8..8b42f42778 100644 --- a/pkg/sdk/go/things_test.go +++ b/pkg/sdk/go/things_test.go @@ -186,7 +186,7 @@ func TestCreateThing(t *testing.T) { authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil) authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil) + authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil) repoCall3 := cRepo.On("Save", mock.Anything, mock.Anything).Return(convertThings(tc.response), tc.repoErr) rThing, err := mgsdk.CreateThing(tc.client, tc.token) @@ -275,7 +275,7 @@ func TestCreateThings(t *testing.T) { authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil) authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil) + authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil) repoCall1 := cRepo.On("Save", mock.Anything, mock.Anything).Return(convertThings(tc.response...), tc.err) if len(tc.things) > 0 { repoCall1 = cRepo.On("Save", mock.Anything, mock.Anything, mock.Anything).Return(convertThings(tc.response...), tc.err) @@ -1272,27 +1272,27 @@ func TestDeleteThing(t *testing.T) { Status: mgclients.EnabledStatus.String(), } - repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) - repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil) - repoCall2 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil) - repoCall4 := cache.On("Remove", mock.Anything, thing.ID).Return(nil) + authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) + authCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil) + repoCall := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil) + cacheCall := cache.On("Remove", mock.Anything, thing.ID).Return(nil) err := mgsdk.DeleteThing("wrongID", validToken) assert.Equal(t, err, errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), fmt.Sprintf("Delete thing with wrong id: expected %v got %v", svcerr.ErrNotFound, err)) + authCall.Unset() + authCall1.Unset() repoCall.Unset() - repoCall1.Unset() - repoCall2.Unset() - repoCall = auth.On("DeletePolicyFilter", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil) - repoCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) - repoCall2 = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall3 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil) + authCall = auth.On("DeleteEntityPolicies", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil) + authCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) + authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) + repoCall = cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil) err = mgsdk.DeleteThing(thing.ID, validToken) assert.Nil(t, err, fmt.Sprintf("Delete thing with correct id: expected %v got %v", nil, err)) - ok := repoCall3.Parent.AssertCalled(t, "Delete", mock.Anything, thing.ID) + ok := repoCall.Parent.AssertCalled(t, "Delete", mock.Anything, thing.ID) assert.True(t, ok, "Delete was not called on deleting thing with correct id") + authCall.Unset() + authCall1.Unset() + authCall2.Unset() repoCall.Unset() - repoCall1.Unset() - repoCall2.Unset() - repoCall3.Unset() - repoCall4.Unset() + cacheCall.Unset() } diff --git a/pkg/sdk/go/users.go b/pkg/sdk/go/users.go index 24ceb35c2f..f7c79c7dcf 100644 --- a/pkg/sdk/go/users.go +++ b/pkg/sdk/go/users.go @@ -344,3 +344,9 @@ func (sdk mgSDK) changeClientStatus(token, id, status string) (User, errors.SDKE return user, nil } + +func (sdk mgSDK) DeleteUser(id, token string) errors.SDKError { + url := fmt.Sprintf("%s/%s/%s", sdk.usersURL, usersEndpoint, id) + _, _, sdkerr := sdk.processRequest(http.MethodDelete, url, token, nil, nil, http.StatusNoContent) + return sdkerr +} diff --git a/pkg/sdk/go/users_test.go b/pkg/sdk/go/users_test.go index 0878ed87cb..57ae018bc2 100644 --- a/pkg/sdk/go/users_test.go +++ b/pkg/sdk/go/users_test.go @@ -181,7 +181,7 @@ func TestCreateClient(t *testing.T) { repoCall = auth.On("Identify", mock.Anything, mock.Anything).Return(&magistrala.IdentityRes{}, svcerr.ErrAuthentication) } repoCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil) - repoCall2 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: true}, nil) + repoCall2 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil) repoCall3 := crepo.On("Save", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.err) rClient, err := mgsdk.CreateUser(tc.client, tc.token) tc.response.ID = rClient.ID @@ -957,7 +957,7 @@ func TestUpdateClientRole(t *testing.T) { repoCall = auth.On("Identify", mock.Anything, mock.Anything).Return(&magistrala.IdentityRes{}, svcerr.ErrAuthentication) } repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall2 := auth.On("DeletePolicyFilter", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil) + repoCall2 := auth.On("DeletePolicyFilter", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil) repoCall3 := auth.On("AddPolicy", mock.Anything, mock.Anything).Return(&magistrala.AddPolicyRes{Added: true}, nil) repoCall4 := crepo.On("UpdateRole", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.err) uClient, err := mgsdk.UpdateUserRole(tc.client, tc.token) @@ -1051,59 +1051,6 @@ func TestEnableClient(t *testing.T) { repoCall2.Unset() repoCall3.Unset() } - - cases2 := []struct { - desc string - token string - status string - metadata sdk.Metadata - response sdk.UsersPage - size uint64 - }{ - { - desc: "list enabled clients", - status: mgclients.EnabledStatus.String(), - size: 2, - response: sdk.UsersPage{ - Users: []sdk.User{enabledClient1, endisabledClient1}, - }, - }, - { - desc: "list disabled clients", - status: mgclients.DisabledStatus.String(), - size: 1, - response: sdk.UsersPage{ - Users: []sdk.User{disabledClient1}, - }, - }, - { - desc: "list enabled and disabled clients", - status: mgclients.AllStatus.String(), - size: 3, - response: sdk.UsersPage{ - Users: []sdk.User{enabledClient1, disabledClient1, endisabledClient1}, - }, - }, - } - - for _, tc := range cases2 { - pm := sdk.PageMetadata{ - Total: 100, - Offset: 0, - Limit: 100, - Status: tc.status, - } - repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: validID}, nil) - repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall2 := crepo.On("RetrieveAll", mock.Anything, mock.Anything).Return(convertClientsPage(tc.response), nil) - clientsPage, err := mgsdk.Users(pm, validToken) - assert.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) - size := uint64(len(clientsPage.Users)) - assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size)) - repoCall.Unset() - repoCall1.Unset() - repoCall2.Unset() - } } func TestDisableClient(t *testing.T) { @@ -1180,57 +1127,79 @@ func TestDisableClient(t *testing.T) { repoCall2.Unset() repoCall3.Unset() } +} + +func TestDeleteUser(t *testing.T) { + ts, crepo, _, auth := setupUsers() + defer ts.Close() + + conf := sdk.Config{ + UsersURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) - cases2 := []struct { + enabledClient1 := sdk.User{ID: testsutil.GenerateUUID(t), Credentials: sdk.Credentials{Identity: "client1@example.com", Secret: "password"}, Status: mgclients.EnabledStatus.String()} + deletedClient1 := sdk.User{ID: testsutil.GenerateUUID(t), Credentials: sdk.Credentials{Identity: "client3@example.com", Secret: "password"}, Status: mgclients.DeletedStatus.String()} + deletedenabledClient1 := enabledClient1 + deletedenabledClient1.Status = mgclients.DisabledStatus.String() + deletedenabledClient1.ID = testsutil.GenerateUUID(t) + + cases := []struct { desc string + id string token string - status string - metadata sdk.Metadata - response sdk.UsersPage - size uint64 + client sdk.User + response sdk.User + repoErr error + err errors.SDKError }{ { - desc: "list enabled clients", - status: mgclients.EnabledStatus.String(), - size: 2, - response: sdk.UsersPage{ - Users: []sdk.User{enabledClient1, disenabledClient1}, - }, + desc: "delete enabled client", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + response: deletedenabledClient1, + err: nil, + repoErr: nil, }, { - desc: "list disabled clients", - status: mgclients.DisabledStatus.String(), - size: 1, - response: sdk.UsersPage{ - Users: []sdk.User{disabledClient1}, - }, + desc: "delete disabled client", + id: deletedClient1.ID, + token: validToken, + client: deletedClient1, + response: sdk.User{}, + repoErr: sdk.ErrFailedDisable, + err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), }, { - desc: "list enabled and disabled clients", - status: mgclients.AllStatus.String(), - size: 3, - response: sdk.UsersPage{ - Users: []sdk.User{enabledClient1, disabledClient1, disenabledClient1}, - }, + desc: "delete non-existing client", + id: wrongID, + client: sdk.User{}, + token: validToken, + response: sdk.User{}, + repoErr: sdk.ErrFailedDisable, + err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), }, } - for _, tc := range cases2 { - pm := sdk.PageMetadata{ - Total: 100, - Offset: 0, - Limit: 100, - Status: tc.status, - } + for _, tc := range cases { repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: validID}, nil) repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall2 := crepo.On("RetrieveAll", mock.Anything, mock.Anything).Return(convertClientsPage(tc.response), nil) - page, err := mgsdk.Users(pm, validToken) - assert.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) - size := uint64(len(page.Users)) - assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size)) + repoCall2 := crepo.On("RetrieveByID", mock.Anything, tc.id).Return(convertClient(tc.client), tc.repoErr) + repoCall3 := crepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.repoErr) + err := mgsdk.DeleteUser(tc.id, tc.token) + assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err)) + if tc.err == nil { + ok := repoCall.Parent.AssertCalled(t, "Identify", mock.Anything, mock.Anything) + assert.True(t, ok, fmt.Sprintf("Identify was not called on %s", tc.desc)) + ok = repoCall2.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, tc.id) + assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc)) + ok = repoCall3.Parent.AssertCalled(t, "ChangeStatus", mock.Anything, mock.Anything) + assert.True(t, ok, fmt.Sprintf("ChangeStatus was not called on %s", tc.desc)) + } repoCall.Unset() repoCall1.Unset() repoCall2.Unset() + repoCall3.Unset() } } diff --git a/pkg/sdk/mocks/sdk.go b/pkg/sdk/mocks/sdk.go index 02ed58467d..55b80b8731 100644 --- a/pkg/sdk/mocks/sdk.go +++ b/pkg/sdk/mocks/sdk.go @@ -766,6 +766,26 @@ func (_m *SDK) DeleteThing(id string, token string) errors.SDKError { return r0 } +// DeleteUser provides a mock function with given fields: id, token +func (_m *SDK) DeleteUser(id string, token string) errors.SDKError { + ret := _m.Called(id, token) + + if len(ret) == 0 { + panic("no return value specified for DeleteUser") + } + + var r0 errors.SDKError + if rf, ok := ret.Get(0).(func(string, string) errors.SDKError); ok { + r0 = rf(id, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(errors.SDKError) + } + } + + return r0 +} + // DisableChannel provides a mock function with given fields: id, token func (_m *SDK) DisableChannel(id string, token string) (sdk.Channel, errors.SDKError) { ret := _m.Called(id, token) diff --git a/things/postgres/clients.go b/things/postgres/clients.go index 69e9f066fd..b8f581c8f9 100644 --- a/things/postgres/clients.go +++ b/things/postgres/clients.go @@ -33,9 +33,6 @@ type Repository interface { // RetrieveBySecret retrieves a client based on the secret (key). RetrieveBySecret(ctx context.Context, key string) (mgclients.Client, error) - - // Delete deletes client with given id - Delete(ctx context.Context, id string) error } // NewRepository instantiates a PostgreSQL @@ -124,17 +121,3 @@ func (repo clientRepo) RetrieveBySecret(ctx context.Context, key string) (mgclie return mgclients.Client{}, repoerr.ErrNotFound } - -func (repo clientRepo) Delete(ctx context.Context, id string) error { - q := "DELETE FROM clients AS c WHERE c.id = $1 ;" - - result, err := repo.DB.ExecContext(ctx, q, id) - if err != nil { - return postgres.HandleError(repoerr.ErrRemoveEntity, err) - } - if rows, _ := result.RowsAffected(); rows == 0 { - return repoerr.ErrNotFound - } - - return nil -} diff --git a/things/postgres/clients_test.go b/things/postgres/clients_test.go index d69ab89e40..8cef3c24db 100644 --- a/things/postgres/clients_test.go +++ b/things/postgres/clients_test.go @@ -25,7 +25,6 @@ var ( invalidName = strings.Repeat("m", maxNameSize+10) clientIdentity = "client-identity@example.com" clientName = "client name" - invalidClientID = "invalidClientID" invalidDomainID = strings.Repeat("m", maxNameSize+10) namesgen = namegenerator.NewGenerator() ) @@ -365,51 +364,3 @@ func TestClientsRetrieveBySecret(t *testing.T) { assert.Equal(t, res, tc.response, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, res)) } } - -func TestDelete(t *testing.T) { - t.Cleanup(func() { - _, err := db.Exec("DELETE FROM clients") - require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err)) - }) - repo := postgres.NewRepository(database) - - client := clients.Client{ - ID: testsutil.GenerateUUID(t), - Name: clientName, - Credentials: clients.Credentials{ - Identity: clientIdentity, - Secret: testsutil.GenerateUUID(t), - }, - Status: clients.EnabledStatus, - } - - _, err := repo.Save(context.Background(), client) - require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) - - cases := []struct { - desc string - id string - err error - }{ - { - desc: "delete client successfully", - id: client.ID, - err: nil, - }, - { - desc: "delete client with invalid id", - id: invalidClientID, - err: repoerr.ErrNotFound, - }, - { - desc: "delete client with empty id", - id: "", - err: repoerr.ErrNotFound, - }, - } - - for _, tc := range cases { - err := repo.Delete(context.Background(), tc.id) - assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) - } -} diff --git a/things/service.go b/things/service.go index 25e8326201..8e11233cdb 100644 --- a/things/service.go +++ b/things/service.go @@ -438,43 +438,25 @@ func (svc service) DeleteClient(ctx context.Context, token, id string) error { return err } - // Remove from cache if err := svc.clientCache.Remove(ctx, id); err != nil { return errors.Wrap(svcerr.ErrRemoveEntity, err) } - // Remove policy of groups - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.GroupType, - Object: id, - ObjectType: auth.ThingType, - }); err != nil { + deleteRes, err := svc.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{ + EntityType: auth.ThingType, + Id: id, + }) + if err != nil { return errors.Wrap(svcerr.ErrRemoveEntity, err) } - - // Remove policy from domain - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.DomainType, - Object: id, - ObjectType: auth.ThingType, - }); err != nil { - return errors.Wrap(svcerr.ErrRemoveEntity, err) + if !deleteRes.Deleted { + return svcerr.ErrAuthorization } - // Remove thing from database if err := svc.clients.Delete(ctx, id); err != nil { return errors.Wrap(svcerr.ErrRemoveEntity, err) } - // Remove policy of users - if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{ - SubjectType: auth.UserType, - Object: id, - ObjectType: auth.ThingType, - }); err != nil { - return errors.Wrap(svcerr.ErrRemoveEntity, err) - } - return nil } diff --git a/things/service_test.go b/things/service_test.go index b61b2eeaa2..810bcda99d 100644 --- a/things/service_test.go +++ b/things/service_test.go @@ -65,7 +65,7 @@ func TestCreateThings(t *testing.T) { token string authResponse *magistrala.AuthorizeRes addPolicyResponse *magistrala.AddPoliciesRes - deletePolicyRes *magistrala.DeletePoliciesRes + deletePolicyRes *magistrala.DeletePolicyRes authorizeErr error identifyErr error addPolicyErr error @@ -320,7 +320,7 @@ func TestCreateThings(t *testing.T) { authResponse: &magistrala.AuthorizeRes{Authorized: true}, addPolicyResponse: &magistrala.AddPoliciesRes{Added: true}, saveErr: repoerr.ErrConflict, - deletePolicyRes: &magistrala.DeletePoliciesRes{Deleted: false}, + deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false}, deletePolicyErr: svcerr.ErrInvalidPolicy, err: repoerr.ErrConflict, }, @@ -1609,33 +1609,27 @@ func TestDeleteClient(t *testing.T) { invalidClientID := "invalidClientID" _ = invalidClientID cases := []struct { - desc string - token string - identifyResponse *magistrala.IdentityRes - authorizeResponse *magistrala.AuthorizeRes - deletePolicyResponse *magistrala.DeletePolicyFilterRes - deletePolicyResponse1 *magistrala.DeletePolicyFilterRes - deletePolicyResponse2 *magistrala.DeletePolicyFilterRes - clientID string - identifyErr error - authorizeErr error - removeErr error - deleteErr error - deletePolicyErr error - deletePolicyErr1 error - deletePolicyErr2 error - err error + desc string + token string + identifyResponse *magistrala.IdentityRes + authorizeResponse *magistrala.AuthorizeRes + deletePolicyResponse *magistrala.DeletePolicyRes + clientID string + identifyErr error + authorizeErr error + removeErr error + deleteErr error + deletePolicyErr error + err error }{ { - desc: "Delete client with authorized token", - token: validToken, - clientID: client.ID, - identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, - authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse2: &magistrala.DeletePolicyFilterRes{Deleted: true}, - err: nil, + desc: "Delete client with authorized token", + token: validToken, + clientID: client.ID, + identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: true}, + err: nil, }, { desc: "Delete client with unauthorized token", @@ -1655,15 +1649,14 @@ func TestDeleteClient(t *testing.T) { err: svcerr.ErrAuthorization, }, { - desc: "Delete client with repo error ", - token: validToken, - clientID: client.ID, - identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, - authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deleteErr: repoerr.ErrRemoveEntity, - err: repoerr.ErrRemoveEntity, + desc: "Delete client with repo error ", + token: validToken, + clientID: client.ID, + identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: true}, + deleteErr: repoerr.ErrRemoveEntity, + err: repoerr.ErrRemoveEntity, }, { desc: "Delete client with cache error ", @@ -1675,60 +1668,26 @@ func TestDeleteClient(t *testing.T) { err: repoerr.ErrRemoveEntity, }, { - desc: "Delete client with failed to delete groups policy", + desc: "Delete client with failed to delete policy", token: validToken, clientID: client.ID, identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: false}, + deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: false}, deletePolicyErr: errRemovePolicies, err: errRemovePolicies, }, - { - desc: "Delete client with failed to delete domains policy", - token: validToken, - clientID: client.ID, - identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, - authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: false}, - deletePolicyErr1: errRemovePolicies, - err: errRemovePolicies, - }, - { - desc: "Delete client with failed to delete users policy", - token: validToken, - clientID: client.ID, - identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, - authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true}, - deletePolicyResponse2: &magistrala.DeletePolicyFilterRes{Deleted: false}, - deletePolicyErr2: errRemovePolicies, - err: errRemovePolicies, - }, } for _, tc := range cases { repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(tc.identifyResponse, tc.identifyErr) repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(tc.authorizeResponse, tc.authorizeErr) repoCall2 := cache.On("Remove", mock.Anything, tc.clientID).Return(tc.removeErr) - repoCall3 := auth.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{ - SubjectType: authsvc.GroupType, - Object: tc.clientID, - ObjectType: authsvc.ThingType, + repoCall3 := auth.On("DeleteEntityPolicies", context.Background(), &magistrala.DeleteEntityPoliciesReq{ + EntityType: authsvc.ThingType, + Id: tc.clientID, }).Return(tc.deletePolicyResponse, tc.deletePolicyErr) - repoCall4 := auth.On("DeletePolicyFilter", mock.Anything, &magistrala.DeletePolicyFilterReq{ - SubjectType: authsvc.DomainType, - Object: tc.clientID, - ObjectType: authsvc.ThingType, - }).Return(tc.deletePolicyResponse1, tc.deletePolicyErr1) - repoCall5 := cRepo.On("Delete", context.Background(), tc.clientID).Return(tc.deleteErr) - repoCall6 := auth.On("DeletePolicyFilter", mock.Anything, &magistrala.DeletePolicyFilterReq{ - SubjectType: authsvc.UserType, - Object: tc.clientID, - ObjectType: authsvc.ThingType, - }).Return(tc.deletePolicyResponse2, tc.deletePolicyErr2) + repoCall4 := cRepo.On("Delete", context.Background(), tc.clientID).Return(tc.deleteErr) err := svc.DeleteClient(context.Background(), tc.token, tc.clientID) assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) repoCall.Unset() @@ -1736,8 +1695,6 @@ func TestDeleteClient(t *testing.T) { repoCall2.Unset() repoCall3.Unset() repoCall4.Unset() - repoCall5.Unset() - repoCall6.Unset() } } @@ -1832,7 +1789,7 @@ func TestUnShare(t *testing.T) { userID string identifyResponse *magistrala.IdentityRes authorizeResponse *magistrala.AuthorizeRes - deletePoliciesResponse *magistrala.DeletePoliciesRes + deletePoliciesResponse *magistrala.DeletePolicyRes identifyErr error authorizeErr error deletePoliciesErr error @@ -1844,7 +1801,7 @@ func TestUnShare(t *testing.T) { clientID: clientID, identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, err: nil, }, { @@ -1870,7 +1827,7 @@ func TestUnShare(t *testing.T) { clientID: clientID, identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{}, deletePoliciesErr: svcerr.ErrInvalidPolicy, err: svcerr.ErrInvalidPolicy, }, @@ -1880,7 +1837,7 @@ func TestUnShare(t *testing.T) { clientID: clientID, identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false}, err: nil, }, } diff --git a/things/standalone/standalone.go b/things/standalone/standalone.go index 13ea7b7c91..adaab18bb6 100644 --- a/things/standalone/standalone.go +++ b/things/standalone/standalone.go @@ -62,11 +62,11 @@ func (repo singleUserRepo) AddPolicies(ctx context.Context, in *magistrala.AddPo return nil, nil } -func (repo singleUserRepo) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) { +func (repo singleUserRepo) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { return nil, nil } -func (repo singleUserRepo) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) { +func (repo singleUserRepo) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { return nil, nil } @@ -97,3 +97,7 @@ func (repo singleUserRepo) CountSubjects(ctx context.Context, in *magistrala.Cou func (repo singleUserRepo) ListPermissions(ctx context.Context, in *magistrala.ListPermissionsReq, opts ...grpc.CallOption) (*magistrala.ListPermissionsRes, error) { return nil, nil } + +func (repo singleUserRepo) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) { + return nil, nil +} diff --git a/users/README.md b/users/README.md index e1c63cd122..dae098dd7b 100644 --- a/users/README.md +++ b/users/README.md @@ -48,6 +48,10 @@ The service is configured using the environment variables presented in the follo | MG_EMAIL_TEMPLATE | Email template for sending emails with password reset link | email.tmpl | | MG_USERS_ES_URL | Event store URL | | | MG_JAEGER_URL | Jaeger server URL | | +| MG_OAUTH_UI_REDIRECT_URL | OAuth UI redirect URL | | +| MG_OAUTH_UI_ERROR_URL | OAuth UI error URL | | +| MG_USERS_DELETE_INTERVAL | Interval for deleting users | 24h | +| MG_USERS_DELETE_AFTER | Time after which users are deleted | 720h | | MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 | | MG_SEND_TELEMETRY | Send telemetry to magistrala call home server. | true | | MG_USERS_INSTANCE_ID | Magistrala instance ID | "" | @@ -107,6 +111,10 @@ MG_USERS_ES_URL=nats://localhost:4222 \ MG_JAEGER_URL=http://localhost:14268/api/traces \ MG_JAEGER_TRACE_RATIO=1.0 \ MG_SEND_TELEMETRY=true \ +MG_OAUTH_UI_REDIRECT_URL=http://localhost:9095/domains \ +MG_OAUTH_UI_ERROR_URL=http://localhost:9095/error \ +MG_USERS_DELETE_INTERVAL=24h \ +MG_USERS_DELETE_AFTER=720h \ MG_USERS_INSTANCE_ID="" \ $GOBIN/magistrala-users ``` diff --git a/users/api/clients.go b/users/api/clients.go index 74f691b94e..b41aa49e6f 100644 --- a/users/api/clients.go +++ b/users/api/clients.go @@ -124,6 +124,13 @@ func clientsHandler(svc users.Service, r *chi.Mux, logger *slog.Logger, pr *rege api.EncodeResponse, opts..., ), "disable_client").ServeHTTP) + + r.Delete("/{id}", otelhttp.NewHandler(kithttp.NewServer( + deleteClientEndpoint(svc), + decodeChangeClientStatus, + api.EncodeResponse, + opts..., + ), "delete_client").ServeHTTP) }) r.Route("/password", func(r chi.Router) { diff --git a/users/api/endpoint_test.go b/users/api/endpoint_test.go index f69a4d90d4..e91a58c6c4 100644 --- a/users/api/endpoint_test.go +++ b/users/api/endpoint_test.go @@ -1734,6 +1734,74 @@ func TestDisableClient(t *testing.T) { } } +func TestDeleteClient(t *testing.T) { + us, svc, _ := newUsersServer() + defer us.Close() + + cases := []struct { + desc string + client mgclients.Client + response mgclients.Client + token string + status int + err error + }{ + { + desc: "delete user with valid token", + client: client, + response: mgclients.Client{ + ID: client.ID, + }, + token: validToken, + status: http.StatusNoContent, + err: nil, + }, + { + desc: "delete user with invalid token", + client: client, + token: inValidToken, + status: http.StatusUnauthorized, + err: svcerr.ErrAuthentication, + }, + { + desc: "delete user with empty id", + client: mgclients.Client{ + ID: "", + }, + token: validToken, + status: http.StatusMethodNotAllowed, + err: apiutil.ErrMissingID, + }, + { + desc: "delete user with invalid id", + client: mgclients.Client{ + ID: "invalid", + }, + token: validToken, + status: http.StatusForbidden, + err: svcerr.ErrAuthorization, + }, + } + + for _, tc := range cases { + data := toJSON(tc.client) + req := testRequest{ + client: us.Client(), + method: http.MethodDelete, + url: fmt.Sprintf("%s/users/%s", us.URL, tc.client.ID), + contentType: contentType, + token: tc.token, + body: strings.NewReader(data), + } + + repoCall := svc.On("DeleteClient", mock.Anything, mock.Anything, mock.Anything).Return(tc.err) + res, err := req.make() + assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err)) + assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode)) + repoCall.Unset() + } +} + func TestListUsersByUserGroupId(t *testing.T) { us, svc, _ := newUsersServer() defer us.Close() diff --git a/users/api/endpoints.go b/users/api/endpoints.go index b8bb81101a..2e77bedae0 100644 --- a/users/api/endpoints.go +++ b/users/api/endpoints.go @@ -360,7 +360,7 @@ func enableClientEndpoint(svc users.Service) endpoint.Endpoint { return nil, err } - return deleteClientRes{Client: client}, nil + return changeClientStatusClientRes{Client: client}, nil } } @@ -376,7 +376,22 @@ func disableClientEndpoint(svc users.Service) endpoint.Endpoint { return nil, err } - return deleteClientRes{Client: client}, nil + return changeClientStatusClientRes{Client: client}, nil + } +} + +func deleteClientEndpoint(svc users.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(changeClientStatusReq) + if err := req.validate(); err != nil { + return nil, errors.Wrap(apiutil.ErrValidation, err) + } + + if err := svc.DeleteClient(ctx, req.token, req.id); err != nil { + return nil, err + } + + return deleteClientRes{true}, nil } } diff --git a/users/api/logging.go b/users/api/logging.go index 57fd513cb0..7ed01bcbe7 100644 --- a/users/api/logging.go +++ b/users/api/logging.go @@ -413,3 +413,20 @@ func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, client mgclients }(time.Now()) return lm.svc.OAuthCallback(ctx, client) } + +// DeleteClient logs the delete_client request. It logs the client id and token and the time it took to complete the request. +func (lm *loggingMiddleware) DeleteClient(ctx context.Context, token, id string) (err error) { + defer func(begin time.Time) { + args := []any{ + slog.String("duration", time.Since(begin).String()), + slog.String("user_id", id), + } + if err != nil { + args = append(args, slog.Any("error", err)) + lm.logger.Warn("Delete user failed to complete successfully", args...) + return + } + lm.logger.Info("Delete user completed successfully", args...) + }(time.Now()) + return lm.svc.DeleteClient(ctx, token, id) +} diff --git a/users/api/metrics.go b/users/api/metrics.go index ea4328e818..ccf231adda 100644 --- a/users/api/metrics.go +++ b/users/api/metrics.go @@ -199,3 +199,12 @@ func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, client mgclients }(time.Now()) return ms.svc.OAuthCallback(ctx, client) } + +// DeleteClient instruments DeleteClient method with metrics. +func (ms *metricsMiddleware) DeleteClient(ctx context.Context, token, id string) error { + defer func(begin time.Time) { + ms.counter.With("method", "delete_client").Add(1) + ms.latency.With("method", "delete_client").Observe(time.Since(begin).Seconds()) + }(time.Now()) + return ms.svc.DeleteClient(ctx, token, id) +} diff --git a/users/api/responses.go b/users/api/responses.go index fb05de493b..f0f9a4d3fb 100644 --- a/users/api/responses.go +++ b/users/api/responses.go @@ -18,9 +18,16 @@ var ( _ magistrala.Response = (*tokenRes)(nil) _ magistrala.Response = (*viewClientRes)(nil) _ magistrala.Response = (*createClientRes)(nil) - _ magistrala.Response = (*deleteClientRes)(nil) + _ magistrala.Response = (*changeClientStatusClientRes)(nil) _ magistrala.Response = (*clientsPageRes)(nil) _ magistrala.Response = (*viewMembersRes)(nil) + _ magistrala.Response = (*passwResetReqRes)(nil) + _ magistrala.Response = (*passwChangeRes)(nil) + _ magistrala.Response = (*assignUsersRes)(nil) + _ magistrala.Response = (*unassignUsersRes)(nil) + _ magistrala.Response = (*updateClientRes)(nil) + _ magistrala.Response = (*tokenRes)(nil) + _ magistrala.Response = (*deleteClientRes)(nil) ) type pageRes struct { @@ -139,19 +146,19 @@ func (res viewMembersRes) Empty() bool { return false } -type deleteClientRes struct { +type changeClientStatusClientRes struct { mgclients.Client `json:",inline"` } -func (res deleteClientRes) Code() int { +func (res changeClientStatusClientRes) Code() int { return http.StatusOK } -func (res deleteClientRes) Headers() map[string]string { +func (res changeClientStatusClientRes) Headers() map[string]string { return map[string]string{} } -func (res deleteClientRes) Empty() bool { +func (res changeClientStatusClientRes) Empty() bool { return false } @@ -212,3 +219,23 @@ func (res unassignUsersRes) Headers() map[string]string { func (res unassignUsersRes) Empty() bool { return true } + +type deleteClientRes struct { + deleted bool +} + +func (res deleteClientRes) Code() int { + if res.deleted { + return http.StatusNoContent + } + + return http.StatusOK +} + +func (res deleteClientRes) Headers() map[string]string { + return map[string]string{} +} + +func (res deleteClientRes) Empty() bool { + return true +} diff --git a/users/clients.go b/users/clients.go index 2f359d43e1..e40c4f226d 100644 --- a/users/clients.go +++ b/users/clients.go @@ -63,6 +63,9 @@ type Service interface { // DisableClient logically disables the client identified with the provided ID. DisableClient(ctx context.Context, token, id string) (clients.Client, error) + // DeleteClient deletes client with given ID. + DeleteClient(ctx context.Context, token, id string) error + // Identify returns the client id from the given token. Identify(ctx context.Context, tkn string) (string, error) diff --git a/users/delete_handler.go b/users/delete_handler.go new file mode 100644 index 0000000000..e61eadf091 --- /dev/null +++ b/users/delete_handler.go @@ -0,0 +1,100 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +// The DeleteHandler is a cron job that runs periodically to delete users that have been marked as deleted +// for a certain period of time together with the user's policies from the auth service. +// The handler runs in a separate goroutine and checks for users that have been marked as deleted for a certain period of time. +// If the user has been marked as deleted for more than the specified period, +// the handler deletes the user's policies from the auth service and deletes the user from the database. + +package users + +import ( + "context" + "log/slog" + "time" + + "github.com/absmach/magistrala" + "github.com/absmach/magistrala/auth" + mgclients "github.com/absmach/magistrala/pkg/clients" + svcerr "github.com/absmach/magistrala/pkg/errors/service" + "github.com/absmach/magistrala/users/postgres" +) + +const defLimit = uint64(100) + +type handler struct { + clients postgres.Repository + auth magistrala.AuthServiceClient + checkInterval time.Duration + deleteAfter time.Duration + logger *slog.Logger +} + +func NewDeleteHandler(ctx context.Context, clients postgres.Repository, auth magistrala.AuthServiceClient, defCheckInterval, deleteAfter time.Duration, logger *slog.Logger) { + handler := &handler{ + clients: clients, + auth: auth, + checkInterval: defCheckInterval, + deleteAfter: deleteAfter, + logger: logger, + } + + go func() { + ticker := time.NewTicker(handler.checkInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + handler.handle(ctx) + } + } + }() +} + +func (h *handler) handle(ctx context.Context) { + pm := mgclients.Page{Limit: defLimit, Offset: 0, Status: mgclients.DeletedStatus} + + for { + dbUsers, err := h.clients.RetrieveAll(ctx, pm) + if err != nil { + h.logger.Error("failed to retrieve users", slog.Any("error", err)) + break + } + if dbUsers.Total == 0 { + break + } + + for _, u := range dbUsers.Clients { + if time.Since(u.UpdatedAt) < h.deleteAfter { + continue + } + + deleteRes, err := h.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{ + Id: u.ID, + EntityType: auth.UserType, + }) + if err != nil { + h.logger.Error("failed to delete user policies", slog.Any("error", err)) + continue + } + if !deleteRes.Deleted { + h.logger.Error("failed to delete user policies", slog.Any("error", svcerr.ErrAuthorization)) + continue + } + + if err := h.clients.Delete(ctx, u.ID); err != nil { + h.logger.Error("failed to delete user", slog.Any("error", err)) + continue + } + + h.logger.Info("user deleted", slog.Group("user", + slog.String("id", u.ID), + slog.String("name", u.Name), + )) + } + } +} diff --git a/users/events/events.go b/users/events/events.go index d46a4dfdb5..dda7a669a6 100644 --- a/users/events/events.go +++ b/users/events/events.go @@ -26,6 +26,7 @@ const ( resetSecret = clientPrefix + "reset_secret" sendPasswordReset = clientPrefix + "send_password_reset" oauthCallback = clientPrefix + "oauth_callback" + deleteClient = clientPrefix + "delete" ) var ( @@ -43,6 +44,7 @@ var ( _ events.Event = (*resetSecretEvent)(nil) _ events.Event = (*sendPasswordResetEvent)(nil) _ events.Event = (*oauthCallbackEvent)(nil) + _ events.Event = (*deleteClientEvent)(nil) ) type createClientEvent struct { @@ -386,3 +388,14 @@ func (oce oauthCallbackEvent) Encode() (map[string]interface{}, error) { "client_id": oce.clientID, }, nil } + +type deleteClientEvent struct { + id string +} + +func (dce deleteClientEvent) Encode() (map[string]interface{}, error) { + return map[string]interface{}{ + "operation": deleteClient, + "id": dce.id, + }, nil +} diff --git a/users/events/streams.go b/users/events/streams.go index f838c06080..5563d89478 100644 --- a/users/events/streams.go +++ b/users/events/streams.go @@ -312,3 +312,15 @@ func (es *eventStore) OAuthCallback(ctx context.Context, client mgclients.Client return token, nil } + +func (es *eventStore) DeleteClient(ctx context.Context, token, id string) error { + if err := es.svc.DeleteClient(ctx, token, id); err != nil { + return err + } + + event := deleteClientEvent{ + id: id, + } + + return es.Publish(ctx, event) +} diff --git a/users/mocks/repository.go b/users/mocks/repository.go index 66a4ecda85..12baf1ac2b 100644 --- a/users/mocks/repository.go +++ b/users/mocks/repository.go @@ -63,6 +63,24 @@ func (_m *Repository) CheckSuperAdmin(ctx context.Context, adminID string) error return r0 } +// Delete provides a mock function with given fields: ctx, id +func (_m *Repository) Delete(ctx context.Context, id string) error { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // RetrieveAll provides a mock function with given fields: ctx, pm func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) { ret := _m.Called(ctx, pm) diff --git a/users/mocks/service.go b/users/mocks/service.go index 55ae57a150..cd769feeca 100644 --- a/users/mocks/service.go +++ b/users/mocks/service.go @@ -19,6 +19,24 @@ type Service struct { mock.Mock } +// DeleteClient provides a mock function with given fields: ctx, token, id +func (_m *Service) DeleteClient(ctx context.Context, token string, id string) error { + ret := _m.Called(ctx, token, id) + + if len(ret) == 0 { + panic("no return value specified for DeleteClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, token, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DisableClient provides a mock function with given fields: ctx, token, id func (_m *Service) DisableClient(ctx context.Context, token string, id string) (clients.Client, error) { ret := _m.Called(ctx, token, id) diff --git a/users/service.go b/users/service.go index e2f906f8b6..9f1b1d1f5b 100644 --- a/users/service.go +++ b/users/service.go @@ -435,8 +435,10 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client if err != nil { return mgclients.Client{}, err } - if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil { - return mgclients.Client{}, err + if tokenUserID != client.ID { + if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil { + return mgclients.Client{}, err + } } dbClient, err := svc.clients.RetrieveByID(ctx, client.ID) if err != nil { @@ -454,6 +456,20 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client return client, nil } +func (svc service) DeleteClient(ctx context.Context, token, id string) error { + client := mgclients.Client{ + ID: id, + UpdatedAt: time.Now(), + Status: mgclients.DeletedStatus, + } + + if _, err := svc.changeClientStatus(ctx, token, client); err != nil { + return err + } + + return nil +} + func (svc service) ListMembers(ctx context.Context, token, objectKind, objectID string, pm mgclients.Page) (mgclients.MembersPage, error) { res, err := svc.identify(ctx, token) if err != nil { diff --git a/users/service_test.go b/users/service_test.go index 880f6e1f7e..183464510c 100644 --- a/users/service_test.go +++ b/users/service_test.go @@ -23,7 +23,6 @@ import ( "github.com/absmach/magistrala/users/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" ) var ( @@ -66,7 +65,7 @@ func TestRegisterClient(t *testing.T) { client mgclients.Client identifyResponse *magistrala.IdentityRes addPoliciesResponse *magistrala.AddPoliciesRes - deletePoliciesResponse *magistrala.DeletePoliciesRes + deletePoliciesResponse *magistrala.DeletePolicyRes token string identifyErr error addPoliciesResponseErr error @@ -85,7 +84,7 @@ func TestRegisterClient(t *testing.T) { desc: "register existing client", client: client, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, token: validToken, saveErr: repoerr.ErrConflict, err: repoerr.ErrConflict, @@ -144,7 +143,7 @@ func TestRegisterClient(t *testing.T) { }, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, saveErr: errors.ErrMalformedEntity, err: errors.ErrMalformedEntity, token: validToken, @@ -159,7 +158,7 @@ func TestRegisterClient(t *testing.T) { }, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, err: nil, }, { @@ -172,7 +171,7 @@ func TestRegisterClient(t *testing.T) { }, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, err: repoerr.ErrMalformedEntity, }, { @@ -186,7 +185,7 @@ func TestRegisterClient(t *testing.T) { Status: mgclients.AllStatus, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, err: svcerr.ErrInvalidStatus, }, { @@ -200,7 +199,7 @@ func TestRegisterClient(t *testing.T) { Role: 2, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true}, err: svcerr.ErrInvalidRole, }, { @@ -241,7 +240,7 @@ func TestRegisterClient(t *testing.T) { Role: mgclients.AdminRole, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false}, deletePoliciesResponseErr: svcerr.ErrConflict, saveErr: repoerr.ErrConflict, err: svcerr.ErrConflict, @@ -257,7 +256,7 @@ func TestRegisterClient(t *testing.T) { Role: mgclients.AdminRole, }, addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true}, - deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false}, + deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false}, saveErr: repoerr.ErrConflict, err: svcerr.ErrConflict, }, @@ -292,7 +291,7 @@ func TestRegisterClient(t *testing.T) { identifyResponse *magistrala.IdentityRes authorizeResponse *magistrala.AuthorizeRes addPoliciesResponse *magistrala.AddPoliciesRes - deletePoliciesResponse *magistrala.DeletePoliciesRes + deletePoliciesResponse *magistrala.DeletePolicyRes token string identifyErr error authorizeErr error @@ -1005,7 +1004,7 @@ func TestUpdateClientRole(t *testing.T) { membershipAuthReq *magistrala.AuthorizeReq superAdminAuthRes *magistrala.AuthorizeRes membershipAuthRes *magistrala.AuthorizeRes - deletePolicyFilterResponse *magistrala.DeletePolicyFilterRes + deletePolicyFilterResponse *magistrala.DeletePolicyRes addPolicyResponse *magistrala.AddPolicyRes updateRoleResponse mgclients.Client token string @@ -1095,7 +1094,7 @@ func TestUpdateClientRole(t *testing.T) { superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true}, membershipAuthReq: membershipAuthReq, membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, + deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: true}, updateRoleResponse: client2, token: validToken, err: nil, @@ -1108,7 +1107,7 @@ func TestUpdateClientRole(t *testing.T) { superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true}, membershipAuthReq: membershipAuthReq, membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false}, + deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false}, updateRoleResponse: mgclients.Client{}, token: validToken, deletePolicyErr: svcerr.ErrAuthorization, @@ -1122,7 +1121,7 @@ func TestUpdateClientRole(t *testing.T) { superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true}, membershipAuthReq: membershipAuthReq, membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true}, - deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false}, + deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false}, updateRoleResponse: mgclients.Client{}, token: validToken, deletePolicyErr: svcerr.ErrMalformedEntity, @@ -1137,7 +1136,7 @@ func TestUpdateClientRole(t *testing.T) { membershipAuthReq: membershipAuthReq, membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true}, addPolicyResponse: &magistrala.AddPolicyRes{Added: true}, - deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: true}, + deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: true}, updateRoleResponse: mgclients.Client{}, token: validToken, updateRoleErr: svcerr.ErrAuthentication, @@ -1152,7 +1151,7 @@ func TestUpdateClientRole(t *testing.T) { membershipAuthReq: membershipAuthReq, membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true}, addPolicyResponse: &magistrala.AddPolicyRes{Added: true}, - deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false}, + deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false}, updateRoleResponse: mgclients.Client{}, token: validToken, updateRoleErr: svcerr.ErrAuthentication, @@ -1375,17 +1374,14 @@ func TestEnableClient(t *testing.T) { err: svcerr.ErrAuthentication, }, { - desc: "enable disabled client with failed to authorize", - id: disabledClient1.ID, - token: validToken, - client: disabledClient1, - identifyResponse: &magistrala.IdentityRes{UserId: disabledClient1.ID}, - authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, - retrieveByIDResponse: mgclients.Client{}, - changeStatusResponse: mgclients.Client{}, - response: mgclients.Client{}, - identifyErr: svcerr.ErrAuthorization, - err: svcerr.ErrAuthorization, + desc: "enable disabled client with failed to authorize", + id: disabledClient1.ID, + token: validToken, + client: disabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: disabledClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, + identifyErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, }, { desc: "enable disabled client with normal user token", @@ -1453,72 +1449,6 @@ func TestEnableClient(t *testing.T) { repoCall1.Unset() repoCall2.Unset() } - - cases2 := []struct { - desc string - status mgclients.Status - size uint64 - response mgclients.ClientsPage - }{ - { - desc: "list enabled clients", - status: mgclients.EnabledStatus, - size: 2, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 2, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{enabledClient1, endisabledClient1}, - }, - }, - { - desc: "list disabled clients", - status: mgclients.DisabledStatus, - size: 1, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 1, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{disabledClient1}, - }, - }, - { - desc: "list enabled and disabled clients", - status: mgclients.AllStatus, - size: 3, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 3, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{enabledClient1, disabledClient1, endisabledClient1}, - }, - }, - } - - for _, tc := range cases2 { - pm := mgclients.Page{ - Offset: 0, - Limit: 100, - Status: tc.status, - } - authCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: client.ID}, nil) - authCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall := cRepo.On("RetrieveAll", context.Background(), mock.Anything).Return(tc.response, nil) - - page, err := svc.ListClients(context.Background(), validToken, pm) - require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) - size := uint64(len(page.Clients)) - assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size)) - authCall.Unset() - authCall1.Unset() - repoCall.Unset() - } } func TestDisableClient(t *testing.T) { @@ -1582,7 +1512,7 @@ func TestDisableClient(t *testing.T) { id: enabledClient1.ID, token: validToken, client: enabledClient1, - identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID}, + identifyResponse: &magistrala.IdentityRes{UserId: validID}, authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, checkSuperAdminErr: svcerr.ErrAuthorization, err: svcerr.ErrAuthorization, @@ -1643,71 +1573,128 @@ func TestDisableClient(t *testing.T) { repoCall1.Unset() repoCall2.Unset() } +} - cases2 := []struct { - desc string - status mgclients.Status - size uint64 - response mgclients.ClientsPage +func TestDeleteClient(t *testing.T) { + svc, cRepo, auth, _ := newService(true) + + enabledClient1 := mgclients.Client{ID: testsutil.GenerateUUID(t), Credentials: mgclients.Credentials{Identity: "client1@example.com", Secret: "password"}, Status: mgclients.EnabledStatus} + deletedClient1 := mgclients.Client{ID: testsutil.GenerateUUID(t), Credentials: mgclients.Credentials{Identity: "client3@example.com", Secret: "password"}, Status: mgclients.DeletedStatus} + disenabledClient1 := enabledClient1 + disenabledClient1.Status = mgclients.DeletedStatus + + cases := []struct { + desc string + id string + token string + client mgclients.Client + identifyResponse *magistrala.IdentityRes + authorizeResponse *magistrala.AuthorizeRes + retrieveByIDResponse mgclients.Client + changeStatusResponse mgclients.Client + response mgclients.Client + identifyErr error + authorizeErr error + retrieveByIDErr error + changeStatusErr error + checkSuperAdminErr error + err error }{ { - desc: "list enabled clients", - status: mgclients.EnabledStatus, - size: 1, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 1, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{enabledClient1}, - }, + desc: "delete enabled client", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + retrieveByIDResponse: enabledClient1, + changeStatusResponse: disenabledClient1, + response: disenabledClient1, + err: nil, }, { - desc: "list disabled clients", - status: mgclients.DisabledStatus, - size: 2, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 2, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{disenabledClient1, disabledClient1}, - }, + desc: "delete enabled client with invalid token", + id: enabledClient1.ID, + token: inValidToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{}, + identifyErr: svcerr.ErrAuthentication, + err: svcerr.ErrAuthentication, }, { - desc: "list enabled and disabled clients", - status: mgclients.AllStatus, - size: 3, - response: mgclients.ClientsPage{ - Page: mgclients.Page{ - Total: 3, - Offset: 0, - Limit: 100, - }, - Clients: []mgclients.Client{enabledClient1, disabledClient1, disenabledClient1}, - }, + desc: "delete enabled client with failed to authorize", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: deletedClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, + err: svcerr.ErrAuthorization, + }, + { + desc: "delete enabled client with normal user token", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: validID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: false}, + checkSuperAdminErr: svcerr.ErrAuthorization, + err: svcerr.ErrAuthorization, + }, + { + desc: "delete enabled client with failed to retrieve client by ID", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + retrieveByIDResponse: mgclients.Client{}, + retrieveByIDErr: repoerr.ErrNotFound, + err: repoerr.ErrNotFound, + }, + { + desc: "delete already deleted client", + id: deletedClient1.ID, + token: validToken, + client: deletedClient1, + identifyResponse: &magistrala.IdentityRes{UserId: deletedClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + retrieveByIDResponse: deletedClient1, + err: errors.ErrStatusAlreadyAssigned, + }, + { + desc: "delete enabled client with failed to change status", + id: enabledClient1.ID, + token: validToken, + client: enabledClient1, + identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID}, + authorizeResponse: &magistrala.AuthorizeRes{Authorized: true}, + retrieveByIDResponse: enabledClient1, + changeStatusResponse: mgclients.Client{}, + changeStatusErr: repoerr.ErrMalformedEntity, + err: svcerr.ErrUpdateEntity, }, } - for _, tc := range cases2 { - pm := mgclients.Page{ - Offset: 0, - Limit: 100, - Status: tc.status, + for _, tc := range cases { + repoCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: tc.token}).Return(tc.identifyResponse, tc.identifyErr) + repoCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(tc.authorizeResponse, tc.authorizeErr) + repoCall2 := cRepo.On("CheckSuperAdmin", context.Background(), mock.Anything).Return(tc.checkSuperAdminErr) + repoCall3 := cRepo.On("RetrieveByID", context.Background(), tc.id).Return(tc.retrieveByIDResponse, tc.retrieveByIDErr) + repoCall4 := cRepo.On("ChangeStatus", context.Background(), mock.Anything).Return(tc.changeStatusResponse, tc.changeStatusErr) + + err := svc.DeleteClient(context.Background(), tc.token, tc.id) + assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err)) + if tc.err == nil { + ok := repoCall3.Parent.AssertCalled(t, "RetrieveByID", context.Background(), tc.id) + assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc)) + ok = repoCall4.Parent.AssertCalled(t, "ChangeStatus", context.Background(), mock.Anything) + assert.True(t, ok, fmt.Sprintf("ChangeStatus was not called on %s", tc.desc)) } - authCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: client.ID}, nil) - authCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) - repoCall := cRepo.On("RetrieveAll", context.Background(), mock.Anything).Return(tc.response, nil) - - page, err := svc.ListClients(context.Background(), validToken, pm) - require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) - size := uint64(len(page.Clients)) - assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size)) - authCall.Unset() - authCall1.Unset() repoCall.Unset() + repoCall1.Unset() + repoCall2.Unset() + repoCall3.Unset() + repoCall4.Unset() } } @@ -2603,7 +2590,7 @@ func TestOAuthCallback(t *testing.T) { addPoliciesErr error saveResponse mgclients.Client saveErr error - deletePoliciesResponse *magistrala.DeletePoliciesRes + deletePoliciesResponse *magistrala.DeletePolicyRes deletePoliciesErr error authorizeResponse *magistrala.AuthorizeRes authorizeErr error @@ -2728,7 +2715,6 @@ func TestOAuthCallback(t *testing.T) { err: svcerr.ErrAuthorization, }, } - for _, tc := range cases { id := tc.saveResponse.ID if tc.retrieveByIdentityResponse.ID != "" { diff --git a/users/tracing/tracing.go b/users/tracing/tracing.go index d9777fc98a..ff93c915b6 100644 --- a/users/tracing/tracing.go +++ b/users/tracing/tracing.go @@ -202,3 +202,11 @@ func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, client mgclients return tm.svc.OAuthCallback(ctx, client) } + +// DeleteClient traces the "DeleteClient" operation of the wrapped clients.Service. +func (tm *tracingMiddleware) DeleteClient(ctx context.Context, token, id string) error { + ctx, span := tm.tracer.Start(ctx, "svc_delete_client", trace.WithAttributes(attribute.String("id", id))) + defer span.End() + + return tm.svc.DeleteClient(ctx, token, id) +}