From b78928c998afa1281eda13302cf503bb1190e9fb Mon Sep 17 00:00:00 2001 From: Burak Sekili Date: Fri, 19 Nov 2021 16:32:38 +0300 Subject: [PATCH] NOISSUE - Listing Policies (#1498) * allow admin to fetch all things Signed-off-by: Burak Sekili * enable users to fetch their own things via owner field in db Signed-off-by: Burak Sekili * add listpolicies RPC Signed-off-by: Burak Sekili * add listPolicies gRPC methods for client and server, and update keto initialization Signed-off-by: Burak Sekili * update fetching things method Signed-off-by: Burak Sekili * remove log Signed-off-by: Burak Sekili * update retrieving policies Signed-off-by: Burak Sekili * fix linter error Signed-off-by: Burak Sekili * update mock Signed-off-by: Burak Sekili * remove checking subject set while parsing subject sets Signed-off-by: Burak Sekili * move subject declaration to constant value Signed-off-by: Burak Sekili --- auth.pb.go | 609 +++++++++++++++++++++++++--- auth.proto | 11 + auth/api/grpc/client.go | 36 ++ auth/api/grpc/endpoint.go | 12 + auth/api/grpc/requests.go | 6 + auth/api/grpc/responses.go | 4 + auth/api/grpc/server.go | 24 ++ auth/api/logging.go | 13 + auth/api/metrics.go | 9 + auth/keto/policies.go | 82 +++- auth/mocks/policies.go | 15 + auth/policies.go | 11 + auth/service.go | 12 + auth/service_test.go | 31 ++ bootstrap/mocks/users.go | 4 + cmd/auth/main.go | 15 +- consumers/notifiers/mocks/auth.go | 4 + things/api/things/http/transport.go | 18 +- things/mocks/auth.go | 9 + things/postgres/things.go | 12 + things/service.go | 68 ++-- things/standalone/standalone.go | 4 + twins/mocks/auth.go | 4 + users/mocks/authn.go | 4 + 24 files changed, 922 insertions(+), 95 deletions(-) diff --git a/auth.pb.go b/auth.pb.go index 0f347d38..1235788f 100644 --- a/auth.pb.go +++ b/auth.pb.go @@ -784,6 +784,116 @@ func (m *DeletePolicyRes) GetDeleted() bool { return false } +type ListPoliciesReq struct { + Sub string `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"` + Obj string `protobuf:"bytes,2,opt,name=obj,proto3" json:"obj,omitempty"` + Act string `protobuf:"bytes,3,opt,name=act,proto3" json:"act,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPoliciesReq) Reset() { *m = ListPoliciesReq{} } +func (m *ListPoliciesReq) String() string { return proto.CompactTextString(m) } +func (*ListPoliciesReq) ProtoMessage() {} +func (*ListPoliciesReq) Descriptor() ([]byte, []int) { + return fileDescriptor_8bbd6f3875b0e874, []int{14} +} +func (m *ListPoliciesReq) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPoliciesReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPoliciesReq.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPoliciesReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPoliciesReq.Merge(m, src) +} +func (m *ListPoliciesReq) XXX_Size() int { + return m.Size() +} +func (m *ListPoliciesReq) XXX_DiscardUnknown() { + xxx_messageInfo_ListPoliciesReq.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPoliciesReq proto.InternalMessageInfo + +func (m *ListPoliciesReq) GetSub() string { + if m != nil { + return m.Sub + } + return "" +} + +func (m *ListPoliciesReq) GetObj() string { + if m != nil { + return m.Obj + } + return "" +} + +func (m *ListPoliciesReq) GetAct() string { + if m != nil { + return m.Act + } + return "" +} + +type ListPoliciesRes struct { + Policies []string `protobuf:"bytes,1,rep,name=policies,proto3" json:"policies,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPoliciesRes) Reset() { *m = ListPoliciesRes{} } +func (m *ListPoliciesRes) String() string { return proto.CompactTextString(m) } +func (*ListPoliciesRes) ProtoMessage() {} +func (*ListPoliciesRes) Descriptor() ([]byte, []int) { + return fileDescriptor_8bbd6f3875b0e874, []int{15} +} +func (m *ListPoliciesRes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPoliciesRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPoliciesRes.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPoliciesRes) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPoliciesRes.Merge(m, src) +} +func (m *ListPoliciesRes) XXX_Size() int { + return m.Size() +} +func (m *ListPoliciesRes) XXX_DiscardUnknown() { + xxx_messageInfo_ListPoliciesRes.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPoliciesRes proto.InternalMessageInfo + +func (m *ListPoliciesRes) GetPolicies() []string { + if m != nil { + return m.Policies + } + return nil +} + type Assignment struct { Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` GroupID string `protobuf:"bytes,2,opt,name=groupID,proto3" json:"groupID,omitempty"` @@ -797,7 +907,7 @@ func (m *Assignment) Reset() { *m = Assignment{} } func (m *Assignment) String() string { return proto.CompactTextString(m) } func (*Assignment) ProtoMessage() {} func (*Assignment) Descriptor() ([]byte, []int) { - return fileDescriptor_8bbd6f3875b0e874, []int{14} + return fileDescriptor_8bbd6f3875b0e874, []int{16} } func (m *Assignment) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -862,7 +972,7 @@ func (m *MembersReq) Reset() { *m = MembersReq{} } func (m *MembersReq) String() string { return proto.CompactTextString(m) } func (*MembersReq) ProtoMessage() {} func (*MembersReq) Descriptor() ([]byte, []int) { - return fileDescriptor_8bbd6f3875b0e874, []int{15} + return fileDescriptor_8bbd6f3875b0e874, []int{17} } func (m *MembersReq) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -941,7 +1051,7 @@ func (m *MembersRes) Reset() { *m = MembersRes{} } func (m *MembersRes) String() string { return proto.CompactTextString(m) } func (*MembersRes) ProtoMessage() {} func (*MembersRes) Descriptor() ([]byte, []int) { - return fileDescriptor_8bbd6f3875b0e874, []int{16} + return fileDescriptor_8bbd6f3875b0e874, []int{18} } func (m *MembersRes) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1020,6 +1130,8 @@ func init() { proto.RegisterType((*AddPolicyRes)(nil), "mainflux.AddPolicyRes") proto.RegisterType((*DeletePolicyReq)(nil), "mainflux.DeletePolicyReq") proto.RegisterType((*DeletePolicyRes)(nil), "mainflux.DeletePolicyRes") + proto.RegisterType((*ListPoliciesReq)(nil), "mainflux.ListPoliciesReq") + proto.RegisterType((*ListPoliciesRes)(nil), "mainflux.ListPoliciesRes") proto.RegisterType((*Assignment)(nil), "mainflux.Assignment") proto.RegisterType((*MembersReq)(nil), "mainflux.MembersReq") proto.RegisterType((*MembersRes)(nil), "mainflux.MembersRes") @@ -1028,51 +1140,54 @@ func init() { func init() { proto.RegisterFile("auth.proto", fileDescriptor_8bbd6f3875b0e874) } var fileDescriptor_8bbd6f3875b0e874 = []byte{ - // 697 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcb, 0x6e, 0xd3, 0x40, - 0x14, 0xcd, 0xfb, 0x71, 0x69, 0xd2, 0x32, 0xaa, 0x82, 0x31, 0x22, 0x14, 0xaf, 0x2a, 0x21, 0xb9, - 0xa8, 0x80, 0x60, 0x03, 0x55, 0x5a, 0x17, 0x64, 0x21, 0x04, 0x32, 0x45, 0x62, 0xeb, 0x24, 0x93, - 0xc4, 0xe0, 0x47, 0xf0, 0x8c, 0x0b, 0x66, 0xc1, 0x1f, 0xb0, 0xe7, 0x27, 0xf8, 0x0f, 0x96, 0x7c, - 0x02, 0x2a, 0x3f, 0x82, 0xe6, 0xe1, 0x78, 0x1a, 0xec, 0x82, 0xd4, 0xdd, 0x9c, 0x9b, 0x7b, 0xcf, - 0xb9, 0x13, 0xcf, 0x39, 0x00, 0x6e, 0x42, 0x17, 0xe6, 0x32, 0x8e, 0x68, 0x84, 0x3a, 0x81, 0xeb, - 0x85, 0x33, 0x3f, 0xf9, 0xa4, 0xdf, 0x98, 0x47, 0xd1, 0xdc, 0xc7, 0x7b, 0xbc, 0x3e, 0x4e, 0x66, - 0x7b, 0x38, 0x58, 0xd2, 0x54, 0xb4, 0x19, 0x4f, 0xa0, 0x3f, 0x9a, 0x4c, 0x30, 0x21, 0x87, 0xe9, - 0x73, 0x9c, 0x3a, 0xf8, 0x03, 0xda, 0x86, 0x26, 0x8d, 0xde, 0xe3, 0x50, 0xab, 0xee, 0x54, 0x77, - 0xbb, 0x8e, 0x00, 0x68, 0x00, 0xad, 0xc9, 0xc2, 0x0d, 0x6d, 0x4b, 0xab, 0xf1, 0xb2, 0x44, 0xc6, - 0x01, 0x6c, 0x1e, 0x2d, 0xdc, 0x30, 0xc4, 0xfe, 0xcb, 0x8f, 0x21, 0x8e, 0x25, 0x41, 0xc4, 0xce, - 0x19, 0x01, 0x07, 0xa5, 0x04, 0xb7, 0xa0, 0x7d, 0xb2, 0xf0, 0xc2, 0xb9, 0x6d, 0xb1, 0xc1, 0x53, - 0xd7, 0x4f, 0x70, 0x36, 0xc8, 0x81, 0x71, 0x1b, 0xba, 0x52, 0xa1, 0xb4, 0x65, 0x04, 0xbd, 0xec, - 0x12, 0xb6, 0xc5, 0x56, 0xd0, 0xa0, 0x4d, 0x05, 0xa9, 0x6c, 0xcc, 0x60, 0xe9, 0x1a, 0x37, 0xa1, - 0x79, 0xc2, 0x2f, 0x5a, 0xac, 0x70, 0x1f, 0x36, 0xde, 0x10, 0x1c, 0xdb, 0x53, 0x1c, 0x52, 0x8f, - 0xa6, 0xa8, 0x0f, 0x35, 0x6f, 0x2a, 0x5b, 0x6a, 0xde, 0x94, 0x4d, 0xe1, 0xc0, 0xf5, 0x7c, 0xc9, - 0x2a, 0x80, 0x61, 0x41, 0xc7, 0x26, 0x24, 0xc1, 0x6c, 0xa5, 0xff, 0x9a, 0x40, 0x08, 0x1a, 0x34, - 0x5d, 0x62, 0xad, 0xbe, 0x53, 0xdd, 0xed, 0x39, 0xfc, 0x6c, 0x58, 0xb0, 0x31, 0x4a, 0xe8, 0x22, - 0x8a, 0xbd, 0xcf, 0x9c, 0x69, 0x0b, 0xea, 0x24, 0x19, 0x4b, 0x2a, 0x76, 0x64, 0x95, 0x68, 0xfc, - 0x4e, 0x32, 0xb1, 0x23, 0xab, 0xb8, 0x13, 0xca, 0x69, 0xba, 0x0e, 0x3b, 0x1a, 0xe6, 0x39, 0x16, - 0x82, 0x86, 0xe2, 0xb5, 0x70, 0x2c, 0xf6, 0xea, 0x38, 0x4a, 0x85, 0xab, 0x4e, 0xa7, 0xaf, 0x22, - 0xdf, 0x9b, 0xa4, 0x97, 0x53, 0xcd, 0x59, 0xfe, 0xad, 0xfa, 0x0c, 0x36, 0x2d, 0xec, 0x63, 0x8a, - 0x2f, 0x2b, 0x7c, 0x67, 0x9d, 0x88, 0xb0, 0x47, 0x31, 0xe5, 0xa5, 0x4c, 0x38, 0x83, 0xc6, 0x5b, - 0x80, 0x11, 0x21, 0xde, 0x3c, 0x0c, 0x70, 0x48, 0x4b, 0x0c, 0xa0, 0x41, 0x7b, 0x1e, 0x47, 0xc9, - 0x72, 0xf5, 0x72, 0x32, 0x88, 0x74, 0xe8, 0x04, 0x38, 0x18, 0xe3, 0xd8, 0xb6, 0xe4, 0x06, 0x2b, - 0x6c, 0x7c, 0x01, 0x78, 0xc1, 0xcf, 0xa4, 0xdc, 0x5a, 0xe5, 0xcc, 0x03, 0x68, 0x45, 0xb3, 0x19, - 0xc1, 0xe2, 0x66, 0x0d, 0x47, 0x22, 0xc6, 0xe3, 0x7b, 0x81, 0x47, 0xb5, 0x06, 0x2f, 0x0b, 0xb0, - 0x7a, 0x3b, 0x4d, 0x4e, 0x22, 0xde, 0x8e, 0xaa, 0x4f, 0x84, 0x3e, 0x75, 0x7d, 0xae, 0xdf, 0x70, - 0x04, 0x50, 0x54, 0x6a, 0xc5, 0x2a, 0xf5, 0x22, 0x95, 0x46, 0xae, 0xc2, 0x6e, 0x20, 0x6e, 0x4c, - 0xb4, 0xe6, 0x4e, 0x9d, 0xdd, 0x40, 0xc2, 0xfd, 0xaf, 0x35, 0xe8, 0x71, 0x7b, 0x93, 0xd7, 0x38, - 0x3e, 0xf5, 0x26, 0x18, 0x1d, 0x40, 0xff, 0xc8, 0x0d, 0x95, 0xcc, 0x41, 0x9a, 0x99, 0x45, 0x95, - 0x79, 0x3e, 0x8a, 0xf4, 0xab, 0xf9, 0x2f, 0x32, 0x23, 0x8c, 0x0a, 0x3a, 0x86, 0xbe, 0x4d, 0xd4, - 0xcc, 0x41, 0xd7, 0xf3, 0xb6, 0xb5, 0x2c, 0xd2, 0x07, 0xa6, 0x08, 0x3f, 0x33, 0x0b, 0x3f, 0xf3, - 0x98, 0x85, 0x9f, 0x51, 0x41, 0x87, 0xd0, 0x53, 0xf6, 0xb0, 0x2d, 0x74, 0xed, 0xef, 0x35, 0x78, - 0x98, 0x5c, 0xc0, 0x71, 0x17, 0x3a, 0x22, 0x11, 0x66, 0x29, 0xda, 0x54, 0x76, 0x65, 0x9f, 0xb5, - 0x70, 0xf9, 0xfd, 0xef, 0x75, 0xb8, 0xc2, 0x6c, 0x98, 0xfd, 0x1b, 0x26, 0x34, 0x79, 0x42, 0x20, - 0x94, 0x77, 0x67, 0x91, 0xa1, 0xaf, 0x53, 0x1a, 0x15, 0xf4, 0xe0, 0x22, 0xc5, 0x41, 0x5e, 0x50, - 0xc3, 0xca, 0xa8, 0xa0, 0xc7, 0xd0, 0x5d, 0x99, 0x1f, 0x29, 0x6d, 0x6a, 0xae, 0xe8, 0xc5, 0x75, - 0x22, 0xc7, 0x33, 0x17, 0x9f, 0x1b, 0x57, 0x02, 0x42, 0x2f, 0xae, 0xb3, 0xf1, 0xa7, 0xb0, 0xa1, - 0x7a, 0x51, 0xfd, 0x5e, 0x6b, 0x66, 0xd7, 0x4b, 0x7f, 0x62, 0x3c, 0x8f, 0xa0, 0x25, 0x6c, 0x8a, - 0xb6, 0x15, 0xad, 0x95, 0x71, 0x2f, 0xf8, 0x50, 0x0f, 0xa1, 0x2d, 0x6d, 0xa0, 0x8e, 0xe6, 0xce, - 0xd4, 0x8b, 0xaa, 0xc4, 0xa8, 0x1c, 0x6e, 0xfd, 0x38, 0x1b, 0x56, 0x7f, 0x9e, 0x0d, 0xab, 0xbf, - 0xce, 0x86, 0xd5, 0x6f, 0xbf, 0x87, 0x95, 0x71, 0x8b, 0x93, 0xdf, 0xfb, 0x13, 0x00, 0x00, 0xff, - 0xff, 0x34, 0x04, 0x0d, 0xfa, 0x6c, 0x07, 0x00, 0x00, + // 737 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xce, 0xff, 0xcf, 0xb9, 0x4d, 0xda, 0x3b, 0xaa, 0x72, 0x7d, 0x7d, 0x75, 0x43, 0xf1, 0xaa, + 0x12, 0xc2, 0x45, 0x05, 0x04, 0x1b, 0xa8, 0xd2, 0xba, 0x20, 0x0b, 0x10, 0xc8, 0x14, 0x89, 0xad, + 0x93, 0x4c, 0x92, 0x01, 0xc7, 0x0e, 0x99, 0x71, 0xc1, 0x2c, 0x78, 0x03, 0xf6, 0x3c, 0x12, 0x4b, + 0x1e, 0x01, 0x95, 0x67, 0x60, 0x8f, 0xe6, 0xc7, 0xf1, 0x34, 0xd8, 0x05, 0xd1, 0xdd, 0xf9, 0x8e, + 0xcf, 0xf9, 0xbe, 0x33, 0x9e, 0x39, 0x1f, 0x80, 0x1f, 0xb3, 0x99, 0xbd, 0x58, 0x46, 0x2c, 0x42, + 0xad, 0xb9, 0x4f, 0xc2, 0x49, 0x10, 0xbf, 0x33, 0xff, 0x9b, 0x46, 0xd1, 0x34, 0xc0, 0x7b, 0x22, + 0x3f, 0x8c, 0x27, 0x7b, 0x78, 0xbe, 0x60, 0x89, 0x2c, 0xb3, 0xee, 0x43, 0x77, 0x30, 0x1a, 0x61, + 0x4a, 0x0f, 0x93, 0x47, 0x38, 0xf1, 0xf0, 0x1b, 0xb4, 0x0d, 0x75, 0x16, 0xbd, 0xc6, 0xa1, 0x51, + 0xde, 0x29, 0xef, 0xb6, 0x3d, 0x09, 0x50, 0x0f, 0x1a, 0xa3, 0x99, 0x1f, 0xba, 0x8e, 0x51, 0x11, + 0x69, 0x85, 0xac, 0x03, 0xd8, 0x3c, 0x9a, 0xf9, 0x61, 0x88, 0x83, 0xa7, 0x6f, 0x43, 0xbc, 0x54, + 0x04, 0x11, 0x8f, 0x53, 0x02, 0x01, 0x0a, 0x09, 0xae, 0x40, 0xf3, 0x64, 0x46, 0xc2, 0xa9, 0xeb, + 0xf0, 0xc6, 0x53, 0x3f, 0x88, 0x71, 0xda, 0x28, 0x80, 0x75, 0x15, 0xda, 0x4a, 0xa1, 0xb0, 0x64, + 0x00, 0x9d, 0xf4, 0x10, 0xae, 0xc3, 0x47, 0x30, 0xa0, 0xc9, 0x24, 0xa9, 0x2a, 0x4c, 0x61, 0xe1, + 0x18, 0xff, 0x43, 0xfd, 0x44, 0x1c, 0x34, 0x5f, 0xe1, 0x16, 0x6c, 0xbc, 0xa0, 0x78, 0xe9, 0x8e, + 0x71, 0xc8, 0x08, 0x4b, 0x50, 0x17, 0x2a, 0x64, 0xac, 0x4a, 0x2a, 0x64, 0xcc, 0xbb, 0xf0, 0xdc, + 0x27, 0x81, 0x62, 0x95, 0xc0, 0x72, 0xa0, 0xe5, 0x52, 0x1a, 0x63, 0x3e, 0xd2, 0x6f, 0x75, 0x20, + 0x04, 0x35, 0x96, 0x2c, 0xb0, 0x51, 0xdd, 0x29, 0xef, 0x76, 0x3c, 0x11, 0x5b, 0x0e, 0x6c, 0x0c, + 0x62, 0x36, 0x8b, 0x96, 0xe4, 0xbd, 0x60, 0xda, 0x82, 0x2a, 0x8d, 0x87, 0x8a, 0x8a, 0x87, 0x3c, + 0x13, 0x0d, 0x5f, 0x29, 0x26, 0x1e, 0xf2, 0x8c, 0x3f, 0x62, 0x82, 0xa6, 0xed, 0xf1, 0xd0, 0xb2, + 0xcf, 0xb1, 0x50, 0xd4, 0x97, 0xaf, 0x45, 0x60, 0x39, 0x57, 0xcb, 0xd3, 0x32, 0x42, 0x75, 0x3c, + 0x7e, 0x16, 0x05, 0x64, 0x94, 0x5c, 0x4e, 0x35, 0x63, 0xf9, 0xb5, 0xea, 0x43, 0xd8, 0x74, 0x70, + 0x80, 0x19, 0xbe, 0xac, 0xf0, 0xb5, 0x75, 0x22, 0xca, 0x1f, 0xc5, 0x58, 0xa4, 0x52, 0xe1, 0x14, + 0x72, 0xd5, 0xc7, 0x84, 0x32, 0x51, 0x4a, 0x30, 0xfd, 0x73, 0xd5, 0xeb, 0xeb, 0x44, 0x14, 0x99, + 0xd0, 0x5a, 0x28, 0x68, 0x94, 0x77, 0xaa, 0xbb, 0x6d, 0x6f, 0x85, 0xad, 0x97, 0x00, 0x03, 0x4a, + 0xc9, 0x34, 0x9c, 0xe3, 0x90, 0x15, 0x2c, 0x9e, 0x01, 0xcd, 0xe9, 0x32, 0x8a, 0x17, 0xab, 0x17, + 0x9b, 0x42, 0xce, 0x3c, 0xc7, 0xf3, 0x21, 0x5e, 0xba, 0x8e, 0x9a, 0x61, 0x85, 0xad, 0x0f, 0x00, + 0x4f, 0x44, 0x4c, 0x8b, 0x57, 0xba, 0x98, 0xb9, 0x07, 0x8d, 0x68, 0x32, 0xa1, 0x58, 0x9e, 0xad, + 0xe6, 0x29, 0xc4, 0x79, 0x02, 0x32, 0x27, 0xcc, 0xa8, 0x89, 0xb4, 0x04, 0xab, 0x37, 0x5b, 0x17, + 0x24, 0xf2, 0xcd, 0xea, 0xfa, 0x54, 0xea, 0x33, 0x3f, 0x10, 0xfa, 0x35, 0x4f, 0x02, 0x4d, 0xa5, + 0x92, 0xaf, 0x52, 0xcd, 0x53, 0xa9, 0x65, 0x2a, 0xfc, 0x04, 0xf2, 0xc4, 0xd4, 0xa8, 0x8b, 0x5f, + 0x9b, 0xc2, 0xfd, 0x8f, 0x15, 0xe8, 0x08, 0x5b, 0xa1, 0xcf, 0xf1, 0xf2, 0x94, 0x8c, 0x30, 0x3a, + 0x80, 0xee, 0x91, 0x1f, 0x6a, 0x5e, 0x87, 0x0c, 0x3b, 0xb5, 0x48, 0xfb, 0xbc, 0x05, 0x9a, 0x7f, + 0x67, 0x5f, 0x94, 0x37, 0x59, 0x25, 0x74, 0x0c, 0x5d, 0x97, 0xea, 0x5e, 0x87, 0xfe, 0xcd, 0xca, + 0xd6, 0x3c, 0xd0, 0xec, 0xd9, 0xd2, 0x74, 0xed, 0xd4, 0x74, 0xed, 0x63, 0x6e, 0xba, 0x56, 0x09, + 0x1d, 0x42, 0x47, 0x9b, 0xc3, 0x75, 0xd0, 0x3f, 0x3f, 0x8f, 0x21, 0x4c, 0xec, 0x02, 0x8e, 0x1b, + 0xd0, 0x92, 0x4e, 0x34, 0x49, 0xd0, 0xa6, 0x36, 0x2b, 0xbf, 0xd6, 0xdc, 0xe1, 0xf7, 0xbf, 0x57, + 0xe1, 0x2f, 0xbe, 0xfe, 0xe9, 0xdf, 0xb0, 0xa1, 0x2e, 0x9c, 0x09, 0xa1, 0xac, 0x3a, 0xb5, 0x2a, + 0x73, 0x9d, 0xd2, 0x2a, 0xa1, 0xdb, 0x17, 0x29, 0xf6, 0xb2, 0x84, 0x6e, 0x92, 0x56, 0x09, 0xdd, + 0x83, 0xf6, 0xca, 0x74, 0x90, 0x56, 0xa6, 0xfb, 0x99, 0x99, 0x9f, 0xa7, 0xaa, 0x3d, 0x75, 0x8f, + 0x73, 0xed, 0x9a, 0x31, 0x99, 0xf9, 0x79, 0xde, 0xfe, 0x00, 0x36, 0x74, 0x0f, 0xd0, 0xef, 0x6b, + 0xcd, 0x64, 0xcc, 0xc2, 0x4f, 0x8a, 0x47, 0xdf, 0x6a, 0x9d, 0x67, 0xcd, 0x36, 0xcc, 0xc2, 0x4f, + 0x9c, 0xe7, 0x2e, 0x34, 0xe4, 0xba, 0xa3, 0x6d, 0x6d, 0xe6, 0x95, 0x01, 0x5c, 0x70, 0xe1, 0x77, + 0xa0, 0xa9, 0xd6, 0x49, 0x6f, 0xcd, 0x36, 0xdc, 0xcc, 0xcb, 0x52, 0xab, 0x74, 0xb8, 0xf5, 0xf9, + 0xac, 0x5f, 0xfe, 0x72, 0xd6, 0x2f, 0x7f, 0x3d, 0xeb, 0x97, 0x3f, 0x7d, 0xeb, 0x97, 0x86, 0x0d, + 0x41, 0x7e, 0xf3, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x8f, 0x30, 0xea, 0x2c, 0x08, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1272,6 +1387,7 @@ 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) DeletePolicy(ctx context.Context, in *DeletePolicyReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) + ListPolicies(ctx context.Context, in *ListPoliciesReq, opts ...grpc.CallOption) (*ListPoliciesRes, error) Assign(ctx context.Context, in *Assignment, opts ...grpc.CallOption) (*empty.Empty, error) Members(ctx context.Context, in *MembersReq, opts ...grpc.CallOption) (*MembersRes, error) } @@ -1329,6 +1445,15 @@ func (c *authServiceClient) DeletePolicy(ctx context.Context, in *DeletePolicyRe return out, nil } +func (c *authServiceClient) ListPolicies(ctx context.Context, in *ListPoliciesReq, opts ...grpc.CallOption) (*ListPoliciesRes, error) { + out := new(ListPoliciesRes) + err := c.cc.Invoke(ctx, "/mainflux.AuthService/ListPolicies", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *authServiceClient) Assign(ctx context.Context, in *Assignment, opts ...grpc.CallOption) (*empty.Empty, error) { out := new(empty.Empty) err := c.cc.Invoke(ctx, "/mainflux.AuthService/Assign", in, out, opts...) @@ -1354,6 +1479,7 @@ type AuthServiceServer interface { Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error) AddPolicy(context.Context, *AddPolicyReq) (*AddPolicyRes, error) DeletePolicy(context.Context, *DeletePolicyReq) (*DeletePolicyRes, error) + ListPolicies(context.Context, *ListPoliciesReq) (*ListPoliciesRes, error) Assign(context.Context, *Assignment) (*empty.Empty, error) Members(context.Context, *MembersReq) (*MembersRes, error) } @@ -1377,6 +1503,9 @@ func (*UnimplementedAuthServiceServer) AddPolicy(ctx context.Context, req *AddPo func (*UnimplementedAuthServiceServer) DeletePolicy(ctx context.Context, req *DeletePolicyReq) (*DeletePolicyRes, error) { return nil, status.Errorf(codes.Unimplemented, "method DeletePolicy not implemented") } +func (*UnimplementedAuthServiceServer) ListPolicies(ctx context.Context, req *ListPoliciesReq) (*ListPoliciesRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPolicies not implemented") +} func (*UnimplementedAuthServiceServer) Assign(ctx context.Context, req *Assignment) (*empty.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Assign not implemented") } @@ -1478,6 +1607,24 @@ func _AuthService_DeletePolicy_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _AuthService_ListPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPoliciesReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).ListPolicies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/mainflux.AuthService/ListPolicies", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).ListPolicies(ctx, req.(*ListPoliciesReq)) + } + return interceptor(ctx, in, info, handler) +} + func _AuthService_Assign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(Assignment) if err := dec(in); err != nil { @@ -1538,6 +1685,10 @@ var _AuthService_serviceDesc = grpc.ServiceDesc{ MethodName: "DeletePolicy", Handler: _AuthService_DeletePolicy_Handler, }, + { + MethodName: "ListPolicies", + Handler: _AuthService_ListPolicies_Handler, + }, { MethodName: "Assign", Handler: _AuthService_Assign_Handler, @@ -2118,6 +2269,90 @@ func (m *DeletePolicyRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ListPoliciesReq) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListPoliciesReq) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPoliciesReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Act) > 0 { + i -= len(m.Act) + copy(dAtA[i:], m.Act) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Act))) + i-- + dAtA[i] = 0x1a + } + if len(m.Obj) > 0 { + i -= len(m.Obj) + copy(dAtA[i:], m.Obj) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Obj))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sub) > 0 { + i -= len(m.Sub) + copy(dAtA[i:], m.Sub) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Sub))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ListPoliciesRes) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListPoliciesRes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPoliciesRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Policies) > 0 { + for iNdEx := len(m.Policies) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Policies[iNdEx]) + copy(dAtA[i:], m.Policies[iNdEx]) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Policies[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *Assignment) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2561,6 +2796,48 @@ func (m *DeletePolicyRes) Size() (n int) { return n } +func (m *ListPoliciesReq) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sub) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.Obj) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.Act) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ListPoliciesRes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Policies) > 0 { + for _, s := range m.Policies { + l = len(s) + n += 1 + l + sovAuth(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *Assignment) Size() (n int) { if m == nil { return 0 @@ -4191,6 +4468,242 @@ func (m *DeletePolicyRes) Unmarshal(dAtA []byte) error { } return nil } +func (m *ListPoliciesReq) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListPoliciesReq: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListPoliciesReq: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sub", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sub = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Obj", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Obj = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Act", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Act = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListPoliciesRes) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListPoliciesRes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListPoliciesRes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Policies = append(m.Policies, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Assignment) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/auth.proto b/auth.proto index fd0373a3..aaed00e8 100644 --- a/auth.proto +++ b/auth.proto @@ -20,6 +20,7 @@ service AuthService { rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {} rpc AddPolicy(AddPolicyReq) returns (AddPolicyRes) {} rpc DeletePolicy(DeletePolicyReq) returns (DeletePolicyRes) {} + rpc ListPolicies(ListPoliciesReq) returns (ListPoliciesRes) {} rpc Assign(Assignment) returns(google.protobuf.Empty) {} rpc Members(MembersReq) returns (MembersRes) {} } @@ -95,6 +96,16 @@ message DeletePolicyRes { bool deleted = 1; } +message ListPoliciesReq { + string sub = 1; + string obj = 2; + string act = 3; +} + +message ListPoliciesRes { + repeated string policies = 1; +} + message Assignment { string token = 1; string groupID = 2; diff --git a/auth/api/grpc/client.go b/auth/api/grpc/client.go index d0337674..8e629ad5 100644 --- a/auth/api/grpc/client.go +++ b/auth/api/grpc/client.go @@ -28,6 +28,7 @@ type grpcClient struct { authorize endpoint.Endpoint addPolicy endpoint.Endpoint deletePolicy endpoint.Endpoint + listPolicies endpoint.Endpoint assign endpoint.Endpoint members endpoint.Endpoint timeout time.Duration @@ -76,6 +77,14 @@ func NewClient(tracer opentracing.Tracer, conn *grpc.ClientConn, timeout time.Du decodeDeletePolicyResponse, mainflux.DeletePolicyRes{}, ).Endpoint()), + listPolicies: kitot.TraceClient(tracer, "list_policies")(kitgrpc.NewClient( + conn, + svcName, + "ListPolicies", + encodeListPoliciesRequest, + decodeListPoliciesResponse, + mainflux.ListPoliciesRes{}, + ).Endpoint()), assign: kitot.TraceClient(tracer, "assign")(kitgrpc.NewClient( conn, svcName, @@ -224,6 +233,33 @@ func encodeDeletePolicyRequest(_ context.Context, grpcReq interface{}) (interfac }, nil } +func (client grpcClient) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + ctx, close := context.WithTimeout(ctx, client.timeout) + defer close() + + res, err := client.listPolicies(ctx, listPoliciesReq{Obj: in.GetObj(), Act: in.GetAct(), Sub: in.GetSub()}) + if err != nil { + return &mainflux.ListPoliciesRes{}, err + } + + lpr := res.(listPoliciesRes) + return &mainflux.ListPoliciesRes{Policies: lpr.policies}, err +} + +func decodeListPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { + res := grpcRes.(*mainflux.ListPoliciesRes) + return listPoliciesRes{policies: res.GetPolicies()}, nil +} + +func encodeListPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { + req := grpcReq.(listPoliciesReq) + return &mainflux.ListPoliciesReq{ + Sub: req.Sub, + Obj: req.Obj, + Act: req.Act, + }, nil +} + func (client grpcClient) Members(ctx context.Context, req *mainflux.MembersReq, _ ...grpc.CallOption) (r *mainflux.MembersRes, err error) { ctx, close := context.WithTimeout(ctx, client.timeout) defer close() diff --git a/auth/api/grpc/endpoint.go b/auth/api/grpc/endpoint.go index 3a17e784..f6a0bc3c 100644 --- a/auth/api/grpc/endpoint.go +++ b/auth/api/grpc/endpoint.go @@ -100,6 +100,18 @@ func deletePolicyEndpoint(svc auth.Service) endpoint.Endpoint { } } +func listPoliciesEndpoint(svc auth.Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(listPoliciesReq) + + page, err := svc.ListPolicies(ctx, auth.PolicyReq{Subject: req.Sub, Object: req.Obj, Relation: req.Act}) + if err != nil { + return deletePolicyRes{}, err + } + return listPoliciesRes{policies: page.Policies}, nil + } +} + func assignEndpoint(svc auth.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(assignReq) diff --git a/auth/api/grpc/requests.go b/auth/api/grpc/requests.go index 0acdc203..e524f12e 100644 --- a/auth/api/grpc/requests.go +++ b/auth/api/grpc/requests.go @@ -133,3 +133,9 @@ func (req deletePolicyReq) validate() error { } return nil } + +type listPoliciesReq struct { + Sub string + Obj string + Act string +} diff --git a/auth/api/grpc/responses.go b/auth/api/grpc/responses.go index ba5dc2ae..fd21f4df 100644 --- a/auth/api/grpc/responses.go +++ b/auth/api/grpc/responses.go @@ -24,6 +24,10 @@ type deletePolicyRes struct { deleted bool } +type listPoliciesRes struct { + policies []string +} + type membersRes struct { total uint64 offset uint64 diff --git a/auth/api/grpc/server.go b/auth/api/grpc/server.go index fdf3fc65..4da03626 100644 --- a/auth/api/grpc/server.go +++ b/auth/api/grpc/server.go @@ -25,6 +25,7 @@ type grpcServer struct { authorize kitgrpc.Handler addPolicy kitgrpc.Handler deletePolicy kitgrpc.Handler + listPolicies kitgrpc.Handler assign kitgrpc.Handler members kitgrpc.Handler } @@ -57,6 +58,11 @@ func NewServer(tracer opentracing.Tracer, svc auth.Service) mainflux.AuthService decodeDeletePolicyRequest, encodeDeletePolicyResponse, ), + listPolicies: kitgrpc.NewServer( + kitot.TraceServer(tracer, "list_policies")(listPoliciesEndpoint(svc)), + decodeListPoliciesRequest, + encodeListPoliciesResponse, + ), assign: kitgrpc.NewServer( kitot.TraceServer(tracer, "assign")(assignEndpoint(svc)), decodeAssignRequest, @@ -110,6 +116,14 @@ func (s *grpcServer) DeletePolicy(ctx context.Context, req *mainflux.DeletePolic return res.(*mainflux.DeletePolicyRes), nil } +func (s *grpcServer) ListPolicies(ctx context.Context, req *mainflux.ListPoliciesReq) (*mainflux.ListPoliciesRes, error) { + _, res, err := s.listPolicies.ServeGRPC(ctx, req) + if err != nil { + return nil, encodeError(err) + } + return res.(*mainflux.ListPoliciesRes), nil +} + func (s *grpcServer) Assign(ctx context.Context, token *mainflux.Assignment) (*empty.Empty, error) { _, res, err := s.assign.ServeGRPC(ctx, token) if err != nil { @@ -181,6 +195,16 @@ func encodeDeletePolicyResponse(_ context.Context, grpcRes interface{}) (interfa return &mainflux.DeletePolicyRes{Deleted: res.deleted}, nil } +func decodeListPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { + req := grpcReq.(*mainflux.ListPoliciesReq) + return listPoliciesReq{Sub: req.GetSub(), Obj: req.GetObj(), Act: req.GetAct()}, nil +} + +func encodeListPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) { + res := grpcRes.(listPoliciesRes) + return &mainflux.ListPoliciesRes{Policies: res.policies}, nil +} + func decodeMembersRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { req := grpcReq.(*mainflux.MembersReq) return membersReq{ diff --git a/auth/api/logging.go b/auth/api/logging.go index efae32ed..6ac2d060 100644 --- a/auth/api/logging.go +++ b/auth/api/logging.go @@ -26,6 +26,19 @@ func LoggingMiddleware(svc auth.Service, logger log.Logger) auth.Service { return &loggingMiddleware{logger, svc} } +func (lm *loggingMiddleware) ListPolicies(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method list_policies took %s to complete", time.Since(begin)) + if err != nil { + lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err)) + return + } + lm.logger.Info(fmt.Sprintf("%s without errors.", message)) + }(time.Now()) + + return lm.svc.ListPolicies(ctx, pr) +} + func (lm *loggingMiddleware) Issue(ctx context.Context, token string, newKey auth.Key) (key auth.Key, secret string, err error) { defer func(begin time.Time) { d := "infinite duration" diff --git a/auth/api/metrics.go b/auth/api/metrics.go index af0dfaa8..25e602fc 100644 --- a/auth/api/metrics.go +++ b/auth/api/metrics.go @@ -28,6 +28,15 @@ func MetricsMiddleware(svc auth.Service, counter metrics.Counter, latency metric } } +func (ms *metricsMiddleware) ListPolicies(ctx context.Context, pr auth.PolicyReq) (p auth.PolicyPage, err error) { + defer func(begin time.Time) { + ms.counter.With("method", "list_policies").Add(1) + ms.latency.With("method", "list_policies").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return ms.svc.ListPolicies(ctx, pr) +} + func (ms *metricsMiddleware) Issue(ctx context.Context, token string, key auth.Key) (auth.Key, string, error) { defer func(begin time.Time) { ms.counter.With("method", "issue_key").Add(1) diff --git a/auth/keto/policies.go b/auth/keto/policies.go index 1f00f81d..a462991d 100644 --- a/auth/keto/policies.go +++ b/auth/keto/policies.go @@ -6,6 +6,7 @@ package keto import ( "context" "regexp" + "strings" "github.com/mainflux/mainflux/auth" "github.com/mainflux/mainflux/pkg/errors" @@ -20,16 +21,17 @@ const ( type policyAgent struct { writer acl.WriteServiceClient checker acl.CheckServiceClient + reader acl.ReadServiceClient } // NewPolicyAgent returns a gRPC communication functionalities // to communicate with ORY Keto. -func NewPolicyAgent(checker acl.CheckServiceClient, writer acl.WriteServiceClient) auth.PolicyAgent { - return policyAgent{checker: checker, writer: writer} +func NewPolicyAgent(checker acl.CheckServiceClient, writer acl.WriteServiceClient, reader acl.ReadServiceClient) auth.PolicyAgent { + return policyAgent{checker: checker, writer: writer, reader: reader} } -func (c policyAgent) CheckPolicy(ctx context.Context, pr auth.PolicyReq) error { - res, err := c.checker.Check(context.Background(), &acl.CheckRequest{ +func (pa policyAgent) CheckPolicy(ctx context.Context, pr auth.PolicyReq) error { + res, err := pa.checker.Check(context.Background(), &acl.CheckRequest{ Namespace: ketoNamespace, Object: pr.Object, Relation: pr.Relation, @@ -44,8 +46,19 @@ func (c policyAgent) CheckPolicy(ctx context.Context, pr auth.PolicyReq) error { return nil } -func (c policyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { - trt := c.writer.TransactRelationTuples +func (pa policyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { + var ss *acl.Subject + switch isSubjectSet(pr.Subject) { + case true: + namespace, object, relation := parseSubjectSet(pr.Subject) + ss = &acl.Subject{ + Ref: &acl.Subject_Set{Set: &acl.SubjectSet{Namespace: namespace, Object: object, Relation: relation}}, + } + default: + ss = &acl.Subject{Ref: &acl.Subject_Id{Id: pr.Subject}} + } + + trt := pa.writer.TransactRelationTuples _, err := trt(context.Background(), &acl.TransactRelationTuplesRequest{ RelationTupleDeltas: []*acl.RelationTupleDelta{ { @@ -54,9 +67,7 @@ func (c policyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { Namespace: ketoNamespace, Object: pr.Object, Relation: pr.Relation, - Subject: &acl.Subject{Ref: &acl.Subject_Id{ - Id: pr.Subject, - }}, + Subject: ss, }, }, }, @@ -64,8 +75,8 @@ func (c policyAgent) AddPolicy(ctx context.Context, pr auth.PolicyReq) error { return err } -func (c policyAgent) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error { - trt := c.writer.TransactRelationTuples +func (pa policyAgent) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error { + trt := pa.writer.TransactRelationTuples _, err := trt(context.Background(), &acl.TransactRelationTuplesRequest{ RelationTupleDeltas: []*acl.RelationTupleDelta{ { @@ -84,6 +95,37 @@ func (c policyAgent) DeletePolicy(ctx context.Context, pr auth.PolicyReq) error return err } +func (pa policyAgent) RetrievePolicies(ctx context.Context, pr auth.PolicyReq) ([]*acl.RelationTuple, error) { + var ss *acl.Subject + switch isSubjectSet(pr.Subject) { + case true: + namespace, object, relation := parseSubjectSet(pr.Subject) + ss = &acl.Subject{ + Ref: &acl.Subject_Set{Set: &acl.SubjectSet{Namespace: namespace, Object: object, Relation: relation}}, + } + default: + ss = &acl.Subject{Ref: &acl.Subject_Id{Id: pr.Subject}} + } + + res, err := pa.reader.ListRelationTuples(ctx, &acl.ListRelationTuplesRequest{ + Query: &acl.ListRelationTuplesRequest_Query{ + Namespace: ketoNamespace, + Relation: pr.Relation, + Subject: ss, + }, + }) + if err != nil { + return []*acl.RelationTuple{}, err + } + + tuple := res.GetRelationTuples() + for res.NextPageToken != "" { + tuple = append(tuple, res.GetRelationTuples()...) + } + + return tuple, nil +} + // getSubject returns a 'subject' field for ACL(access control lists). // If the given PolicyReq argument contains a subject as subject set, // it returns subject set; otherwise, it returns a subject. @@ -110,3 +152,21 @@ func isSubjectSet(subject string) bool { } return r.MatchString(subject) } + +func parseSubjectSet(subjectSet string) (namespace, object, relation string) { + r := strings.Split(subjectSet, ":") + if len(r) != 2 { + return + } + namespace = r[0] + + r = strings.Split(r[1], "#") + if len(r) != 2 { + return + } + + object = r[0] + relation = r[1] + + return +} diff --git a/auth/mocks/policies.go b/auth/mocks/policies.go index cd2f6cfa..500a217c 100644 --- a/auth/mocks/policies.go +++ b/auth/mocks/policies.go @@ -8,6 +8,7 @@ import ( "sync" "github.com/mainflux/mainflux/auth" + acl "github.com/ory/keto/proto/ory/keto/acl/v1alpha1" ) type MockSubjectSet struct { @@ -60,3 +61,17 @@ func (pa *policyAgentMock) DeletePolicy(ctx context.Context, pr auth.PolicyReq) } return nil } + +func (pa *policyAgentMock) RetrievePolicies(ctx context.Context, pr auth.PolicyReq) ([]*acl.RelationTuple, error) { + pa.mu.Lock() + defer pa.mu.Unlock() + + ssList := pa.authzDB[pr.Subject] + tuple := []*acl.RelationTuple{} + for _, ss := range ssList { + if ss.Relation == pr.Relation { + tuple = append(tuple, &acl.RelationTuple{Object: ss.Object, Relation: ss.Relation}) + } + } + return tuple, nil +} diff --git a/auth/policies.go b/auth/policies.go index 292f7440..4a7ab1e5 100644 --- a/auth/policies.go +++ b/auth/policies.go @@ -5,6 +5,8 @@ package auth import ( "context" + + acl "github.com/ory/keto/proto/ory/keto/acl/v1alpha1" ) // PolicyReq represents an argument struct for making a policy related @@ -15,6 +17,10 @@ type PolicyReq struct { Relation string } +type PolicyPage struct { + Policies []string +} + // Authz represents a authorization service. It exposes // functionalities through `auth` to perform authorization. type Authz interface { @@ -40,6 +46,9 @@ type Authz interface { // DeletePolicies deletes policies for given subjects. This method is // only allowed to use as an admin. DeletePolicies(ctx context.Context, token, object string, subjectIDs, relations []string) error + + // ListPolicies lists policies based on the given PolicyReq structure. + ListPolicies(ctx context.Context, pr PolicyReq) (PolicyPage, error) } // PolicyAgent facilitates the communication to authorization @@ -58,4 +67,6 @@ type PolicyAgent interface { // DeletePolicy removes a policy. DeletePolicy(ctx context.Context, pr PolicyReq) error + + RetrievePolicies(ctx context.Context, pr PolicyReq) ([]*acl.RelationTuple, error) } diff --git a/auth/service.go b/auth/service.go index 18b9a732..b3d2da03 100644 --- a/auth/service.go +++ b/auth/service.go @@ -236,6 +236,18 @@ func (svc service) AssignGroupAccessRights(ctx context.Context, token, thingGrou return svc.agent.AddPolicy(ctx, PolicyReq{Object: thingGroupID, Relation: memberRelation, Subject: fmt.Sprintf("%s:%s#%s", "members", userGroupID, memberRelation)}) } +func (svc service) ListPolicies(ctx context.Context, pr PolicyReq) (PolicyPage, error) { + res, err := svc.agent.RetrievePolicies(ctx, pr) + if err != nil { + return PolicyPage{}, err + } + var page PolicyPage + for _, tuple := range res { + page.Policies = append(page.Policies, tuple.GetObject()) + } + return page, err +} + func (svc service) tmpKey(duration time.Duration, key Key) (Key, string, error) { key.ExpiresAt = key.IssuedAt.Add(duration) secret, err := svc.tokenizer.Issue(key) diff --git a/auth/service_test.go b/auth/service_test.go index f48388d4..10ae4f4e 100644 --- a/auth/service_test.go +++ b/auth/service_test.go @@ -1235,3 +1235,34 @@ func TestDeletePolicies(t *testing.T) { assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v, got %v", tc.desc, tc.err, err)) } } + +func TestListPolicies(t *testing.T) { + svc := newService() + _, secret, err := svc.Issue(context.Background(), "", auth.Key{Type: auth.UserKey, IssuedAt: time.Now(), IssuerID: id, Subject: email}) + assert.Nil(t, err, fmt.Sprintf("Issuing login key expected to succeed: %s", err)) + + key := auth.Key{ + ID: "id", + Type: auth.APIKey, + IssuerID: id, + Subject: email, + IssuedAt: time.Now(), + } + + _, apiToken, err := svc.Issue(context.Background(), secret, key) + assert.Nil(t, err, fmt.Sprintf("Issuing user's key expected to succeed: %s", err)) + + readPolicy := "read" + pageLen := 15 + + // Add arbitrary policies to the user. + for i := 0; i < pageLen; i++ { + err = svc.AddPolicies(context.Background(), apiToken, fmt.Sprintf("thing-%d", i), []string{id}, []string{readPolicy}) + assert.Nil(t, err, fmt.Sprintf("adding policies expected to succeed: %s", err)) + } + + page, err := svc.ListPolicies(context.Background(), auth.PolicyReq{Subject: id, Relation: readPolicy}) + assert.Nil(t, err, fmt.Sprintf("listing policies expected to succeed: %s", err)) + assert.Equal(t, pageLen, len(page.Policies), fmt.Sprintf("unexpected listing page size, expected %d, got %d: %v", pageLen, len(page.Policies), err)) + +} diff --git a/bootstrap/mocks/users.go b/bootstrap/mocks/users.go index 979317fa..8bf347e6 100644 --- a/bootstrap/mocks/users.go +++ b/bootstrap/mocks/users.go @@ -52,6 +52,10 @@ func (svc serviceMock) DeletePolicy(ctx context.Context, in *mainflux.DeletePoli panic("not implemented") } +func (svc serviceMock) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + panic("not implemented") +} + func (svc serviceMock) Members(ctx context.Context, req *mainflux.MembersReq, _ ...grpc.CallOption) (r *mainflux.MembersRes, err error) { panic("not implemented") } diff --git a/cmd/auth/main.go b/cmd/auth/main.go index b6fba341..0793b747 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -111,9 +111,9 @@ func main() { dbTracer, dbCloser := initJaeger("auth_db", cfg.jaegerURL, logger) defer dbCloser.Close() - reader, writer := initKeto(cfg.ketoHost, cfg.ketoReadPort, cfg.ketoWritePort, logger) + readerConn, writerConn := initKeto(cfg.ketoHost, cfg.ketoReadPort, cfg.ketoWritePort, logger) - svc := newService(db, dbTracer, cfg.secret, logger, reader, writer) + svc := newService(db, dbTracer, cfg.secret, logger, readerConn, writerConn) errs := make(chan error, 2) go startHTTPServer(tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger, errs) @@ -182,10 +182,10 @@ func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, return tracer, closer } -func initKeto(hostAddress, readPort, writePort string, logger logger.Logger) (acl.CheckServiceClient, acl.WriteServiceClient) { +func initKeto(hostAddress, readPort, writePort string, logger logger.Logger) (readerConnection, writerConnection *grpc.ClientConn) { checkConn, err := grpc.Dial(fmt.Sprintf("%s:%s", hostAddress, readPort), grpc.WithInsecure()) if err != nil { - logger.Error(fmt.Sprintf("Failed to dial %s:%s for Keto Check Service: %s", hostAddress, readPort, err)) + logger.Error(fmt.Sprintf("Failed to dial %s:%s for Keto Read Service: %s", hostAddress, readPort, err)) os.Exit(1) } @@ -194,7 +194,8 @@ func initKeto(hostAddress, readPort, writePort string, logger logger.Logger) (ac logger.Error(fmt.Sprintf("Failed to dial %s:%s for Keto Write Service: %s", hostAddress, writePort, err)) os.Exit(1) } - return acl.NewCheckServiceClient(checkConn), acl.NewWriteServiceClient(writeConn) + + return checkConn, writeConn } func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB { @@ -206,14 +207,14 @@ func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB { return db } -func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger logger.Logger, reader acl.CheckServiceClient, writer acl.WriteServiceClient) auth.Service { +func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger logger.Logger, readerConn, writerConn *grpc.ClientConn) auth.Service { database := postgres.NewDatabase(db) keysRepo := tracing.New(postgres.New(database), tracer) groupsRepo := postgres.NewGroupRepo(database) groupsRepo = tracing.GroupRepositoryMiddleware(tracer, groupsRepo) - pa := keto.NewPolicyAgent(reader, writer) + pa := keto.NewPolicyAgent(acl.NewCheckServiceClient(readerConn), acl.NewWriteServiceClient(writerConn), acl.NewReadServiceClient(readerConn)) idProvider := uuid.New() t := jwt.New(secret) diff --git a/consumers/notifiers/mocks/auth.go b/consumers/notifiers/mocks/auth.go index 01ea0924..f451cdad 100644 --- a/consumers/notifiers/mocks/auth.go +++ b/consumers/notifiers/mocks/auth.go @@ -52,6 +52,10 @@ func (svc authServiceMock) DeletePolicy(ctx context.Context, in *mainflux.Delete panic("not implemented") } +func (svc authServiceMock) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + panic("not implemented") +} + func (svc authServiceMock) Members(ctx context.Context, req *mainflux.MembersReq, _ ...grpc.CallOption) (r *mainflux.MembersRes, err error) { panic("not implemented") } diff --git a/things/api/things/http/transport.go b/things/api/things/http/transport.go index e8d238c2..2b9ae593 100644 --- a/things/api/things/http/transport.go +++ b/things/api/things/http/transport.go @@ -31,6 +31,7 @@ const ( dirKey = "dir" metadataKey = "metadata" disconnKey = "disconnected" + sharedKey = "shared" defOffset = 0 defLimit = 10 ) @@ -356,16 +357,21 @@ func decodeList(_ context.Context, r *http.Request) (interface{}, error) { if err != nil { return nil, err } + shared, err := httputil.ReadBoolQuery(r, sharedKey, false) + if err != nil { + return nil, err + } req := listResourcesReq{ token: r.Header.Get("Authorization"), pageMetadata: things.PageMetadata{ - Offset: o, - Limit: l, - Name: n, - Order: or, - Dir: d, - Metadata: m, + Offset: o, + Limit: l, + Name: n, + Order: or, + Dir: d, + Metadata: m, + FetchSharedThings: shared, }, } diff --git a/things/mocks/auth.go b/things/mocks/auth.go index 108bacb0..d6803094 100644 --- a/things/mocks/auth.go +++ b/things/mocks/auth.go @@ -5,6 +5,7 @@ package mocks import ( "context" + "github.com/golang/protobuf/ptypes/empty" "github.com/mainflux/mainflux" "github.com/mainflux/mainflux/things" @@ -24,6 +25,14 @@ type authServiceMock struct { policies map[string][]MockSubjectSet } +func (svc authServiceMock) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + res := mainflux.ListPoliciesRes{} + for key := range svc.policies { + res.Policies = append(res.Policies, key) + } + return &res, nil +} + // NewAuthService creates mock of users service. func NewAuthService(users map[string]string, policies map[string][]MockSubjectSet) mainflux.AuthServiceClient { return &authServiceMock{users, policies} diff --git a/things/postgres/things.go b/things/postgres/things.go index 207fd5d0..2d75518b 100644 --- a/things/postgres/things.go +++ b/things/postgres/things.go @@ -241,10 +241,18 @@ func (tr thingRepository) RetrieveByIDs(ctx context.Context, thingIDs []string, return page, nil } +func getOwnerQuery(fetchSharedThings bool) string { + if fetchSharedThings { + return "" + } + return "owner = :owner" +} + func (tr thingRepository) RetrieveAll(ctx context.Context, owner string, pm things.PageMetadata) (things.Page, error) { nq, name := getNameQuery(pm.Name) oq := getOrderQuery(pm.Order) dq := getDirQuery(pm.Dir) + ownerQuery := getOwnerQuery(pm.FetchSharedThings) m, mq, err := getMetadataQuery(pm.Metadata) if err != nil { return things.Page{}, errors.Wrap(things.ErrSelectEntity, err) @@ -257,6 +265,9 @@ func (tr thingRepository) RetrieveAll(ctx context.Context, owner string, pm thin if nq != "" { query = append(query, nq) } + if ownerQuery != "" { + query = append(query, ownerQuery) + } var whereClause string if len(query) > 0 { @@ -266,6 +277,7 @@ func (tr thingRepository) RetrieveAll(ctx context.Context, owner string, pm thin q := fmt.Sprintf(`SELECT id, name, key, metadata FROM things %s ORDER BY %s %s LIMIT :limit OFFSET :offset;`, whereClause, oq, dq) params := map[string]interface{}{ + "owner": owner, "limit": pm.Limit, "offset": pm.Offset, "name": name, diff --git a/things/service.go b/things/service.go index d1a68aa9..eb1011a4 100644 --- a/things/service.go +++ b/things/service.go @@ -48,10 +48,12 @@ var ( const ( usersObjectKey = "users" + authoritiesObject = "authorities" memberRelationKey = "member" readRelationKey = "read" writeRelationKey = "write" deleteRelationKey = "delete" + adminSubject = "members:authorities#member" ) // Service specifies an API that must be fullfiled by the domain service @@ -142,14 +144,15 @@ type Service interface { // PageMetadata contains page metadata that helps navigation. type PageMetadata struct { - Total uint64 - Offset uint64 `json:"offset,omitempty"` - Limit uint64 `json:"limit,omitempty"` - Name string `json:"name,omitempty"` - Order string `json:"order,omitempty"` - Dir string `json:"dir,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - Disconnected bool // Used for connected or disconnected lists + Total uint64 + Offset uint64 `json:"offset,omitempty"` + Limit uint64 `json:"limit,omitempty"` + Name string `json:"name,omitempty"` + Order string `json:"order,omitempty"` + Dir string `json:"dir,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Disconnected bool // Used for connected or disconnected lists + FetchSharedThings bool // Used for identifying fetching either all or shared things. } var _ Service = (*thingsService)(nil) @@ -223,7 +226,8 @@ func (ts *thingsService) createThing(ctx context.Context, thing *Thing, identity return Thing{}, ErrCreateEntity } - if err := ts.claimOwnership(ctx, ths[0].ID, []string{readRelationKey, writeRelationKey, deleteRelationKey}, []string{identity.GetId()}); err != nil { + ss := fmt.Sprintf("%s:%s#%s", "members", authoritiesObject, memberRelationKey) + if err := ts.claimOwnership(ctx, ths[0].ID, []string{readRelationKey, writeRelationKey, deleteRelationKey}, []string{identity.GetId(), ss}); err != nil { return Thing{}, err } @@ -237,7 +241,9 @@ func (ts *thingsService) UpdateThing(ctx context.Context, token string, thing Th } if err := ts.authorize(ctx, res.GetId(), thing.ID, writeRelationKey); err != nil { - return err + if err := ts.authorize(ctx, res.GetId(), authoritiesObject, memberRelationKey); err != nil { + return err + } } thing.Owner = res.GetEmail() @@ -281,7 +287,9 @@ func (ts *thingsService) UpdateKey(ctx context.Context, token, id, key string) e } if err := ts.authorize(ctx, res.GetId(), id, writeRelationKey); err != nil { - return err + if err := ts.authorize(ctx, res.GetId(), authoritiesObject, memberRelationKey); err != nil { + return err + } } owner := res.GetEmail() @@ -296,7 +304,9 @@ func (ts *thingsService) ViewThing(ctx context.Context, token, id string) (Thing } if err := ts.authorize(ctx, res.GetId(), id, readRelationKey); err != nil { - return Thing{}, err + if err := ts.authorize(ctx, res.GetId(), authoritiesObject, memberRelationKey); err != nil { + return Thing{}, err + } } return ts.things.RetrieveByID(ctx, res.GetEmail(), id) @@ -308,21 +318,31 @@ func (ts *thingsService) ListThings(ctx context.Context, token string, pm PageMe return Page{}, errors.Wrap(ErrUnauthorizedAccess, err) } + subject := res.GetId() + if err := ts.authorize(ctx, res.GetId(), authoritiesObject, memberRelationKey); err == nil { + subject = adminSubject + pm.FetchSharedThings = true + } + + if pm.FetchSharedThings { + req := &mainflux.ListPoliciesReq{Act: "read", Sub: subject} + lpr, err := ts.auth.ListPolicies(ctx, req) + if err != nil { + return Page{}, err + } + + var page Page + for _, thingID := range lpr.Policies { + page.Things = append(page.Things, Thing{ID: thingID}) + } + return page, nil + } + page, err := ts.things.RetrieveAll(ctx, res.GetEmail(), pm) if err != nil { return Page{}, err } - ths := []Thing{} - for _, thing := range page.Things { - for _, action := range []string{readRelationKey, writeRelationKey, deleteRelationKey} { - if err := ts.authorize(ctx, res.GetId(), thing.ID, action); err == nil { - ths = append(ths, thing) - break - } - } - } - page.Things = ths return page, nil } @@ -342,7 +362,9 @@ func (ts *thingsService) RemoveThing(ctx context.Context, token, id string) erro } if err := ts.authorize(ctx, res.GetId(), id, deleteRelationKey); err != nil { - return err + if err := ts.authorize(ctx, res.GetId(), authoritiesObject, memberRelationKey); err != nil { + return err + } } if err := ts.thingCache.Remove(ctx, id); err != nil { diff --git a/things/standalone/standalone.go b/things/standalone/standalone.go index 98a5031d..37b62c68 100644 --- a/things/standalone/standalone.go +++ b/things/standalone/standalone.go @@ -67,6 +67,10 @@ func (repo singleUserRepo) DeletePolicy(ctx context.Context, req *mainflux.Delet return &mainflux.DeletePolicyRes{Deleted: true}, nil } +func (repo singleUserRepo) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + return &mainflux.ListPoliciesRes{}, errUnsupported +} + func (repo singleUserRepo) Members(ctx context.Context, req *mainflux.MembersReq, _ ...grpc.CallOption) (r *mainflux.MembersRes, err error) { return &mainflux.MembersRes{}, errUnsupported } diff --git a/twins/mocks/auth.go b/twins/mocks/auth.go index 703c5537..d99749a2 100644 --- a/twins/mocks/auth.go +++ b/twins/mocks/auth.go @@ -18,6 +18,10 @@ type authServiceClient struct { users map[string]string } +func (svc authServiceClient) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + panic("not implemented") +} + // NewAuthServiceClient creates mock of auth service. func NewAuthServiceClient(users map[string]string) mainflux.AuthServiceClient { return &authServiceClient{users} diff --git a/users/mocks/authn.go b/users/mocks/authn.go index 53a840ed..ead96615 100644 --- a/users/mocks/authn.go +++ b/users/mocks/authn.go @@ -24,6 +24,10 @@ type authServiceMock struct { authz map[string][]SubjectSet } +func (svc authServiceMock) ListPolicies(ctx context.Context, in *mainflux.ListPoliciesReq, opts ...grpc.CallOption) (*mainflux.ListPoliciesRes, error) { + panic("not implemented") +} + // NewAuthService creates mock of users service. func NewAuthService(users map[string]string, authzDB map[string][]SubjectSet) mainflux.AuthServiceClient { return &authServiceMock{users, authzDB}