mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
SMQ-2568 - Check Domain enabled / disabled status during Authn or Authz (#2586)
Signed-off-by: Felix Gateru <felix.gateru@gmail.com> Signed-off-by: Arvindh <arvindh91@gmail.com> Co-authored-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@@ -116,21 +117,28 @@ var File_domains_v1_domains_proto protoreflect.FileDescriptor
|
||||
var file_domains_v1_domains_proto_rawDesc = []byte{
|
||||
0x0a, 0x18, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x64, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x29, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
||||
0x55, 0x73, 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, 0x1f, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52,
|
||||
0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
|
||||
0x69, 0x64, 0x32, 0x61, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x53, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73,
|
||||
0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x19, 0x2e,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||
0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x62, 0x73, 0x6d, 0x61, 0x63, 0x68, 0x2f, 0x73, 0x75, 0x70, 0x65,
|
||||
0x72, 0x6d, 0x71, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29,
|
||||
0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 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, 0x1f, 0x0a, 0x0d, 0x44, 0x65, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0xb1, 0x01, 0x0a, 0x0e, 0x44,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a,
|
||||
0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x44,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
|
||||
0x71, 0x1a, 0x19, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44,
|
||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x22, 0x00, 0x12, 0x4e,
|
||||
0x0a, 0x0e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79,
|
||||
0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74,
|
||||
0x72, 0x69, 0x65, 0x76, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1c,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69,
|
||||
0x65, 0x76, 0x65, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x42, 0x35,
|
||||
0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x62, 0x73,
|
||||
0x6d, 0x61, 0x63, 0x68, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x6d, 0x71, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -147,14 +155,18 @@ func file_domains_v1_domains_proto_rawDescGZIP() []byte {
|
||||
|
||||
var file_domains_v1_domains_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_domains_v1_domains_proto_goTypes = []any{
|
||||
(*DeleteUserRes)(nil), // 0: domains.v1.DeleteUserRes
|
||||
(*DeleteUserReq)(nil), // 1: domains.v1.DeleteUserReq
|
||||
(*DeleteUserRes)(nil), // 0: domains.v1.DeleteUserRes
|
||||
(*DeleteUserReq)(nil), // 1: domains.v1.DeleteUserReq
|
||||
(*v1.RetrieveEntityReq)(nil), // 2: common.v1.RetrieveEntityReq
|
||||
(*v1.RetrieveEntityRes)(nil), // 3: common.v1.RetrieveEntityRes
|
||||
}
|
||||
var file_domains_v1_domains_proto_depIdxs = []int32{
|
||||
1, // 0: domains.v1.DomainsService.DeleteUserFromDomains:input_type -> domains.v1.DeleteUserReq
|
||||
0, // 1: domains.v1.DomainsService.DeleteUserFromDomains:output_type -> domains.v1.DeleteUserRes
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
2, // 1: domains.v1.DomainsService.RetrieveEntity:input_type -> common.v1.RetrieveEntityReq
|
||||
0, // 2: domains.v1.DomainsService.DeleteUserFromDomains:output_type -> domains.v1.DeleteUserRes
|
||||
3, // 3: domains.v1.DomainsService.RetrieveEntity:output_type -> common.v1.RetrieveEntityRes
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
|
||||
@@ -11,6 +11,7 @@ package v1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
v1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
@@ -23,6 +24,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
DomainsService_DeleteUserFromDomains_FullMethodName = "/domains.v1.DomainsService/DeleteUserFromDomains"
|
||||
DomainsService_RetrieveEntity_FullMethodName = "/domains.v1.DomainsService/RetrieveEntity"
|
||||
)
|
||||
|
||||
// DomainsServiceClient is the client API for DomainsService service.
|
||||
@@ -33,6 +35,7 @@ const (
|
||||
// domains functionalities for SuperMQ services.
|
||||
type DomainsServiceClient interface {
|
||||
DeleteUserFromDomains(ctx context.Context, in *DeleteUserReq, opts ...grpc.CallOption) (*DeleteUserRes, error)
|
||||
RetrieveEntity(ctx context.Context, in *v1.RetrieveEntityReq, opts ...grpc.CallOption) (*v1.RetrieveEntityRes, error)
|
||||
}
|
||||
|
||||
type domainsServiceClient struct {
|
||||
@@ -53,6 +56,16 @@ func (c *domainsServiceClient) DeleteUserFromDomains(ctx context.Context, in *De
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *domainsServiceClient) RetrieveEntity(ctx context.Context, in *v1.RetrieveEntityReq, opts ...grpc.CallOption) (*v1.RetrieveEntityRes, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(v1.RetrieveEntityRes)
|
||||
err := c.cc.Invoke(ctx, DomainsService_RetrieveEntity_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DomainsServiceServer is the server API for DomainsService service.
|
||||
// All implementations must embed UnimplementedDomainsServiceServer
|
||||
// for forward compatibility.
|
||||
@@ -61,6 +74,7 @@ func (c *domainsServiceClient) DeleteUserFromDomains(ctx context.Context, in *De
|
||||
// domains functionalities for SuperMQ services.
|
||||
type DomainsServiceServer interface {
|
||||
DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error)
|
||||
RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error)
|
||||
mustEmbedUnimplementedDomainsServiceServer()
|
||||
}
|
||||
|
||||
@@ -74,6 +88,9 @@ type UnimplementedDomainsServiceServer struct{}
|
||||
func (UnimplementedDomainsServiceServer) DeleteUserFromDomains(context.Context, *DeleteUserReq) (*DeleteUserRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteUserFromDomains not implemented")
|
||||
}
|
||||
func (UnimplementedDomainsServiceServer) RetrieveEntity(context.Context, *v1.RetrieveEntityReq) (*v1.RetrieveEntityRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RetrieveEntity not implemented")
|
||||
}
|
||||
func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {}
|
||||
func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -113,6 +130,24 @@ func _DomainsService_DeleteUserFromDomains_Handler(srv interface{}, ctx context.
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DomainsService_RetrieveEntity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(v1.RetrieveEntityReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DomainsServiceServer).RetrieveEntity(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DomainsService_RetrieveEntity_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DomainsServiceServer).RetrieveEntity(ctx, req.(*v1.RetrieveEntityReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// DomainsService_ServiceDesc is the grpc.ServiceDesc for DomainsService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -124,6 +159,10 @@ var DomainsService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "DeleteUserFromDomains",
|
||||
Handler: _DomainsService_DeleteUserFromDomains_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RetrieveEntity",
|
||||
Handler: _DomainsService_RetrieveEntity_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "domains/v1/domains.proto",
|
||||
|
||||
@@ -81,7 +81,6 @@ func AuthorizationMiddleware(svc channels.Service, repo channels.Repository, aut
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session authn.Session, chs ...channels.Channel) ([]channels.Channel, error) {
|
||||
// If domain is disabled , then this authorization will fail for all non-admin domain users
|
||||
if err := am.extAuthorize(ctx, channels.DomainOpCreateChannel, authz.PolicyReq{
|
||||
Domain: session.DomainID,
|
||||
SubjectType: policies.UserType,
|
||||
|
||||
+23
-7
@@ -25,6 +25,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/events"
|
||||
"github.com/absmach/supermq/pkg/events/store"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
@@ -48,12 +49,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "bootstrap"
|
||||
envPrefixDB = "SMQ_BOOTSTRAP_DB_"
|
||||
envPrefixHTTP = "SMQ_BOOTSTRAP_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
defDB = "bootstrap"
|
||||
defSvcHTTPPort = "9013"
|
||||
svcName = "bootstrap"
|
||||
envPrefixDB = "SMQ_BOOTSTRAP_DB_"
|
||||
envPrefixHTTP = "SMQ_BOOTSTRAP_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
defDB = "bootstrap"
|
||||
defSvcHTTPPort = "9013"
|
||||
|
||||
stream = "events.supermq.clients"
|
||||
streamID = "supermq.bootstrap"
|
||||
@@ -148,7 +150,21 @@ func main() {
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnClient.Secure())
|
||||
defer authnClient.Close()
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domainsAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg, domainsAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+17
-1
@@ -29,6 +29,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
@@ -61,6 +62,7 @@ const (
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
envPrefixClients = "SMQ_CLIENTS_AUTH_GRPC_"
|
||||
envPrefixGroups = "SMQ_GROUPS_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
defDB = "channels"
|
||||
defSvcHTTPPort = "9005"
|
||||
defSvcGRPCPort = "7005"
|
||||
@@ -162,7 +164,21 @@ func main() {
|
||||
defer authnClient.Close()
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnClient.Secure())
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+17
-1
@@ -32,6 +32,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
@@ -65,6 +66,7 @@ const (
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
envPrefixChannels = "SMQ_CHANNELS_GRPC_"
|
||||
envPrefixGroups = "SMQ_GROUPS_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
defDB = "clients"
|
||||
defSvcHTTPPort = "9000"
|
||||
defSvcAuthGRPCPort = "7000"
|
||||
@@ -179,7 +181,21 @@ func main() {
|
||||
defer authnClient.Close()
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnClient.Secure())
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzClient, err := authsvcAuthz.NewAuthorization(ctx, grpcCfg, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+37
-19
@@ -19,14 +19,18 @@ import (
|
||||
domainsSvc "github.com/absmach/supermq/domains"
|
||||
domainsgrpcapi "github.com/absmach/supermq/domains/api/grpc"
|
||||
httpapi "github.com/absmach/supermq/domains/api/http"
|
||||
cache "github.com/absmach/supermq/domains/cache"
|
||||
"github.com/absmach/supermq/domains/events"
|
||||
dmw "github.com/absmach/supermq/domains/middleware"
|
||||
dpostgres "github.com/absmach/supermq/domains/postgres"
|
||||
"github.com/absmach/supermq/domains/private"
|
||||
dtracing "github.com/absmach/supermq/domains/tracing"
|
||||
redisclient "github.com/absmach/supermq/internal/clients/redis"
|
||||
smqlog "github.com/absmach/supermq/logger"
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
"github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/psvc"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
@@ -45,7 +49,6 @@ import (
|
||||
"github.com/authzed/grpcutil"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
@@ -65,16 +68,18 @@ const (
|
||||
)
|
||||
|
||||
type config struct {
|
||||
LogLevel string `env:"SMQ_DOMAINS_LOG_LEVEL" envDefault:"info"`
|
||||
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
|
||||
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
|
||||
InstanceID string `env:"SMQ_DOMAINS_INSTANCE_ID" envDefault:""`
|
||||
SpicedbHost string `env:"SMQ_SPICEDB_HOST" envDefault:"localhost"`
|
||||
SpicedbPort string `env:"SMQ_SPICEDB_PORT" envDefault:"50051"`
|
||||
SpicedbSchemaFile string `env:"SMQ_SPICEDB_SCHEMA_FILE" envDefault:"schema.zed"`
|
||||
SpicedbPreSharedKey string `env:"SMQ_SPICEDB_PRE_SHARED_KEY" envDefault:"12345678"`
|
||||
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
|
||||
ESURL string `env:"SMQ_ES_URL" envDefault:"nats://localhost:4222"`
|
||||
LogLevel string `env:"SMQ_DOMAINS_LOG_LEVEL" envDefault:"info"`
|
||||
JaegerURL url.URL `env:"SMQ_JAEGER_URL" envDefault:"http://localhost:4318/v1/traces"`
|
||||
SendTelemetry bool `env:"SMQ_SEND_TELEMETRY" envDefault:"true"`
|
||||
CacheURL string `env:"SMQ_DOMAINS_CACHE_URL" envDefault:"redis://localhost:6379/0"`
|
||||
CacheKeyDuration time.Duration `env:"SMQ_DOMAINS_CACHE_KEY_DURATION" envDefault:"10m"`
|
||||
InstanceID string `env:"SMQ_DOMAINS_INSTANCE_ID" envDefault:""`
|
||||
SpicedbHost string `env:"SMQ_SPICEDB_HOST" envDefault:"localhost"`
|
||||
SpicedbPort string `env:"SMQ_SPICEDB_PORT" envDefault:"50051"`
|
||||
SpicedbSchemaFile string `env:"SMQ_SPICEDB_SCHEMA_FILE" envDefault:"schema.zed"`
|
||||
SpicedbPreSharedKey string `env:"SMQ_SPICEDB_PRE_SHARED_KEY" envDefault:"12345678"`
|
||||
TraceRatio float64 `env:"SMQ_JAEGER_TRACE_RATIO" envDefault:"1.0"`
|
||||
ESURL string `env:"SMQ_ES_URL" envDefault:"nats://localhost:4222"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -153,7 +158,23 @@ func main() {
|
||||
defer authnHandler.Close()
|
||||
logger.Info("Authn successfully connected to auth gRPC server " + authnHandler.Secure())
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, clientConfig)
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
domainsRepo := dpostgres.New(database)
|
||||
|
||||
cacheclient, err := redisclient.Connect(cfg.CacheURL)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer cacheclient.Close()
|
||||
cache := cache.NewDomainsCache(cacheclient, cfg.CacheKeyDuration)
|
||||
|
||||
psvc := private.New(domainsRepo, cache)
|
||||
|
||||
domAuthz := domainsAuthz.NewAuthorization(psvc)
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, clientConfig, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("authz failed to connect to auth gRPC server : %s", err.Error()))
|
||||
exitCode = 1
|
||||
@@ -170,7 +191,7 @@ func main() {
|
||||
}
|
||||
logger.Info("Policy client successfully connected to spicedb gRPC server")
|
||||
|
||||
svc, err := newDomainService(ctx, db, tracer, cfg, dbConfig, authz, policyService, logger)
|
||||
svc, err := newDomainService(ctx, domainsRepo, cache, tracer, cfg, authz, policyService, logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to create %s service: %s", svcName, err.Error()))
|
||||
exitCode = 1
|
||||
@@ -185,7 +206,7 @@ func main() {
|
||||
}
|
||||
registerDomainsServiceServer := func(srv *grpc.Server) {
|
||||
reflection.Register(srv)
|
||||
grpcDomainsV1.RegisterDomainsServiceServer(srv, domainsgrpcapi.NewDomainsServer(svc))
|
||||
grpcDomainsV1.RegisterDomainsServiceServer(srv, domainsgrpcapi.NewDomainsServer(psvc))
|
||||
}
|
||||
|
||||
gs := grpcserver.NewServer(ctx, cancel, svcName, grpcServerConfig, registerDomainsServiceServer, logger)
|
||||
@@ -221,10 +242,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func newDomainService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg config, dbConfig pgclient.Config, authz authz.Authorization, policiessvc policies.Service, logger *slog.Logger) (domains.Service, error) {
|
||||
database := postgres.NewDatabase(db, dbConfig, tracer)
|
||||
domainsRepo := dpostgres.New(database)
|
||||
|
||||
func newDomainService(ctx context.Context, domainsRepo domainsSvc.Repository, cache domainsSvc.Cache, tracer trace.Tracer, cfg config, authz authz.Authorization, policiessvc policies.Service, logger *slog.Logger) (domains.Service, error) {
|
||||
idProvider := uuid.New()
|
||||
sidProvider, err := sid.New()
|
||||
if err != nil {
|
||||
@@ -236,7 +254,7 @@ func newDomainService(ctx context.Context, db *sqlx.DB, tracer trace.Tracer, cfg
|
||||
return nil, err
|
||||
}
|
||||
|
||||
svc, err := domainsSvc.New(domainsRepo, policiessvc, idProvider, sidProvider, availableActions, builtInRoles)
|
||||
svc, err := domainsSvc.New(domainsRepo, cache, policiessvc, idProvider, sidProvider, availableActions, builtInRoles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to init domain service: %w", err)
|
||||
}
|
||||
|
||||
+16
-1
@@ -30,6 +30,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
@@ -157,7 +158,21 @@ func main() {
|
||||
defer authnHandler.Close()
|
||||
logger.Info("Authn successfully connected to auth gRPC server " + authnHandler.Secure())
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientConfig)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientConfig, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error("failed to create authz " + err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+23
-7
@@ -23,6 +23,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/postgres"
|
||||
@@ -39,12 +40,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "invitations"
|
||||
envPrefixDB = "SMQ_INVITATIONS_DB_"
|
||||
envPrefixHTTP = "SMQ_INVITATIONS_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
defDB = "invitations"
|
||||
defSvcHTTPPort = "9020"
|
||||
svcName = "invitations"
|
||||
envPrefixDB = "SMQ_INVITATIONS_DB_"
|
||||
envPrefixHTTP = "SMQ_INVITATIONS_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
defDB = "invitations"
|
||||
defSvcHTTPPort = "9020"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
@@ -120,7 +122,21 @@ func main() {
|
||||
defer authnHandler.Close()
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnHandler.Secure())
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientCfg)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientCfg, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+23
-7
@@ -23,6 +23,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/events/store"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
@@ -39,12 +40,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
svcName = "journal"
|
||||
envPrefixDB = "SMQ_JOURNAL_DB_"
|
||||
envPrefixHTTP = "SMQ_JOURNAL_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
defDB = "journal"
|
||||
defSvcHTTPPort = "9021"
|
||||
svcName = "journal"
|
||||
envPrefixDB = "SMQ_JOURNAL_DB_"
|
||||
envPrefixHTTP = "SMQ_JOURNAL_HTTP_"
|
||||
envPrefixAuth = "SMQ_AUTH_GRPC_"
|
||||
envPrefixDomains = "SMQ_DOMAINS_GRPC_"
|
||||
defDB = "journal"
|
||||
defSvcHTTPPort = "9021"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
@@ -111,7 +113,21 @@ func main() {
|
||||
defer authnHandler.Close()
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnHandler.Secure())
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientCfg)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, _, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientCfg, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
|
||||
+16
-17
@@ -23,6 +23,7 @@ import (
|
||||
authsvcAuthn "github.com/absmach/supermq/pkg/authn/authsvc"
|
||||
smqauthz "github.com/absmach/supermq/pkg/authz"
|
||||
authsvcAuthz "github.com/absmach/supermq/pkg/authz/authsvc"
|
||||
domainsAuthz "github.com/absmach/supermq/pkg/domains/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
jaegerclient "github.com/absmach/supermq/pkg/jaeger"
|
||||
"github.com/absmach/supermq/pkg/oauth2"
|
||||
@@ -181,7 +182,21 @@ func main() {
|
||||
defer authnHandler.Close()
|
||||
logger.Info("AuthN successfully connected to auth gRPC server " + authnHandler.Secure())
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientConfig)
|
||||
domsGrpcCfg := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domsGrpcCfg, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load domains gRPC client configuration : %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
domAuthz, domainsClient, domainsHandler, err := domainsAuthz.NewAuthorization(ctx, domsGrpcCfg)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
|
||||
authz, authzHandler, err := authsvcAuthz.NewAuthorization(ctx, authClientConfig, domAuthz)
|
||||
if err != nil {
|
||||
logger.Error("failed to create authz " + err.Error())
|
||||
exitCode = 1
|
||||
@@ -190,22 +205,6 @@ func main() {
|
||||
defer authzHandler.Close()
|
||||
logger.Info("AuthZ successfully connected to auth gRPC server " + authzHandler.Secure())
|
||||
|
||||
domainsClientConfig := grpcclient.Config{}
|
||||
if err := env.ParseWithOptions(&domainsClientConfig, env.Options{Prefix: envPrefixDomains}); err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to load %s auth configuration : %s", svcName, err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
domainsClient, domainsHandler, err := grpcclient.SetupDomainsClient(ctx, domainsClientConfig)
|
||||
if err != nil {
|
||||
logger.Error("failed to setup domain gRPC clients " + err.Error())
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
defer domainsHandler.Close()
|
||||
logger.Info("DomainsService gRPC client successfully connected to domains gRPC server " + domainsHandler.Secure())
|
||||
|
||||
policyService, err := newPolicyService(cfg, logger)
|
||||
if err != nil {
|
||||
logger.Error("failed to create new policies service " + err.Error())
|
||||
|
||||
@@ -125,6 +125,8 @@ SMQ_DOMAINS_DB_SSL_KEY=
|
||||
SMQ_DOMAINS_DB_SSL_CERT=
|
||||
SMQ_DOMAINS_DB_SSL_ROOT_CERT=
|
||||
SMQ_DOMAINS_INSTANCE_ID=
|
||||
SMQ_DOMAINS_CACHE_URL=redis://domains-redis:${SMQ_REDIS_TCP_PORT}/0
|
||||
SMQ_DOMAINS_CACHE_KEY_DURATION=10m
|
||||
|
||||
#### Domains Client Config
|
||||
SMQ_DOMAINS_URL=http://domains:9003
|
||||
|
||||
@@ -65,6 +65,11 @@ services:
|
||||
SMQ_SPICEDB_PRE_SHARED_KEY: ${SMQ_SPICEDB_PRE_SHARED_KEY}
|
||||
SMQ_SPICEDB_HOST: ${SMQ_SPICEDB_HOST}
|
||||
SMQ_SPICEDB_PORT: ${SMQ_SPICEDB_PORT}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
networks:
|
||||
- supermq-base-net
|
||||
volumes:
|
||||
|
||||
@@ -61,6 +61,11 @@ services:
|
||||
SMQ_JAEGER_TRACE_RATIO: ${SMQ_JAEGER_TRACE_RATIO}
|
||||
SMQ_SEND_TELEMETRY: ${SMQ_SEND_TELEMETRY}
|
||||
SMQ_JOURNAL_INSTANCE_ID: ${SMQ_JOURNAL_INSTANCE_ID}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
ports:
|
||||
- ${SMQ_JOURNAL_HTTP_PORT}:${SMQ_JOURNAL_HTTP_PORT}
|
||||
networks:
|
||||
|
||||
@@ -19,6 +19,7 @@ volumes:
|
||||
supermq-auth-db-volume:
|
||||
supermq-pat-db-volume:
|
||||
supermq-domains-db-volume:
|
||||
supermq-domains-redis-volume:
|
||||
supermq-invitations-db-volume:
|
||||
supermq-ui-db-volume:
|
||||
|
||||
@@ -174,6 +175,15 @@ services:
|
||||
- supermq-base-net
|
||||
volumes:
|
||||
- supermq-domains-db-volume:/var/lib/postgresql/data
|
||||
|
||||
domains-redis:
|
||||
image: redis:7.2.4-alpine
|
||||
container_name: supermq-domains-redis
|
||||
restart: on-failure
|
||||
networks:
|
||||
- supermq-base-net
|
||||
volumes:
|
||||
- supermq-domains-redis-volume:/data
|
||||
|
||||
domains:
|
||||
image: supermq/domains:${SMQ_RELEASE_TAG}
|
||||
@@ -214,6 +224,8 @@ services:
|
||||
SMQ_DOMAINS_DB_SSL_ROOT_CERT: ${SMQ_DOMAINS_DB_SSL_ROOT_CERT}
|
||||
SMQ_DOMAINS_INSTANCE_ID: ${SMQ_DOMAINS_INSTANCE_ID}
|
||||
SMQ_ES_URL: ${SMQ_ES_URL}
|
||||
SMQ_DOMAINS_CACHE_URL: ${SMQ_DOMAINS_CACHE_URL}
|
||||
SMQ_DOMAINS_CACHE_KEY_DURATION: ${SMQ_DOMAINS_CACHE_KEY_DURATION}
|
||||
SMQ_AUTH_GRPC_URL: ${SMQ_AUTH_GRPC_URL}
|
||||
SMQ_AUTH_GRPC_TIMEOUT: ${SMQ_AUTH_GRPC_TIMEOUT}
|
||||
SMQ_AUTH_GRPC_CLIENT_CERT: ${SMQ_AUTH_GRPC_CLIENT_CERT:+/auth-grpc-client.crt}
|
||||
@@ -313,6 +325,11 @@ services:
|
||||
SMQ_AUTH_GRPC_CLIENT_CERT: ${SMQ_AUTH_GRPC_CLIENT_CERT:+/auth-grpc-client.crt}
|
||||
SMQ_AUTH_GRPC_CLIENT_KEY: ${SMQ_AUTH_GRPC_CLIENT_KEY:+/auth-grpc-client.key}
|
||||
SMQ_AUTH_GRPC_SERVER_CA_CERTS: ${SMQ_AUTH_GRPC_SERVER_CA_CERTS:+/auth-grpc-server-ca.crt}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
SMQ_JAEGER_URL: ${SMQ_JAEGER_URL}
|
||||
SMQ_JAEGER_TRACE_RATIO: ${SMQ_JAEGER_TRACE_RATIO}
|
||||
SMQ_SEND_TELEMETRY: ${SMQ_SEND_TELEMETRY}
|
||||
@@ -457,6 +474,11 @@ services:
|
||||
SMQ_GROUPS_GRPC_CLIENT_CERT: ${SMQ_GROUPS_GRPC_CLIENT_CERT:+/groups-grpc-client.crt}
|
||||
SMQ_GROUPS_GRPC_CLIENT_KEY: ${SMQ_GROUPS_GRPC_CLIENT_KEY:+/groups-grpc-client.key}
|
||||
SMQ_GROUPS_GRPC_SERVER_CA_CERTS: ${SMQ_GROUPS_GRPC_SERVER_CA_CERTS:+/groups-grpc-server-ca.crt}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
SMQ_JAEGER_URL: ${SMQ_JAEGER_URL}
|
||||
SMQ_JAEGER_TRACE_RATIO: ${SMQ_JAEGER_TRACE_RATIO}
|
||||
SMQ_SEND_TELEMETRY: ${SMQ_SEND_TELEMETRY}
|
||||
@@ -587,6 +609,11 @@ services:
|
||||
SMQ_GROUPS_GRPC_CLIENT_CERT: ${SMQ_GROUPS_GRPC_CLIENT_CERT:+/groups-grpc-client.crt}
|
||||
SMQ_GROUPS_GRPC_CLIENT_KEY: ${SMQ_GROUPS_GRPC_CLIENT_KEY:+/groups-grpc-client.key}
|
||||
SMQ_GROUPS_GRPC_SERVER_CA_CERTS: ${SMQ_GROUPS_GRPC_SERVER_CA_CERTS:+/groups-grpc-server-ca.crt}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
SMQ_ES_URL: ${SMQ_ES_URL}
|
||||
SMQ_JAEGER_URL: ${SMQ_JAEGER_URL}
|
||||
SMQ_JAEGER_TRACE_RATIO: ${SMQ_JAEGER_TRACE_RATIO}
|
||||
@@ -784,6 +811,11 @@ services:
|
||||
SMQ_CLIENTS_AUTH_GRPC_CLIENT_CERT: ${SMQ_CLIENTS_AUTH_GRPC_CLIENT_CERT:+/clients-grpc-client.crt}
|
||||
SMQ_CLIENTS_AUTH_GRPC_CLIENT_KEY: ${SMQ_CLIENTS_AUTH_GRPC_CLIENT_KEY:+/clients-grpc-client.key}
|
||||
SMQ_CLIENTS_AUTH_GRPC_SERVER_CA_CERTS: ${SMQ_CLIENTS_AUTH_GRPC_SERVER_CA_CERTS:+/clients-grpc-server-ca.crt}
|
||||
SMQ_DOMAINS_GRPC_URL: ${SMQ_DOMAINS_GRPC_URL}
|
||||
SMQ_DOMAINS_GRPC_TIMEOUT: ${SMQ_DOMAINS_GRPC_TIMEOUT}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_CERT: ${SMQ_DOMAINS_GRPC_CLIENT_CERT:+/domains-grpc-client.crt}
|
||||
SMQ_DOMAINS_GRPC_CLIENT_KEY: ${SMQ_DOMAINS_GRPC_CLIENT_KEY:+/domains-grpc-client.key}
|
||||
SMQ_DOMAINS_GRPC_SERVER_CA_CERTS: ${SMQ_DOMAINS_GRPC_SERVER_CA_CERTS:+/domains-grpc-server-ca.crt}
|
||||
SMQ_ES_URL: ${SMQ_ES_URL}
|
||||
SMQ_JAEGER_URL: ${SMQ_JAEGER_URL}
|
||||
SMQ_JAEGER_TRACE_RATIO: ${SMQ_JAEGER_TRACE_RATIO}
|
||||
|
||||
@@ -251,7 +251,6 @@ definition domain {
|
||||
relation update: role#member | team#member
|
||||
relation enable: role#member | team#member
|
||||
relation disable: role#member | team#member
|
||||
relation membership: role#member | team#member
|
||||
relation read: role#member | team#member
|
||||
relation delete: role#member | team#member
|
||||
|
||||
@@ -304,7 +303,6 @@ definition domain {
|
||||
permission read_permission = read + team->domain_read + organization->admin
|
||||
permission enable_permission = enable + team->domain_update + organization->admin
|
||||
permission disable_permission = disable + team->domain_update + organization->admin
|
||||
permission membership_permission = membership + team->domain_membership + organization->admin
|
||||
permission delete_permission = delete + team->domain_delete + organization->admin
|
||||
|
||||
permission manage_role_permission = manage_role + team->domain_manage_role + organization->admin
|
||||
@@ -312,6 +310,9 @@ definition domain {
|
||||
permission remove_role_users_permission = remove_role_users + team->domain_remove_role_users + organization->admin
|
||||
permission view_role_users_permission = view_role_users + team->domain_view_role_users + organization->admin
|
||||
|
||||
permission membership = read + update + enable + disable + delete + manage_role + add_role_users + remove_role_users + view_role_users
|
||||
permission admin = read & update & enable & disable & delete & manage_role & add_role_users & remove_role_users & view_role_users
|
||||
|
||||
permission client_create_permission = client_create + team->client_create + organization->admin
|
||||
permission channel_create_permission = channel_create + team->channel_create + organization->admin
|
||||
permission group_create_permission = group_create + team->group_create + organization->admin
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
grpcCommonV1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
grpcDomainsV1 "github.com/absmach/supermq/api/grpc/domains/v1"
|
||||
grpcapi "github.com/absmach/supermq/auth/api/grpc"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
@@ -20,6 +21,7 @@ var _ grpcDomainsV1.DomainsServiceClient = (*domainsGrpcClient)(nil)
|
||||
|
||||
type domainsGrpcClient struct {
|
||||
deleteUserFromDomains endpoint.Endpoint
|
||||
retrieveEntity endpoint.Endpoint
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
@@ -34,7 +36,14 @@ func NewDomainsClient(conn *grpc.ClientConn, timeout time.Duration) grpcDomainsV
|
||||
decodeDeleteUserResponse,
|
||||
grpcDomainsV1.DeleteUserRes{},
|
||||
).Endpoint(),
|
||||
|
||||
retrieveEntity: kitgrpc.NewClient(
|
||||
conn,
|
||||
domainsSvcName,
|
||||
"RetrieveEntity",
|
||||
encodeRetrieveEntityRequest,
|
||||
decodeRetrieveEntityResponse,
|
||||
grpcCommonV1.RetrieveEntityRes{},
|
||||
).Endpoint(),
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
@@ -65,3 +74,35 @@ func encodeDeleteUserRequest(_ context.Context, grpcReq interface{}) (interface{
|
||||
Id: req.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client domainsGrpcClient) RetrieveEntity(ctx context.Context, in *grpcCommonV1.RetrieveEntityReq, opts ...grpc.CallOption) (*grpcCommonV1.RetrieveEntityRes, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, client.timeout)
|
||||
defer cancel()
|
||||
|
||||
res, err := client.retrieveEntity(ctx, retrieveEntityReq{
|
||||
ID: in.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
return &grpcCommonV1.RetrieveEntityRes{}, grpcapi.DecodeError(err)
|
||||
}
|
||||
|
||||
rdsr := res.(retrieveEntityRes)
|
||||
return &grpcCommonV1.RetrieveEntityRes{
|
||||
Entity: &grpcCommonV1.EntityBasic{
|
||||
Id: rdsr.id,
|
||||
Status: uint32(rdsr.status),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeRetrieveEntityResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(*grpcCommonV1.RetrieveEntityRes)
|
||||
return retrieveEntityRes{id: res.Entity.GetId(), status: uint8(res.Entity.GetStatus())}, nil
|
||||
}
|
||||
|
||||
func encodeRetrieveEntityRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(retrieveEntityReq)
|
||||
return &grpcCommonV1.RetrieveEntityReq{
|
||||
Id: req.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
domains "github.com/absmach/supermq/domains/private"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
)
|
||||
|
||||
@@ -24,3 +24,22 @@ func deleteUserFromDomainsEndpoint(svc domains.Service) endpoint.Endpoint {
|
||||
return deleteUserRes{deleted: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveEntityEndpoint(svc domains.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(retrieveEntityReq)
|
||||
if err := req.validate(); err != nil {
|
||||
return retrieveEntityRes{}, err
|
||||
}
|
||||
|
||||
dom, err := svc.RetrieveEntity(ctx, req.ID)
|
||||
if err != nil {
|
||||
return retrieveEntityRes{}, err
|
||||
}
|
||||
|
||||
return retrieveEntityRes{
|
||||
id: dom.ID,
|
||||
status: uint8(dom.Status),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
grpcDomainsV1 "github.com/absmach/supermq/api/grpc/domains/v1"
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/domains"
|
||||
grpcapi "github.com/absmach/supermq/domains/api/grpc"
|
||||
domains "github.com/absmach/supermq/domains/private"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -31,7 +31,6 @@ const (
|
||||
description = "Description"
|
||||
groupName = "smqx"
|
||||
adminpermission = "admin"
|
||||
|
||||
authoritiesObj = "authorities"
|
||||
memberRelation = "member"
|
||||
loginDuration = 30 * time.Minute
|
||||
|
||||
@@ -18,3 +18,15 @@ func (req deleteUserPoliciesReq) validate() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type retrieveEntityReq struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (req retrieveEntityReq) validate() error {
|
||||
if req.ID == "" {
|
||||
return apiutil.ErrMissingID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,3 +6,8 @@ package grpc
|
||||
type deleteUserRes struct {
|
||||
deleted bool
|
||||
}
|
||||
|
||||
type retrieveEntityRes struct {
|
||||
id string
|
||||
status uint8
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ package grpc
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpcCommonV1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
grpcDomainsV1 "github.com/absmach/supermq/api/grpc/domains/v1"
|
||||
grpcapi "github.com/absmach/supermq/auth/api/grpc"
|
||||
"github.com/absmach/supermq/domains"
|
||||
domains "github.com/absmach/supermq/domains/private"
|
||||
kitgrpc "github.com/go-kit/kit/transport/grpc"
|
||||
)
|
||||
|
||||
@@ -17,6 +18,7 @@ var _ grpcDomainsV1.DomainsServiceServer = (*domainsGrpcServer)(nil)
|
||||
type domainsGrpcServer struct {
|
||||
grpcDomainsV1.UnimplementedDomainsServiceServer
|
||||
deleteUserFromDomains kitgrpc.Handler
|
||||
retrieveEntity kitgrpc.Handler
|
||||
}
|
||||
|
||||
func NewDomainsServer(svc domains.Service) grpcDomainsV1.DomainsServiceServer {
|
||||
@@ -26,6 +28,11 @@ func NewDomainsServer(svc domains.Service) grpcDomainsV1.DomainsServiceServer {
|
||||
decodeDeleteUserRequest,
|
||||
encodeDeleteUserResponse,
|
||||
),
|
||||
retrieveEntity: kitgrpc.NewServer(
|
||||
retrieveEntityEndpoint(svc),
|
||||
decodeRetrieveEntityRequest,
|
||||
encodeRetrieveEntityResponse,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,3 +55,31 @@ func (s *domainsGrpcServer) DeleteUserFromDomains(ctx context.Context, req *grpc
|
||||
}
|
||||
return res.(*grpcDomainsV1.DeleteUserRes), nil
|
||||
}
|
||||
|
||||
func decodeRetrieveEntityRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
|
||||
req := grpcReq.(*grpcCommonV1.RetrieveEntityReq)
|
||||
|
||||
return retrieveEntityReq{
|
||||
ID: req.GetId(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeRetrieveEntityResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
|
||||
res := grpcRes.(retrieveEntityRes)
|
||||
|
||||
return &grpcCommonV1.RetrieveEntityRes{
|
||||
Entity: &grpcCommonV1.EntityBasic{
|
||||
Id: res.id,
|
||||
Status: uint32(res.status),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *domainsGrpcServer) RetrieveEntity(ctx context.Context, req *grpcCommonV1.RetrieveEntityReq) (*grpcCommonV1.RetrieveEntityRes, error) {
|
||||
_, res, err := s.retrieveEntity.ServeGRPC(ctx, req)
|
||||
if err != nil {
|
||||
return nil, grpcapi.EncodeError(err)
|
||||
}
|
||||
|
||||
return res.(*grpcCommonV1.RetrieveEntityRes), nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/supermq/domains/mocks"
|
||||
"github.com/absmach/supermq/domains/private/mocks"
|
||||
)
|
||||
|
||||
var svc *mocks.Service
|
||||
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package cache contains the domain concept definitions needed to
|
||||
// support Magistrala Domains cache service functionality.
|
||||
package cache
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type domainsCache struct {
|
||||
client *redis.Client
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
func NewDomainsCache(client *redis.Client, duration time.Duration) domains.Cache {
|
||||
return &domainsCache{
|
||||
client: client,
|
||||
duration: duration,
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *domainsCache) Save(ctx context.Context, domainID string, status domains.Status) error {
|
||||
if domainID == "" {
|
||||
return errors.Wrap(repoerr.ErrCreateEntity, errors.New("domain ID is empty"))
|
||||
}
|
||||
statusString := status.String()
|
||||
if statusString == domains.Unknown {
|
||||
return errors.Wrap(repoerr.ErrCreateEntity, svcerr.ErrInvalidStatus)
|
||||
}
|
||||
if err := dc.client.Set(ctx, domainID, status.String(), dc.duration).Err(); err != nil {
|
||||
return errors.Wrap(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *domainsCache) Status(ctx context.Context, domainID string) (domains.Status, error) {
|
||||
st, err := dc.client.Get(ctx, domainID).Result()
|
||||
if err != nil {
|
||||
return domains.AllStatus, errors.Wrap(repoerr.ErrNotFound, err)
|
||||
}
|
||||
status, err := domains.ToStatus(st)
|
||||
if err != nil {
|
||||
return domains.AllStatus, errors.Wrap(repoerr.ErrNotFound, err)
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (dc *domainsCache) Remove(ctx context.Context, domainID string) error {
|
||||
if domainID == "" {
|
||||
return errors.Wrap(repoerr.ErrRemoveEntity, errors.New("domain ID is empty"))
|
||||
}
|
||||
if err := dc.client.Del(ctx, domainID).Err(); err != nil {
|
||||
return errors.Wrap(repoerr.ErrRemoveEntity, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Vendored
+209
@@ -0,0 +1,209 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/domains/cache"
|
||||
"github.com/absmach/supermq/internal/testsutil"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func setupDomainsClient(t *testing.T) domains.Cache {
|
||||
opts, err := redis.ParseURL(redisURL)
|
||||
assert.Nil(t, err, fmt.Sprintf("got unexpected error on parsing redis URL: %s", err))
|
||||
redisClient := redis.NewClient(opts)
|
||||
|
||||
return cache.NewDomainsCache(redisClient, 10*time.Minute)
|
||||
}
|
||||
|
||||
func TestSave(t *testing.T) {
|
||||
dc := setupDomainsClient(t)
|
||||
|
||||
domainID := testsutil.GenerateUUID(t)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
status domains.Status
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Save with enabled status",
|
||||
domainID: domainID,
|
||||
status: domains.EnabledStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Save with disabled status",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
status: domains.DisabledStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Save with frozen status",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
status: domains.FreezeStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Save with empty domain ID",
|
||||
domainID: "",
|
||||
status: domains.EnabledStatus,
|
||||
err: repoerr.ErrCreateEntity,
|
||||
},
|
||||
{
|
||||
desc: "Save with all status",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
status: domains.AllStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Save with invalid status",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
status: domains.Status(6),
|
||||
err: repoerr.ErrCreateEntity,
|
||||
},
|
||||
{
|
||||
desc: "Save the same record",
|
||||
domainID: domainID,
|
||||
status: domains.EnabledStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Save client with long id ",
|
||||
domainID: strings.Repeat("a", 513*1024*1024),
|
||||
status: domains.EnabledStatus,
|
||||
err: repoerr.ErrCreateEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := dc.Save(context.Background(), tc.domainID, tc.status)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
dc := setupDomainsClient(t)
|
||||
|
||||
enabledDomainID := testsutil.GenerateUUID(t)
|
||||
err := dc.Save(context.Background(), enabledDomainID, domains.EnabledStatus)
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
|
||||
|
||||
disabledDomainID := testsutil.GenerateUUID(t)
|
||||
err = dc.Save(context.Background(), disabledDomainID, domains.DisabledStatus)
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
|
||||
|
||||
frozenDomainID := testsutil.GenerateUUID(t)
|
||||
err = dc.Save(context.Background(), frozenDomainID, domains.FreezeStatus)
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
|
||||
|
||||
allDomainID := testsutil.GenerateUUID(t)
|
||||
err = dc.Save(context.Background(), allDomainID, domains.AllStatus)
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
status domains.Status
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Get domain status from cache for enabled domain",
|
||||
domainID: enabledDomainID,
|
||||
status: domains.EnabledStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Get domain status from cache for disabled domain",
|
||||
domainID: disabledDomainID,
|
||||
status: domains.DisabledStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Get domain status from cache for frozen domain",
|
||||
domainID: frozenDomainID,
|
||||
status: domains.FreezeStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Get domain status from cache for all domain",
|
||||
domainID: allDomainID,
|
||||
status: domains.AllStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Get domain status from cache for non existing domain",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
status: domains.AllStatus,
|
||||
err: repoerr.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
status, err := dc.Status(context.Background(), tc.domainID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
assert.Equal(t, tc.status, status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
dc := setupDomainsClient(t)
|
||||
|
||||
domainID := testsutil.GenerateUUID(t)
|
||||
err := dc.Save(context.Background(), domainID, domains.EnabledStatus)
|
||||
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Remove domain from cache",
|
||||
domainID: domainID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Remove domain from cache with empty domain ID",
|
||||
domainID: "",
|
||||
err: repoerr.ErrRemoveEntity,
|
||||
},
|
||||
{
|
||||
desc: "Remove non existing domain from cache",
|
||||
domainID: testsutil.GenerateUUID(t),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Remove domain from cache with long id",
|
||||
domainID: strings.Repeat("a", 513*1024*1024),
|
||||
err: repoerr.ErrRemoveEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := dc.Remove(context.Background(), tc.domainID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
if err == nil {
|
||||
_, err = dc.Status(context.Background(), tc.domainID)
|
||||
assert.True(t, errors.Contains(err, repoerr.ErrNotFound))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var (
|
||||
redisClient *redis.Client
|
||||
redisURL string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
pool, err := dockertest.NewPool("")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not connect to docker: %s", err)
|
||||
}
|
||||
|
||||
container, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: "redis",
|
||||
Tag: "7.2.4-alpine",
|
||||
}, func(config *docker.HostConfig) {
|
||||
config.AutoRemove = true
|
||||
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start container: %s", err)
|
||||
}
|
||||
|
||||
redisURL = fmt.Sprintf("redis://localhost:%s/0", container.GetPort("6379/tcp"))
|
||||
opts, err := redis.ParseURL(redisURL)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse redis URL: %s", err)
|
||||
}
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
redisClient = redis.NewClient(opts)
|
||||
|
||||
return redisClient.Ping(context.Background()).Err()
|
||||
}); err != nil {
|
||||
log.Fatalf("Could not connect to docker: %s", err)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
if err := pool.Purge(container); err != nil {
|
||||
log.Fatalf("Could not purge container: %s", err)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
+14
-1
@@ -169,7 +169,6 @@ type Service interface {
|
||||
DisableDomain(ctx context.Context, sesssion authn.Session, id string) (Domain, error)
|
||||
FreezeDomain(ctx context.Context, sesssion authn.Session, id string) (Domain, error)
|
||||
ListDomains(ctx context.Context, sesssion authn.Session, page Page) (DomainsPage, error)
|
||||
DeleteUserFromDomains(ctx context.Context, id string) error
|
||||
roles.RoleManager
|
||||
}
|
||||
|
||||
@@ -199,3 +198,17 @@ type Repository interface {
|
||||
|
||||
roles.Repository
|
||||
}
|
||||
|
||||
// Cache contains domains caching interface.
|
||||
//
|
||||
//go:generate mockery --name Cache --output=./mocks --filename cache.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
type Cache interface {
|
||||
// Save stores pair domain status and domain id.
|
||||
Save(ctx context.Context, domainID string, status Status) error
|
||||
|
||||
// Status returns domain status for given domain ID.
|
||||
Status(ctx context.Context, domainID string) (Status, error)
|
||||
|
||||
// Removes domain from cache.
|
||||
Remove(ctx context.Context, domainID string) error
|
||||
}
|
||||
|
||||
+26
-21
@@ -11,20 +11,22 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
domainPrefix = "domain."
|
||||
domainCreate = domainPrefix + "create"
|
||||
domainRetrieve = domainPrefix + "retrieve"
|
||||
domainUpdate = domainPrefix + "update"
|
||||
domainEnable = domainPrefix + "enable"
|
||||
domainDisable = domainPrefix + "disable"
|
||||
domainFreeze = domainPrefix + "freeze"
|
||||
domainList = domainPrefix + "list"
|
||||
domainUserDelete = domainPrefix + "user_delete"
|
||||
domainPrefix = "domain."
|
||||
domainCreate = domainPrefix + "create"
|
||||
domainRetrieve = domainPrefix + "retrieve"
|
||||
domainRetrieveStatus = domainPrefix + "retrieve_status"
|
||||
domainUpdate = domainPrefix + "update"
|
||||
domainEnable = domainPrefix + "enable"
|
||||
domainDisable = domainPrefix + "disable"
|
||||
domainFreeze = domainPrefix + "freeze"
|
||||
domainList = domainPrefix + "list"
|
||||
domainUserDelete = domainPrefix + "user_delete"
|
||||
)
|
||||
|
||||
var (
|
||||
_ events.Event = (*createDomainEvent)(nil)
|
||||
_ events.Event = (*retrieveDomainEvent)(nil)
|
||||
_ events.Event = (*retrieveDomainStatusEvent)(nil)
|
||||
_ events.Event = (*updateDomainEvent)(nil)
|
||||
_ events.Event = (*enableDomainEvent)(nil)
|
||||
_ events.Event = (*disableDomainEvent)(nil)
|
||||
@@ -91,6 +93,21 @@ func (rde retrieveDomainEvent) Encode() (map[string]interface{}, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type retrieveDomainStatusEvent struct {
|
||||
id string
|
||||
status domains.Status
|
||||
}
|
||||
|
||||
func (rdse retrieveDomainStatusEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": domainRetrieve,
|
||||
"id": rdse.id,
|
||||
"status": rdse.status.String(),
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type updateDomainEvent struct {
|
||||
domains.Domain
|
||||
}
|
||||
@@ -220,15 +237,3 @@ func (lde listDomainsEvent) Encode() (map[string]interface{}, error) {
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
type deleteUserFromDomainsEvent struct {
|
||||
userID string
|
||||
}
|
||||
|
||||
func (dude deleteUserFromDomainsEvent) Encode() (map[string]interface{}, error) {
|
||||
val := map[string]interface{}{
|
||||
"operation": domainUserDelete,
|
||||
"user_id": dude.userID,
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
@@ -164,17 +164,3 @@ func (es *eventStore) ListDomains(ctx context.Context, session authn.Session, p
|
||||
|
||||
return dp, nil
|
||||
}
|
||||
|
||||
func (es *eventStore) DeleteUserFromDomains(ctx context.Context, userID string) error {
|
||||
if err := es.svc.DeleteUserFromDomains(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
event := deleteUserFromDomainsEvent{userID}
|
||||
|
||||
if err := es.Publish(ctx, event); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -107,12 +107,12 @@ func (am *authorizationMiddleware) DisableDomain(ctx context.Context, session au
|
||||
func (am *authorizationMiddleware) FreezeDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
|
||||
// Only SuperAdmin can freeze the domain
|
||||
if err := am.authz.Authorize(ctx, authz.PolicyReq{
|
||||
Subject: session.DomainUserID,
|
||||
Subject: session.UserID,
|
||||
SubjectType: policies.UserType,
|
||||
SubjectKind: policies.UsersKind,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: id,
|
||||
ObjectType: policies.DomainType,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
}); err != nil {
|
||||
return domains.Domain{}, err
|
||||
}
|
||||
@@ -133,10 +133,6 @@ func (am *authorizationMiddleware) ListDomains(ctx context.Context, session auth
|
||||
return am.svc.ListDomains(ctx, session, page)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
|
||||
return am.svc.DeleteUserFromDomains(ctx, id)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, authReq authz.PolicyReq) error {
|
||||
perm, err := am.opp.GetPermission(op)
|
||||
if err != nil {
|
||||
|
||||
@@ -163,19 +163,3 @@ func (lm *loggingMiddleware) ListDomains(ctx context.Context, session authn.Sess
|
||||
}(time.Now())
|
||||
return lm.svc.ListDomains(ctx, session, page)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
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.DeleteUserFromDomains(ctx, id)
|
||||
}
|
||||
|
||||
@@ -91,11 +91,3 @@ func (ms *metricsMiddleware) ListDomains(ctx context.Context, session authn.Sess
|
||||
}(time.Now())
|
||||
return ms.svc.ListDomains(ctx, session, page)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) DeleteUserFromDomains(ctx context.Context, id string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "delete_user_from_domains").Add(1)
|
||||
ms.latency.With("method", "delete_user_from_domains").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.DeleteUserFromDomains(ctx, id)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
// Copyright (c) Abstract Machines
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
domains "github.com/absmach/supermq/domains"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Cache is an autogenerated mock type for the Cache type
|
||||
type Cache struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Remove provides a mock function with given fields: ctx, domainID
|
||||
func (_m *Cache) Remove(ctx context.Context, domainID string) error {
|
||||
ret := _m.Called(ctx, domainID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Remove")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(ctx, domainID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: ctx, domainID, status
|
||||
func (_m *Cache) Save(ctx context.Context, domainID string, status domains.Status) error {
|
||||
ret := _m.Called(ctx, domainID, status)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Save")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, domains.Status) error); ok {
|
||||
r0 = rf(ctx, domainID, status)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Status provides a mock function with given fields: ctx, domainID
|
||||
func (_m *Cache) Status(ctx context.Context, domainID string) (domains.Status, error) {
|
||||
ret := _m.Called(ctx, domainID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Status")
|
||||
}
|
||||
|
||||
var r0 domains.Status
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (domains.Status, error)); ok {
|
||||
return rf(ctx, domainID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) domains.Status); ok {
|
||||
r0 = rf(ctx, domainID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(domains.Status)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, domainID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewCache creates a new instance of Cache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewCache(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Cache {
|
||||
mock := &Cache{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -9,6 +9,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
commonv1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
@@ -103,6 +105,80 @@ func (_c *DomainsServiceClient_DeleteUserFromDomains_Call) RunAndReturn(run func
|
||||
return _c
|
||||
}
|
||||
|
||||
// RetrieveEntity provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *DomainsServiceClient) RetrieveEntity(ctx context.Context, in *commonv1.RetrieveEntityReq, opts ...grpc.CallOption) (*commonv1.RetrieveEntityRes, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveEntity")
|
||||
}
|
||||
|
||||
var r0 *commonv1.RetrieveEntityRes
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *commonv1.RetrieveEntityReq, ...grpc.CallOption) (*commonv1.RetrieveEntityRes, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *commonv1.RetrieveEntityReq, ...grpc.CallOption) *commonv1.RetrieveEntityRes); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*commonv1.RetrieveEntityRes)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *commonv1.RetrieveEntityReq, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DomainsServiceClient_RetrieveEntity_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RetrieveEntity'
|
||||
type DomainsServiceClient_RetrieveEntity_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RetrieveEntity is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *commonv1.RetrieveEntityReq
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *DomainsServiceClient_Expecter) RetrieveEntity(ctx interface{}, in interface{}, opts ...interface{}) *DomainsServiceClient_RetrieveEntity_Call {
|
||||
return &DomainsServiceClient_RetrieveEntity_Call{Call: _e.mock.On("RetrieveEntity",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *DomainsServiceClient_RetrieveEntity_Call) Run(run func(ctx context.Context, in *commonv1.RetrieveEntityReq, opts ...grpc.CallOption)) *DomainsServiceClient_RetrieveEntity_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*commonv1.RetrieveEntityReq), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *DomainsServiceClient_RetrieveEntity_Call) Return(_a0 *commonv1.RetrieveEntityRes, _a1 error) *DomainsServiceClient_RetrieveEntity_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *DomainsServiceClient_RetrieveEntity_Call) RunAndReturn(run func(context.Context, *commonv1.RetrieveEntityReq, ...grpc.CallOption) (*commonv1.RetrieveEntityRes, error)) *DomainsServiceClient_RetrieveEntity_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewDomainsServiceClient creates a new instance of DomainsServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewDomainsServiceClient(t interface {
|
||||
|
||||
@@ -77,24 +77,6 @@ func (_m *Service) CreateDomain(ctx context.Context, sesssion authn.Session, d d
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteUserFromDomains provides a mock function with given fields: ctx, id
|
||||
func (_m *Service) DeleteUserFromDomains(ctx context.Context, id string) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteUserFromDomains")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// DisableDomain provides a mock function with given fields: ctx, sesssion, id
|
||||
func (_m *Service) DisableDomain(ctx context.Context, sesssion authn.Session, id string) (domains.Domain, error) {
|
||||
ret := _m.Called(ctx, sesssion, id)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
// Copyright (c) Abstract Machines
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
domains "github.com/absmach/supermq/domains"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Service is an autogenerated mock type for the Service type
|
||||
type Service struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// DeleteUserFromDomains provides a mock function with given fields: ctx, id
|
||||
func (_m *Service) DeleteUserFromDomains(ctx context.Context, id string) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteUserFromDomains")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// RetrieveEntity provides a mock function with given fields: ctx, id
|
||||
func (_m *Service) RetrieveEntity(ctx context.Context, id string) (domains.Domain, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveEntity")
|
||||
}
|
||||
|
||||
var r0 domains.Domain
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (domains.Domain, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) domains.Domain); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Get(0).(domains.Domain)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewService(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Service {
|
||||
mock := &Service{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package private
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
)
|
||||
|
||||
const defLimit = 100
|
||||
|
||||
//go:generate mockery --name Service --output=./mocks --filename service.go --quiet --note "Copyright (c) Abstract Machines"
|
||||
type Service interface {
|
||||
RetrieveEntity(ctx context.Context, id string) (domains.Domain, error)
|
||||
DeleteUserFromDomains(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
var _ Service = (*service)(nil)
|
||||
|
||||
func New(repo domains.Repository, cache domains.Cache) Service {
|
||||
return service{
|
||||
repo: repo,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
type service struct {
|
||||
repo domains.Repository
|
||||
cache domains.Cache
|
||||
}
|
||||
|
||||
func (svc service) RetrieveEntity(ctx context.Context, id string) (domains.Domain, error) {
|
||||
status, err := svc.cache.Status(ctx, id)
|
||||
if err == nil {
|
||||
return domains.Domain{ID: id, Status: status}, nil
|
||||
}
|
||||
dom, err := svc.repo.RetrieveByID(ctx, id)
|
||||
if err != nil {
|
||||
return domains.Domain{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
status = dom.Status
|
||||
if err := svc.cache.Save(ctx, id, status); err != nil {
|
||||
return domains.Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
return domains.Domain{ID: dom.ID, Status: dom.Status}, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
|
||||
domainsPage, err := svc.repo.ListDomains(ctx, domains.Page{UserID: id, Limit: defLimit})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if domainsPage.Total > defLimit {
|
||||
for i := defLimit; i < int(domainsPage.Total); i += defLimit {
|
||||
page := domains.Page{UserID: id, Offset: uint64(i), Limit: defLimit}
|
||||
dp, err := svc.repo.ListDomains(ctx, page)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsPage.Domains = append(domainsPage.Domains, dp.Domains...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -40,7 +40,6 @@ const (
|
||||
enablePermission = "enable_permission"
|
||||
disablePermission = "disable_permission"
|
||||
readPermission = "read_permission"
|
||||
membershipPermission = "membership_permission"
|
||||
deletePermission = "delete_permission"
|
||||
manageRolePermission = "manage_role_permission"
|
||||
addRoleUsersPermission = "add_role_users_permission"
|
||||
|
||||
+15
-44
@@ -15,8 +15,6 @@ import (
|
||||
"github.com/absmach/supermq/pkg/roles"
|
||||
)
|
||||
|
||||
const defLimit = 100
|
||||
|
||||
var (
|
||||
errCreateDomainPolicy = errors.New("failed to create domain policy")
|
||||
errRollbackRepo = errors.New("failed to rollback repo")
|
||||
@@ -24,6 +22,7 @@ var (
|
||||
|
||||
type service struct {
|
||||
repo Repository
|
||||
cache Cache
|
||||
policy policies.Service
|
||||
idProvider supermq.IDProvider
|
||||
roles.ProvisionManageService
|
||||
@@ -31,7 +30,7 @@ type service struct {
|
||||
|
||||
var _ Service = (*service)(nil)
|
||||
|
||||
func New(repo Repository, policy policies.Service, idProvider supermq.IDProvider, sidProvider supermq.IDProvider, availableActions []roles.Action, builtInRoles map[roles.BuiltInRoleName][]roles.Action) (Service, error) {
|
||||
func New(repo Repository, cache Cache, policy policies.Service, idProvider supermq.IDProvider, sidProvider supermq.IDProvider, availableActions []roles.Action, builtInRoles map[roles.BuiltInRoleName][]roles.Action) (Service, error) {
|
||||
rpms, err := roles.NewProvisionManageService(policies.DomainType, repo, policy, sidProvider, availableActions, builtInRoles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,6 +38,7 @@ func New(repo Repository, policy policies.Service, idProvider supermq.IDProvider
|
||||
|
||||
return &service{
|
||||
repo: repo,
|
||||
cache: cache,
|
||||
policy: policy,
|
||||
idProvider: idProvider,
|
||||
ProvisionManageService: rpms,
|
||||
@@ -123,6 +123,10 @@ func (svc service) EnableDomain(ctx context.Context, session authn.Session, id s
|
||||
if err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
if err := svc.cache.Remove(ctx, id); err != nil {
|
||||
return dom, errors.Wrap(svcerr.ErrRemoveEntity, err)
|
||||
}
|
||||
|
||||
return dom, nil
|
||||
}
|
||||
|
||||
@@ -132,6 +136,10 @@ func (svc service) DisableDomain(ctx context.Context, session authn.Session, id
|
||||
if err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
if err := svc.cache.Remove(ctx, id); err != nil {
|
||||
return dom, errors.Wrap(svcerr.ErrRemoveEntity, err)
|
||||
}
|
||||
|
||||
return dom, nil
|
||||
}
|
||||
|
||||
@@ -142,6 +150,10 @@ func (svc service) FreezeDomain(ctx context.Context, session authn.Session, id s
|
||||
if err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrUpdateEntity, err)
|
||||
}
|
||||
if err := svc.cache.Remove(ctx, id); err != nil {
|
||||
return dom, errors.Wrap(svcerr.ErrRemoveEntity, err)
|
||||
}
|
||||
|
||||
return dom, nil
|
||||
}
|
||||
|
||||
@@ -157,44 +169,3 @@ func (svc service) ListDomains(ctx context.Context, session authn.Session, p Pag
|
||||
}
|
||||
return dp, nil
|
||||
}
|
||||
|
||||
func (svc service) DeleteUserFromDomains(ctx context.Context, id string) (err error) {
|
||||
domainsPage, err := svc.repo.ListDomains(ctx, Page{UserID: id, Limit: defLimit})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if domainsPage.Total > defLimit {
|
||||
for i := defLimit; i < int(domainsPage.Total); i += defLimit {
|
||||
page := Page{UserID: id, Offset: uint64(i), Limit: defLimit}
|
||||
dp, err := svc.repo.ListDomains(ctx, page)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainsPage.Domains = append(domainsPage.Domains, dp.Domains...)
|
||||
}
|
||||
}
|
||||
|
||||
// if err := svc.RemoveMembersFromAllRoles(ctx, authn.Session{}, []string{id}); err != nil {
|
||||
// return err
|
||||
// }
|
||||
////////////ToDo//////////////
|
||||
// Remove user from all roles in all domains
|
||||
//////////////////////////
|
||||
|
||||
// for _, domain := range domainsPage.Domains {
|
||||
// req := policies.Policy{
|
||||
// Subject: policies.EncodeDomainUserID(domain.ID, id),
|
||||
// SubjectType: policies.UserType,
|
||||
// }
|
||||
// if err := svc.policies.DeletePolicyFilter(ctx, req); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// if err := svc.repo.DeleteUserPolicies(ctx, id); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+52
-75
@@ -59,11 +59,13 @@ var (
|
||||
|
||||
var (
|
||||
drepo *mocks.Repository
|
||||
dcache *mocks.Cache
|
||||
policy *policiesMocks.Service
|
||||
)
|
||||
|
||||
func newService() domains.Service {
|
||||
drepo = new(mocks.Repository)
|
||||
dcache = new(mocks.Cache)
|
||||
idProvider := uuid.NewMock()
|
||||
sidProvider := sid.NewMock()
|
||||
policy = new(policiesMocks.Service)
|
||||
@@ -71,7 +73,7 @@ func newService() domains.Service {
|
||||
builtInRoles := map[roles.BuiltInRoleName][]roles.Action{
|
||||
groups.BuiltInRoleAdmin: availableActions,
|
||||
}
|
||||
ds, _ := domains.New(drepo, policy, idProvider, sidProvider, availableActions, builtInRoles)
|
||||
ds, _ := domains.New(drepo, dcache, policy, idProvider, sidProvider, availableActions, builtInRoles)
|
||||
return ds
|
||||
}
|
||||
|
||||
@@ -312,6 +314,8 @@ func TestEnableDomain(t *testing.T) {
|
||||
domainID string
|
||||
enableRes domains.Domain
|
||||
enableErr error
|
||||
cacheErr error
|
||||
resp domains.Domain
|
||||
err error
|
||||
}{
|
||||
{
|
||||
@@ -319,6 +323,7 @@ func TestEnableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
enableRes: enabledDomain,
|
||||
resp: enabledDomain,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
@@ -326,6 +331,7 @@ func TestEnableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: "",
|
||||
enableErr: repoerr.ErrNotFound,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
@@ -333,17 +339,29 @@ func TestEnableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
enableErr: errors.ErrMalformedEntity,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
desc: "enable domain with failed to remove cache",
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
enableRes: enabledDomain,
|
||||
cacheErr: errors.ErrMalformedEntity,
|
||||
resp: enabledDomain,
|
||||
err: svcerr.ErrRemoveEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := drepo.On("Update", context.Background(), tc.domainID, tc.session.UserID, domains.DomainReq{Status: &status}).Return(tc.enableRes, tc.enableErr)
|
||||
cacheCall := dcache.On("Remove", context.Background(), tc.domainID).Return(tc.cacheErr)
|
||||
domain, err := svc.EnableDomain(context.Background(), tc.session, tc.domainID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
assert.Equal(t, tc.enableRes, domain)
|
||||
assert.Equal(t, tc.resp, domain)
|
||||
repoCall.Unset()
|
||||
cacheCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -361,6 +379,8 @@ func TestDisableDomain(t *testing.T) {
|
||||
domainID string
|
||||
disableRes domains.Domain
|
||||
disableErr error
|
||||
cacheErr error
|
||||
resp domains.Domain
|
||||
err error
|
||||
}{
|
||||
{
|
||||
@@ -368,6 +388,7 @@ func TestDisableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
disableRes: disabledDomain,
|
||||
resp: disabledDomain,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
@@ -375,6 +396,7 @@ func TestDisableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: "",
|
||||
disableErr: repoerr.ErrNotFound,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
@@ -382,17 +404,29 @@ func TestDisableDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
disableErr: errors.ErrMalformedEntity,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
desc: "disable domain with failed to remove cache",
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
disableRes: disabledDomain,
|
||||
cacheErr: errors.ErrMalformedEntity,
|
||||
resp: disabledDomain,
|
||||
err: svcerr.ErrRemoveEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := drepo.On("Update", context.Background(), tc.domainID, tc.session.UserID, domains.DomainReq{Status: &status}).Return(tc.disableRes, tc.disableErr)
|
||||
cacheCall := dcache.On("Remove", context.Background(), tc.domainID).Return(tc.cacheErr)
|
||||
domain, err := svc.DisableDomain(context.Background(), tc.session, tc.domainID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
assert.Equal(t, tc.disableRes, domain)
|
||||
repoCall.Unset()
|
||||
cacheCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -410,6 +444,8 @@ func TestFreezeDomain(t *testing.T) {
|
||||
domainID string
|
||||
freezeRes domains.Domain
|
||||
freezeErr error
|
||||
cacheErr error
|
||||
resp domains.Domain
|
||||
err error
|
||||
}{
|
||||
{
|
||||
@@ -417,6 +453,7 @@ func TestFreezeDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
freezeRes: freezeDomain,
|
||||
resp: freezeDomain,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
@@ -424,6 +461,7 @@ func TestFreezeDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: "",
|
||||
freezeErr: repoerr.ErrNotFound,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
@@ -431,17 +469,29 @@ func TestFreezeDomain(t *testing.T) {
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
freezeErr: errors.ErrMalformedEntity,
|
||||
resp: domains.Domain{},
|
||||
err: svcerr.ErrUpdateEntity,
|
||||
},
|
||||
{
|
||||
desc: "freeze domain with failed to remove cache",
|
||||
session: validSession,
|
||||
domainID: domain.ID,
|
||||
freezeRes: freezeDomain,
|
||||
cacheErr: errors.ErrMalformedEntity,
|
||||
resp: freezeDomain,
|
||||
err: svcerr.ErrRemoveEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := drepo.On("Update", context.Background(), tc.domainID, tc.session.UserID, domains.DomainReq{Status: &status}).Return(tc.freezeRes, tc.freezeErr)
|
||||
cacheCall := dcache.On("Remove", context.Background(), tc.domainID).Return(tc.cacheErr)
|
||||
domain, err := svc.FreezeDomain(context.Background(), tc.session, tc.domainID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
assert.Equal(t, tc.freezeRes, domain)
|
||||
repoCall.Unset()
|
||||
cacheCall.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -518,76 +568,3 @@ func TestListDomains(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteUserFromDomains(t *testing.T) {
|
||||
svc := newService()
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
userID string
|
||||
listUserDomainsRes domains.DomainsPage
|
||||
listUserDomainsRes1 domains.DomainsPage
|
||||
listUserDomainsErr error
|
||||
listUserDomainsErr1 error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "delete user from domains successfully",
|
||||
userID: id,
|
||||
listUserDomainsRes: domains.DomainsPage{
|
||||
Domains: []domains.Domain{domain},
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Total: 1,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "delete user from domains with repository error on list domains",
|
||||
userID: id,
|
||||
listUserDomainsErr: svcerr.ErrViewEntity,
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
{
|
||||
desc: "delete user from domains with domains greater than default limit",
|
||||
userID: id,
|
||||
listUserDomainsRes: domains.DomainsPage{
|
||||
Domains: []domains.Domain{domain},
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
Total: 101,
|
||||
},
|
||||
listUserDomainsRes1: domains.DomainsPage{
|
||||
Domains: []domains.Domain{domain},
|
||||
Offset: 100,
|
||||
Limit: 100,
|
||||
Total: 101,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "delete user from domains with domains greater than default limit with error",
|
||||
userID: id,
|
||||
listUserDomainsRes: domains.DomainsPage{
|
||||
Domains: []domains.Domain{domain},
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
Total: 101,
|
||||
},
|
||||
listUserDomainsRes1: domains.DomainsPage{},
|
||||
listUserDomainsErr1: svcerr.ErrViewEntity,
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := drepo.On("ListDomains", context.Background(), domains.Page{UserID: tc.userID, Limit: 100}).Return(tc.listUserDomainsRes, tc.listUserDomainsErr)
|
||||
repoCall1 := drepo.On("ListDomains", context.Background(), domains.Page{UserID: tc.userID, Offset: 100, Limit: 100}).Return(tc.listUserDomainsRes1, tc.listUserDomainsErr1)
|
||||
err := svc.DeleteUserFromDomains(context.Background(), tc.userID)
|
||||
assert.True(t, errors.Contains(err, tc.err))
|
||||
repoCall.Unset()
|
||||
repoCall1.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,3 @@ func (tm *tracingMiddleware) ListDomains(ctx context.Context, session authn.Sess
|
||||
defer span.End()
|
||||
return tm.svc.ListDomains(ctx, session, p)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) DeleteUserFromDomains(ctx context.Context, id string) error {
|
||||
ctx, span := tm.tracer.Start(ctx, "delete_user_from_domains")
|
||||
defer span.End()
|
||||
return tm.svc.DeleteUserFromDomains(ctx, id)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ type authorizationMiddleware struct {
|
||||
authz smqauthz.Authorization
|
||||
opp svcutil.OperationPerm
|
||||
extOpp svcutil.ExternalOperationPerm
|
||||
|
||||
rmMW.RoleManagerAuthorizationMiddleware
|
||||
}
|
||||
|
||||
@@ -371,6 +370,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope
|
||||
return err
|
||||
}
|
||||
pr.Permission = perm.String()
|
||||
|
||||
if err := am.authz.Authorize(ctx, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission {
|
||||
|
||||
const (
|
||||
// External Permissions for the domain.
|
||||
domainCreateGroupPermission = "channel_create_permission"
|
||||
domainListGroupPermission = "membership_permission"
|
||||
userListGroupsPermission = "membership_permission"
|
||||
domainCreateGroupPermission = "group_create_permission"
|
||||
domainListGroupPermission = "membership"
|
||||
userListGroupsPermission = "membership"
|
||||
clientListGroupPermission = "read_permission"
|
||||
chanelListGroupPermission = "read_permission"
|
||||
)
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package domains.v1;
|
||||
option go_package = "github.com/absmach/supermq/api/grpc/domains/v1";
|
||||
|
||||
import "common/v1/common.proto";
|
||||
|
||||
option go_package = "github.com/absmach/supermq/internal/grpc/domains/v1";
|
||||
|
||||
|
||||
// DomainsService is a service that provides access to
|
||||
// domains functionalities for SuperMQ services.
|
||||
service DomainsService {
|
||||
rpc DeleteUserFromDomains(DeleteUserReq) returns (DeleteUserRes) {}
|
||||
rpc DeleteUserFromDomains(DeleteUserReq)
|
||||
returns (DeleteUserRes) {}
|
||||
rpc RetrieveEntity(common.v1.RetrieveEntityReq)
|
||||
returns (common.v1.RetrieveEntityRes) {}
|
||||
}
|
||||
|
||||
message DeleteUserRes {
|
||||
|
||||
@@ -8,19 +8,24 @@ import (
|
||||
|
||||
grpcAuthV1 "github.com/absmach/supermq/api/grpc/auth/v1"
|
||||
"github.com/absmach/supermq/auth/api/grpc/auth"
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/pkg/authz"
|
||||
pkgDomians "github.com/absmach/supermq/pkg/domains"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
grpchealth "google.golang.org/grpc/health/grpc_health_v1"
|
||||
)
|
||||
|
||||
type authorization struct {
|
||||
authSvcClient grpcAuthV1.AuthServiceClient
|
||||
domains pkgDomians.Authorization
|
||||
}
|
||||
|
||||
var _ authz.Authorization = (*authorization)(nil)
|
||||
|
||||
func NewAuthorization(ctx context.Context, cfg grpcclient.Config) (authz.Authorization, grpcclient.Handler, error) {
|
||||
func NewAuthorization(ctx context.Context, cfg grpcclient.Config, domainsAuthz pkgDomians.Authorization) (authz.Authorization, grpcclient.Handler, error) {
|
||||
client, err := grpcclient.NewHandler(cfg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -34,10 +39,26 @@ func NewAuthorization(ctx context.Context, cfg grpcclient.Config) (authz.Authori
|
||||
return nil, nil, grpcclient.ErrSvcNotServing
|
||||
}
|
||||
authSvcClient := auth.NewAuthClient(client.Connection(), cfg.Timeout)
|
||||
return authorization{authSvcClient}, client, nil
|
||||
return authorization{
|
||||
authSvcClient: authSvcClient,
|
||||
domains: domainsAuthz,
|
||||
}, client, nil
|
||||
}
|
||||
|
||||
func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error {
|
||||
if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) {
|
||||
domainID := pr.Domain
|
||||
if domainID == "" {
|
||||
if pr.ObjectType != policies.DomainType {
|
||||
return svcerr.ErrDomainAuthorization
|
||||
}
|
||||
domainID = pr.Object
|
||||
}
|
||||
if err := a.checkDomain(ctx, pr.SubjectType, pr.Subject, domainID); err != nil {
|
||||
return errors.Wrap(svcerr.ErrDomainAuthorization, err)
|
||||
}
|
||||
}
|
||||
|
||||
req := grpcAuthV1.AuthZReq{
|
||||
Domain: pr.Domain,
|
||||
SubjectType: pr.SubjectType,
|
||||
@@ -58,3 +79,45 @@ func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a authorization) checkDomain(ctx context.Context, subjectType, subject, domainID string) error {
|
||||
dom, err := a.domains.RetrieveEntity(ctx, domainID)
|
||||
if err != nil {
|
||||
return errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
switch dom.Status {
|
||||
case domains.FreezeStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: policies.SuperMQObject,
|
||||
ObjectType: policies.PlatformType,
|
||||
})
|
||||
|
||||
return err
|
||||
case domains.DisabledStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.AdminPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
})
|
||||
|
||||
return err
|
||||
case domains.EnabledStatus:
|
||||
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
|
||||
Subject: subject,
|
||||
SubjectType: subjectType,
|
||||
Permission: policies.MembershipPermission,
|
||||
Object: domainID,
|
||||
ObjectType: policies.DomainType,
|
||||
})
|
||||
|
||||
return err
|
||||
default:
|
||||
return svcerr.ErrInvalidStatus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package domains
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
)
|
||||
|
||||
type Authorization interface {
|
||||
RetrieveEntity(ctx context.Context, id string) (domains.Domain, error)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package grpcclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
grpcCommonV1 "github.com/absmach/supermq/api/grpc/common/v1"
|
||||
grpcDomainsV1 "github.com/absmach/supermq/api/grpc/domains/v1"
|
||||
"github.com/absmach/supermq/domains"
|
||||
pkgDomains "github.com/absmach/supermq/pkg/domains"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
)
|
||||
|
||||
type authorization struct {
|
||||
domainsSvcClient grpcDomainsV1.DomainsServiceClient
|
||||
}
|
||||
|
||||
var _ pkgDomains.Authorization = (*authorization)(nil)
|
||||
|
||||
func NewAuthorization(ctx context.Context, cfg grpcclient.Config) (pkgDomains.Authorization, grpcDomainsV1.DomainsServiceClient, grpcclient.Handler, error) {
|
||||
domainsClient, domainsHandler, err := grpcclient.SetupDomainsClient(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return authorization{domainsSvcClient: domainsClient}, domainsClient, domainsHandler, nil
|
||||
}
|
||||
|
||||
func (a authorization) RetrieveEntity(ctx context.Context, id string) (domains.Domain, error) {
|
||||
req := grpcCommonV1.RetrieveEntityReq{
|
||||
Id: id,
|
||||
}
|
||||
res, err := a.domainsSvcClient.RetrieveEntity(ctx, &req)
|
||||
if err != nil {
|
||||
return domains.Domain{}, err
|
||||
}
|
||||
|
||||
return domains.Domain{
|
||||
ID: res.Entity.GetId(),
|
||||
Status: domains.Status(res.Entity.GetStatus()),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package domainscache
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/absmach/supermq/domains"
|
||||
"github.com/absmach/supermq/domains/private"
|
||||
pkgDomains "github.com/absmach/supermq/pkg/domains"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
)
|
||||
|
||||
type authorization struct {
|
||||
psvc private.Service
|
||||
}
|
||||
|
||||
var _ pkgDomains.Authorization = (*authorization)(nil)
|
||||
|
||||
func NewAuthorization(psvc private.Service) pkgDomains.Authorization {
|
||||
return authorization{
|
||||
psvc: psvc,
|
||||
}
|
||||
}
|
||||
|
||||
func (a authorization) RetrieveEntity(ctx context.Context, id string) (domains.Domain, error) {
|
||||
dom, err := a.psvc.RetrieveEntity(ctx, id)
|
||||
if err != nil {
|
||||
return domains.Domain{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
return dom, nil
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
clientsgrpcapi "github.com/absmach/supermq/clients/api/grpc"
|
||||
climocks "github.com/absmach/supermq/clients/private/mocks"
|
||||
domainsgrpcapi "github.com/absmach/supermq/domains/api/grpc"
|
||||
domainsMocks "github.com/absmach/supermq/domains/mocks"
|
||||
domainsMocks "github.com/absmach/supermq/domains/private/mocks"
|
||||
smqlog "github.com/absmach/supermq/logger"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/grpcclient"
|
||||
|
||||
Reference in New Issue
Block a user