MG-1529 - User Removal (#2122)

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: Rodney Osodo <socials@rodneyosodo.com>
This commit is contained in:
b1ackd0t
2024-06-28 12:13:04 +03:00
committed by GitHub
parent 0794363a3c
commit 2fe2f850c2
67 changed files with 1816 additions and 1148 deletions
+26
View File
@@ -178,6 +178,32 @@ paths:
"500":
$ref: "#/components/responses/ServiceError"
delete:
summary: Delete a user
description: |
Delete a specific user that is identifier by the user ID.
tags:
- Users
parameters:
- $ref: "#/components/parameters/UserID"
security:
- bearerAuth: []
responses:
"204":
description: User deleted.
"400":
description: Failed due to malformed query parameters.
"401":
description: Missing or invalid access token provided.
"404":
description: A non-existent entity request.
"405":
description: Method not allowed.
"422":
description: Database can't process request.
"500":
$ref: "#/components/responses/ServiceError"
/users/{userID}/tags:
patch:
operationId: updateUserTags
+395 -378
View File
File diff suppressed because it is too large Load Diff
+9 -5
View File
@@ -23,8 +23,8 @@ service AuthService {
rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {}
rpc AddPolicy(AddPolicyReq) returns (AddPolicyRes) {}
rpc AddPolicies(AddPoliciesReq) returns (AddPoliciesRes) {}
rpc DeletePolicyFilter(DeletePolicyFilterReq) returns (DeletePolicyFilterRes) {}
rpc DeletePolicies(DeletePoliciesReq) returns (DeletePoliciesRes) {}
rpc DeletePolicyFilter(DeletePolicyFilterReq) returns (DeletePolicyRes) {}
rpc DeletePolicies(DeletePoliciesReq) returns (DeletePolicyRes) {}
rpc ListObjects(ListObjectsReq) returns (ListObjectsRes) {}
rpc ListAllObjects(ListObjectsReq) returns (ListObjectsRes) {}
rpc CountObjects(CountObjectsReq) returns (CountObjectsRes) {}
@@ -32,6 +32,7 @@ service AuthService {
rpc ListAllSubjects(ListSubjectsReq) returns (ListSubjectsRes) {}
rpc CountSubjects(CountSubjectsReq) returns (CountSubjectsRes) {}
rpc ListPermissions(ListPermissionsReq) returns (ListPermissionsRes) {}
rpc DeleteEntityPolicies(DeleteEntityPoliciesReq) returns (DeletePolicyRes) {}
}
// If a token is not carrying any information itself, the type
@@ -115,8 +116,6 @@ message DeletePolicyFilterReq {
string object_type = 10;
}
message DeletePolicyFilterRes { bool deleted = 1; }
message DeletePoliciesReq {
repeated DeletePolicyReq deletePoliciesReq = 1;
}
@@ -134,7 +133,7 @@ message DeletePolicyReq {
string object_type = 10;
}
message DeletePoliciesRes { bool deleted = 1; }
message DeletePolicyRes { bool deleted = 1; }
message ListObjectsReq {
string domain = 1;
@@ -219,3 +218,8 @@ message ListPermissionsRes {
string object_type = 6;
repeated string permissions = 7;
}
message DeleteEntityPoliciesReq{
string entity_type = 1;
string id = 2;
}
+68 -30
View File
@@ -24,22 +24,23 @@ const svcName = "magistrala.AuthService"
var _ magistrala.AuthServiceClient = (*grpcClient)(nil)
type grpcClient struct {
issue endpoint.Endpoint
refresh endpoint.Endpoint
identify endpoint.Endpoint
authorize endpoint.Endpoint
addPolicy endpoint.Endpoint
addPolicies endpoint.Endpoint
deletePolicyFilter endpoint.Endpoint
deletePolicies endpoint.Endpoint
listObjects endpoint.Endpoint
listAllObjects endpoint.Endpoint
countObjects endpoint.Endpoint
listSubjects endpoint.Endpoint
listAllSubjects endpoint.Endpoint
countSubjects endpoint.Endpoint
listPermissions endpoint.Endpoint
timeout time.Duration
issue endpoint.Endpoint
refresh endpoint.Endpoint
identify endpoint.Endpoint
authorize endpoint.Endpoint
addPolicy endpoint.Endpoint
addPolicies endpoint.Endpoint
deletePolicyFilter endpoint.Endpoint
deletePolicies endpoint.Endpoint
listObjects endpoint.Endpoint
listAllObjects endpoint.Endpoint
countObjects endpoint.Endpoint
listSubjects endpoint.Endpoint
listAllSubjects endpoint.Endpoint
countSubjects endpoint.Endpoint
listPermissions endpoint.Endpoint
deleteEntityPolicies endpoint.Endpoint
timeout time.Duration
}
// NewClient returns new gRPC client instance.
@@ -99,7 +100,7 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ
"DeletePolicyFilter",
encodeDeletePolicyFilterRequest,
decodeDeletePolicyFilterResponse,
magistrala.DeletePolicyFilterRes{},
magistrala.DeletePolicyRes{},
).Endpoint(),
deletePolicies: kitgrpc.NewClient(
conn,
@@ -107,7 +108,7 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ
"DeletePolicies",
encodeDeletePoliciesRequest,
decodeDeletePoliciesResponse,
magistrala.DeletePoliciesRes{},
magistrala.DeletePolicyRes{},
).Endpoint(),
listObjects: kitgrpc.NewClient(
conn,
@@ -165,6 +166,14 @@ func NewClient(conn *grpc.ClientConn, timeout time.Duration) magistrala.AuthServ
decodeListPermissionsResponse,
magistrala.ListPermissionsRes{},
).Endpoint(),
deleteEntityPolicies: kitgrpc.NewClient(
conn,
svcName,
"DeleteEntityPolicies",
encodeDeleteEntityPoliciesRequest,
decodeDeleteEntityPoliciesResponse,
magistrala.DeletePolicyRes{},
).Endpoint(),
timeout: timeout,
}
@@ -379,7 +388,7 @@ func encodeAddPoliciesRequest(_ context.Context, grpcReq interface{}) (interface
return &magistrala.AddPoliciesReq{AddPoliciesReq: addPolicies}, nil
}
func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) {
func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
@@ -395,16 +404,16 @@ func (client grpcClient) DeletePolicyFilter(ctx context.Context, in *magistrala.
Object: in.GetObject(),
})
if err != nil {
return &magistrala.DeletePolicyFilterRes{}, decodeError(err)
return &magistrala.DeletePolicyRes{}, decodeError(err)
}
dpr := res.(deletePolicyFilterRes)
return &magistrala.DeletePolicyFilterRes{Deleted: dpr.deleted}, nil
dpr := res.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil
}
func decodeDeletePolicyFilterResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*magistrala.DeletePolicyFilterRes)
return deletePolicyFilterRes{deleted: res.GetDeleted()}, nil
res := grpcRes.(*magistrala.DeletePolicyRes)
return deletePolicyRes{deleted: res.GetDeleted()}, nil
}
func encodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -422,7 +431,7 @@ func encodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (in
}, nil
}
func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) {
func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
r := policiesReq{}
@@ -444,16 +453,16 @@ func (client grpcClient) DeletePolicies(ctx context.Context, in *magistrala.Dele
}
res, err := client.deletePolicies(ctx, r)
if err != nil {
return &magistrala.DeletePoliciesRes{}, decodeError(err)
return &magistrala.DeletePolicyRes{}, decodeError(err)
}
dpr := res.(deletePoliciesRes)
return &magistrala.DeletePoliciesRes{Deleted: dpr.deleted}, nil
dpr := res.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil
}
func decodeDeletePoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*magistrala.DeletePoliciesRes)
return deletePoliciesRes{deleted: res.GetDeleted()}, nil
res := grpcRes.(*magistrala.DeletePolicyRes)
return deletePolicyRes{deleted: res.GetDeleted()}, nil
}
func encodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -730,6 +739,35 @@ func encodeListPermissionsRequest(_ context.Context, grpcReq interface{}) (inter
}, nil
}
func (client grpcClient) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ctx, cancel := context.WithTimeout(ctx, client.timeout)
defer cancel()
res, err := client.deleteEntityPolicies(ctx, deleteEntityPoliciesReq{
EntityType: in.GetEntityType(),
ID: in.GetId(),
})
if err != nil {
return &magistrala.DeletePolicyRes{}, decodeError(err)
}
dpr := res.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: dpr.deleted}, nil
}
func decodeDeleteEntityPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(*magistrala.DeletePolicyRes)
return deletePolicyRes{deleted: res.GetDeleted()}, nil
}
func encodeDeleteEntityPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(deleteEntityPoliciesReq)
return &magistrala.DeleteEntityPoliciesReq{
EntityType: req.EntityType,
Id: req.ID,
}, nil
}
func decodeError(err error) error {
if st, ok := status.FromError(err); ok {
switch st.Code() {
+21 -6
View File
@@ -155,7 +155,7 @@ func deletePolicyFilterEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(policyReq)
if err := req.validate(); err != nil {
return deletePolicyFilterRes{}, err
return deletePolicyRes{}, err
}
err := svc.DeletePolicyFilter(ctx, auth.PolicyReq{
@@ -170,9 +170,9 @@ func deletePolicyFilterEndpoint(svc auth.Service) endpoint.Endpoint {
Object: req.Object,
})
if err != nil {
return deletePolicyFilterRes{}, err
return deletePolicyRes{}, err
}
return deletePolicyFilterRes{deleted: true}, nil
return deletePolicyRes{deleted: true}, nil
}
}
@@ -180,7 +180,7 @@ func deletePoliciesEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
reqs := request.(policiesReq)
if err := reqs.validate(); err != nil {
return deletePoliciesRes{}, err
return deletePolicyRes{}, err
}
prs := []auth.PolicyReq{}
@@ -200,9 +200,9 @@ func deletePoliciesEndpoint(svc auth.Service) endpoint.Endpoint {
}
if err := svc.DeletePolicies(ctx, prs); err != nil {
return deletePoliciesRes{}, err
return deletePolicyRes{}, err
}
return deletePoliciesRes{deleted: true}, nil
return deletePolicyRes{deleted: true}, nil
}
}
@@ -349,3 +349,18 @@ func listPermissionsEndpoint(svc auth.Service) endpoint.Endpoint {
}, nil
}
}
func deleteEntityPoliciesEndpoint(svc auth.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(deleteEntityPoliciesReq)
if err := req.validate(); err != nil {
return deletePolicyRes{}, err
}
if err := svc.DeleteEntityPolicies(ctx, req.EntityType, req.ID); err != nil {
return deletePolicyRes{}, err
}
return deletePolicyRes{deleted: true}, nil
}
}
+56 -6
View File
@@ -471,7 +471,7 @@ func TestDeletePolicyFilter(t *testing.T) {
desc string
token string
deletePolicyFilterReq *magistrala.DeletePolicyFilterReq
deletePolicyFilterRes *magistrala.DeletePolicyFilterRes
deletePolicyFilterRes *magistrala.DeletePolicyRes
err error
}{
{
@@ -485,7 +485,7 @@ func TestDeletePolicyFilter(t *testing.T) {
Relation: readRelation,
Permission: readRelation,
},
deletePolicyFilterRes: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyFilterRes: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
@@ -499,7 +499,7 @@ func TestDeletePolicyFilter(t *testing.T) {
Relation: readRelation,
Permission: readRelation,
},
deletePolicyFilterRes: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyFilterRes: &magistrala.DeletePolicyRes{Deleted: false},
err: svcerr.ErrAuthorization,
},
}
@@ -524,7 +524,7 @@ func TestDeletePolicies(t *testing.T) {
desc string
token string
deletePoliciesReq *magistrala.DeletePoliciesReq
deletePoliciesRes *magistrala.DeletePoliciesRes
deletePoliciesRes *magistrala.DeletePolicyRes
err error
}{
{
@@ -542,7 +542,7 @@ func TestDeletePolicies(t *testing.T) {
},
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesRes: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
@@ -560,7 +560,7 @@ func TestDeletePolicies(t *testing.T) {
},
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{Deleted: false},
deletePoliciesRes: &magistrala.DeletePolicyRes{Deleted: false},
err: svcerr.ErrAuthorization,
},
}
@@ -1010,3 +1010,53 @@ func TestListPermissions(t *testing.T) {
svcCall.Unset()
}
}
func TestDeleteEntityPolicies(t *testing.T) {
conn, err := grpc.Dial(authAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating client connection %s", err))
client := grpcapi.NewClient(conn, time.Second)
cases := []struct {
desc string
token string
deleteEntityPoliciesReq *magistrala.DeleteEntityPoliciesReq
deletePolicyRes *magistrala.DeletePolicyRes
err error
}{
{
desc: "delete valid req",
token: validToken,
deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{
Id: id,
EntityType: usersType,
},
deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
desc: "delete invalid req with invalid token",
token: inValidToken,
deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{
EntityType: usersType,
},
deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false},
err: apiutil.ErrMissingID,
},
{
desc: "delete invalid req with invalid token",
token: inValidToken,
deleteEntityPoliciesReq: &magistrala.DeleteEntityPoliciesReq{
Id: id,
},
deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false},
err: apiutil.ErrMissingPolicyEntityType,
},
}
for _, tc := range cases {
repoCall := svc.On("DeleteEntityPolicies", mock.Anything, tc.deleteEntityPoliciesReq.EntityType, tc.deleteEntityPoliciesReq.Id).Return(tc.err)
dpr, err := client.DeleteEntityPolicies(context.Background(), tc.deleteEntityPoliciesReq)
assert.Equal(t, tc.deletePolicyRes.GetDeleted(), dpr.GetDeleted(), fmt.Sprintf("%s: expected %v got %v", tc.desc, tc.deletePolicyRes.GetDeleted(), dpr.GetDeleted()))
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
}
}
+16
View File
@@ -158,3 +158,19 @@ type listPermissionsReq struct {
Object string
FilterPermissions []string
}
type deleteEntityPoliciesReq struct {
EntityType string
ID string
}
func (req deleteEntityPoliciesReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
if req.EntityType == "" {
return apiutil.ErrMissingPolicyEntityType
}
return nil
}
+1 -5
View File
@@ -27,11 +27,7 @@ type addPoliciesRes struct {
added bool
}
type deletePolicyFilterRes struct {
deleted bool
}
type deletePoliciesRes struct {
type deletePolicyRes struct {
deleted bool
}
+50 -23
View File
@@ -20,21 +20,22 @@ var _ magistrala.AuthServiceServer = (*grpcServer)(nil)
type grpcServer struct {
magistrala.UnimplementedAuthServiceServer
issue kitgrpc.Handler
refresh kitgrpc.Handler
identify kitgrpc.Handler
authorize kitgrpc.Handler
addPolicy kitgrpc.Handler
addPolicies kitgrpc.Handler
deletePolicyFilter kitgrpc.Handler
deletePolicies kitgrpc.Handler
listObjects kitgrpc.Handler
listAllObjects kitgrpc.Handler
countObjects kitgrpc.Handler
listSubjects kitgrpc.Handler
listAllSubjects kitgrpc.Handler
countSubjects kitgrpc.Handler
listPermissions kitgrpc.Handler
issue kitgrpc.Handler
refresh kitgrpc.Handler
identify kitgrpc.Handler
authorize kitgrpc.Handler
addPolicy kitgrpc.Handler
addPolicies kitgrpc.Handler
deletePolicyFilter kitgrpc.Handler
deletePolicies kitgrpc.Handler
listObjects kitgrpc.Handler
listAllObjects kitgrpc.Handler
countObjects kitgrpc.Handler
listSubjects kitgrpc.Handler
listAllSubjects kitgrpc.Handler
countSubjects kitgrpc.Handler
listPermissions kitgrpc.Handler
deleteEntityPolicies kitgrpc.Handler
}
// NewServer returns new AuthServiceServer instance.
@@ -115,6 +116,11 @@ func NewServer(svc auth.Service) magistrala.AuthServiceServer {
decodeListPermissionsRequest,
encodeListPermissionsResponse,
),
deleteEntityPolicies: kitgrpc.NewServer(
(deleteEntityPoliciesEndpoint(svc)),
decodeDeleteEntityPoliciesRequest,
encodeDeleteEntityPoliciesResponse,
),
}
}
@@ -166,20 +172,20 @@ func (s *grpcServer) AddPolicies(ctx context.Context, req *magistrala.AddPolicie
return res.(*magistrala.AddPoliciesRes), nil
}
func (s *grpcServer) DeletePolicyFilter(ctx context.Context, req *magistrala.DeletePolicyFilterReq) (*magistrala.DeletePolicyFilterRes, error) {
func (s *grpcServer) DeletePolicyFilter(ctx context.Context, req *magistrala.DeletePolicyFilterReq) (*magistrala.DeletePolicyRes, error) {
_, res, err := s.deletePolicyFilter.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*magistrala.DeletePolicyFilterRes), nil
return res.(*magistrala.DeletePolicyRes), nil
}
func (s *grpcServer) DeletePolicies(ctx context.Context, req *magistrala.DeletePoliciesReq) (*magistrala.DeletePoliciesRes, error) {
func (s *grpcServer) DeletePolicies(ctx context.Context, req *magistrala.DeletePoliciesReq) (*magistrala.DeletePolicyRes, error) {
_, res, err := s.deletePolicies.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*magistrala.DeletePoliciesRes), nil
return res.(*magistrala.DeletePolicyRes), nil
}
func (s *grpcServer) ListObjects(ctx context.Context, req *magistrala.ListObjectsReq) (*magistrala.ListObjectsRes, error) {
@@ -238,6 +244,14 @@ func (s *grpcServer) ListPermissions(ctx context.Context, req *magistrala.ListPe
return res.(*magistrala.ListPermissionsRes), nil
}
func (s *grpcServer) DeleteEntityPolicies(ctx context.Context, req *magistrala.DeleteEntityPoliciesReq) (*magistrala.DeletePolicyRes, error) {
_, res, err := s.deleteEntityPolicies.ServeGRPC(ctx, req)
if err != nil {
return nil, encodeError(err)
}
return res.(*magistrala.DeletePolicyRes), nil
}
func decodeIssueRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.IssueReq)
return issueReq{
@@ -351,8 +365,8 @@ func decodeDeletePolicyFilterRequest(_ context.Context, grpcReq interface{}) (in
}
func encodeDeletePolicyFilterResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(deletePolicyFilterRes)
return &magistrala.DeletePolicyFilterRes{Deleted: res.deleted}, nil
res := grpcRes.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil
}
func decodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -375,8 +389,8 @@ func decodeDeletePoliciesRequest(_ context.Context, grpcReq interface{}) (interf
}
func encodeDeletePoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(deletePoliciesRes)
return &magistrala.DeletePoliciesRes{Deleted: res.deleted}, nil
res := grpcRes.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil
}
func decodeListObjectsRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
@@ -479,6 +493,19 @@ func encodeListPermissionsResponse(_ context.Context, grpcRes interface{}) (inte
}, nil
}
func decodeDeleteEntityPoliciesRequest(_ context.Context, grpcReq interface{}) (interface{}, error) {
req := grpcReq.(*magistrala.DeleteEntityPoliciesReq)
return deleteEntityPoliciesReq{
EntityType: req.GetEntityType(),
ID: req.GetId(),
}, nil
}
func encodeDeleteEntityPoliciesResponse(_ context.Context, grpcRes interface{}) (interface{}, error) {
res := grpcRes.(deletePolicyRes)
return &magistrala.DeletePolicyRes{Deleted: res.deleted}, nil
}
func encodeError(err error) error {
switch {
case errors.Contains(err, nil):
+17
View File
@@ -490,3 +490,20 @@ func (lm *loggingMiddleware) ListUserDomains(ctx context.Context, token, userID
}(time.Now())
return lm.svc.ListUserDomains(ctx, token, userID, page)
}
func (lm *loggingMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("entity_type", entityType),
slog.String("id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Delete entity policies failed to complete successfully", args...)
return
}
lm.logger.Info("Delete entity policies completed successfully", args...)
}(time.Now())
return lm.svc.DeleteEntityPolicies(ctx, entityType, id)
}
+8
View File
@@ -239,3 +239,11 @@ func (ms *metricsMiddleware) ListUserDomains(ctx context.Context, token, userID
}(time.Now())
return ms.svc.ListUserDomains(ctx, token, userID, page)
}
func (ms *metricsMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) error {
defer func(begin time.Time) {
ms.counter.With("method", "delete_entity_policies").Add(1)
ms.latency.With("method", "delete_entity_policies").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.DeleteEntityPolicies(ctx, entityType, id)
}
+3
View File
@@ -199,4 +199,7 @@ type DomainsRepository interface {
// CheckPolicy check policy in domains database.
CheckPolicy(ctx context.Context, pc Policy) error
// DeleteUserPolicies deletes user policies from domains database.
DeleteUserPolicies(ctx context.Context, id string) (err error)
}
+4
View File
@@ -227,6 +227,10 @@ func (es *eventStore) DeletePolicyFilter(ctx context.Context, pr auth.PolicyReq)
return es.svc.DeletePolicyFilter(ctx, pr)
}
func (es *eventStore) DeleteEntityPolicies(ctx context.Context, entityType, id string) error {
return es.svc.DeleteEntityPolicies(ctx, entityType, id)
}
func (es *eventStore) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error {
return es.svc.DeletePolicies(ctx, prs)
}
+10 -4
View File
@@ -71,16 +71,16 @@ func (m *AuthClient) AddPolicies(ctx context.Context, in *magistrala.AddPolicies
return ret.Get(0).(*magistrala.AddPoliciesRes), ret.Error(1)
}
func (m *AuthClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) {
func (m *AuthClient) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ret := m.Called(ctx, in)
return ret.Get(0).(*magistrala.DeletePolicyFilterRes), ret.Error(1)
return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1)
}
func (m *AuthClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) {
func (m *AuthClient) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ret := m.Called(ctx, in)
return ret.Get(0).(*magistrala.DeletePoliciesRes), ret.Error(1)
return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1)
}
func (m *AuthClient) ListObjects(ctx context.Context, in *magistrala.ListObjectsReq, opts ...grpc.CallOption) (*magistrala.ListObjectsRes, error) {
@@ -124,3 +124,9 @@ func (m *AuthClient) ListPermissions(ctx context.Context, in *magistrala.ListPer
return ret.Get(0).(*magistrala.ListPermissionsRes), ret.Error(1)
}
func (m *AuthClient) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
ret := m.Called(ctx, in)
return ret.Get(0).(*magistrala.DeletePolicyRes), ret.Error(1)
}
+18
View File
@@ -127,6 +127,24 @@ func (_m *Authz) CountSubjects(ctx context.Context, pr auth.PolicyReq) (uint64,
return r0, r1
}
// DeleteEntityPolicies provides a mock function with given fields: ctx, entityType, id
func (_m *Authz) DeleteEntityPolicies(ctx context.Context, entityType string, id string) error {
ret := _m.Called(ctx, entityType, id)
if len(ret) == 0 {
panic("no return value specified for DeleteEntityPolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, entityType, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeletePolicies provides a mock function with given fields: ctx, prs
func (_m *Authz) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error {
ret := _m.Called(ctx, prs)
+18
View File
@@ -78,6 +78,24 @@ func (_m *DomainsRepository) DeletePolicies(ctx context.Context, pcs ...auth.Pol
return r0
}
// DeleteUserPolicies provides a mock function with given fields: ctx, id
func (_m *DomainsRepository) DeleteUserPolicies(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for DeleteUserPolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListDomains provides a mock function with given fields: ctx, pm
func (_m *DomainsRepository) ListDomains(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) {
ret := _m.Called(ctx, pm)
+18
View File
@@ -201,6 +201,24 @@ func (_m *Service) CreateDomain(ctx context.Context, token string, d auth.Domain
return r0, r1
}
// DeleteEntityPolicies provides a mock function with given fields: ctx, entityType, id
func (_m *Service) DeleteEntityPolicies(ctx context.Context, entityType string, id string) error {
ret := _m.Called(ctx, entityType, id)
if len(ret) == 0 {
panic("no return value specified for DeleteEntityPolicies")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, entityType, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeletePolicies provides a mock function with given fields: ctx, prs
func (_m *Service) DeletePolicies(ctx context.Context, prs []auth.PolicyReq) error {
ret := _m.Called(ctx, prs)
+3
View File
@@ -170,6 +170,9 @@ type Authz interface {
// ListPermissions lists permission betweeen given subject and object .
ListPermissions(ctx context.Context, pr PolicyReq, filterPermission []string) (Permissions, error)
// DeleteEntityPolicies deletes all policies for the given entity.
DeleteEntityPolicies(ctx context.Context, entityType, id string) error
}
// PolicyAgent facilitates the communication to authorization
+10
View File
@@ -390,6 +390,16 @@ func (repo domainRepo) DeletePolicies(ctx context.Context, pcs ...auth.Policy) (
return tx.Commit()
}
func (repo domainRepo) DeleteUserPolicies(ctx context.Context, id string) (err error) {
q := "DELETE FROM policies WHERE subject_id = $1;"
if _, err := repo.db.ExecContext(ctx, q, id); err != nil {
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
}
return nil
}
func (repo domainRepo) processRows(rows *sqlx.Rows) ([]auth.Domain, error) {
var items []auth.Domain
for rows.Next() {
+55
View File
@@ -1091,3 +1091,58 @@ func TestCheckPolicy(t *testing.T) {
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
}
}
func TestDeleteUserPolicies(t *testing.T) {
repo := postgres.NewDomainRepository(database)
domain := auth.Domain{
ID: domainID,
Name: "test",
Alias: "test",
Tags: []string{"test"},
Metadata: map[string]interface{}{
"test": "test",
},
CreatedBy: userID,
UpdatedBy: userID,
Status: auth.EnabledStatus,
Permission: "admin",
}
policy := auth.Policy{
SubjectType: auth.UserType,
SubjectID: userID,
SubjectRelation: "admin",
Relation: "admin",
ObjectType: auth.DomainType,
ObjectID: domainID,
}
_, err := repo.Save(context.Background(), domain)
require.Nil(t, err, fmt.Sprintf("failed to save domain %s", domain.ID))
err = repo.SavePolicies(context.Background(), policy)
require.Nil(t, err, fmt.Sprintf("failed to save policy %s", policy.SubjectID))
cases := []struct {
desc string
id string
err error
}{
{
desc: "delete valid user policy",
id: userID,
err: nil,
},
{
desc: "delete invalid user policy",
id: inValid,
err: nil,
},
}
for _, tc := range cases {
err := repo.DeleteUserPolicies(context.Background(), tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.err, err))
}
}
+74 -1
View File
@@ -14,7 +14,10 @@ import (
svcerr "github.com/absmach/magistrala/pkg/errors/service"
)
const recoveryDuration = 5 * time.Minute
const (
recoveryDuration = 5 * time.Minute
defLimit = 100
)
var (
// ErrExpiry indicates that the token is expired.
@@ -32,6 +35,8 @@ var (
errRollbackPolicy = errors.New("failed to rollback policy")
errRemoveLocalPolicy = errors.New("failed to remove from local policy copy")
errRemovePolicyEngine = errors.New("failed to remove from policy engine")
// errInvalidEntityType indicates invalid entity type.
errInvalidEntityType = errors.New("invalid entity type")
)
var (
@@ -999,3 +1004,71 @@ func DecodeDomainUserID(domainUserID string) (string, string) {
return "", ""
}
}
func (svc service) DeleteEntityPolicies(ctx context.Context, entityType, id string) (err error) {
switch entityType {
case ThingType:
req := PolicyReq{
Object: id,
ObjectType: ThingType,
}
return svc.DeletePolicyFilter(ctx, req)
case UserType:
domainsPage, err := svc.domains.ListDomains(ctx, Page{SubjectID: id, Limit: defLimit})
if err != nil {
return err
}
if domainsPage.Total > defLimit {
for i := defLimit; i < int(domainsPage.Total); i += defLimit {
page := Page{SubjectID: id, Offset: uint64(i), Limit: defLimit}
dp, err := svc.domains.ListDomains(ctx, page)
if err != nil {
return err
}
domainsPage.Domains = append(domainsPage.Domains, dp.Domains...)
}
}
for _, domain := range domainsPage.Domains {
policy := PolicyReq{
Subject: EncodeDomainUserID(domain.ID, id),
SubjectType: UserType,
}
if err := svc.agent.DeletePolicyFilter(ctx, policy); err != nil {
return err
}
}
req := PolicyReq{
Subject: id,
SubjectType: UserType,
}
if err := svc.agent.DeletePolicyFilter(ctx, req); err != nil {
return err
}
if err := svc.domains.DeleteUserPolicies(ctx, id); err != nil {
return err
}
return nil
case GroupType:
req := PolicyReq{
SubjectType: GroupType,
Subject: id,
}
if err := svc.DeletePolicyFilter(ctx, req); err != nil {
return err
}
req = PolicyReq{
Object: id,
ObjectType: GroupType,
}
return svc.DeletePolicyFilter(ctx, req)
default:
return errInvalidEntityType
}
}
+19 -8
View File
@@ -158,16 +158,27 @@ func (pa *policyAgent) DeletePolicyFilter(ctx context.Context, pr auth.PolicyReq
RelationshipFilter: &v1.RelationshipFilter{
ResourceType: pr.ObjectType,
OptionalResourceId: pr.Object,
OptionalRelation: pr.Relation,
OptionalSubjectFilter: &v1.SubjectFilter{
OptionalSubjectId: pr.Subject,
SubjectType: pr.SubjectType,
OptionalRelation: &v1.SubjectFilter_RelationFilter{
Relation: pr.SubjectRelation,
},
},
},
}
if pr.Relation != "" {
req.RelationshipFilter.OptionalRelation = pr.Relation
}
if pr.SubjectType != "" {
req.RelationshipFilter.OptionalSubjectFilter = &v1.SubjectFilter{
SubjectType: pr.SubjectType,
}
if pr.Subject != "" {
req.RelationshipFilter.OptionalSubjectFilter.OptionalSubjectId = pr.Subject
}
if pr.SubjectRelation != "" {
req.RelationshipFilter.OptionalSubjectFilter.OptionalRelation = &v1.SubjectFilter_RelationFilter{
Relation: pr.SubjectRelation,
}
}
}
if _, err := pa.permissionClient.DeleteRelationships(ctx, req); err != nil {
return errors.Wrap(errRemovePolicies, handleSpicedbError(err))
}
+9
View File
@@ -303,3 +303,12 @@ func (tm *tracingMiddleware) ListUserDomains(ctx context.Context, token, userID
defer span.End()
return tm.svc.ListUserDomains(ctx, token, userID, p)
}
func (tm *tracingMiddleware) DeleteEntityPolicies(ctx context.Context, entityType, id string) error {
ctx, span := tm.tracer.Start(ctx, "delete_entity_policies", trace.WithAttributes(
attribute.String("entity_type", entityType),
attribute.String("id", id),
))
defer span.End()
return tm.svc.DeleteEntityPolicies(ctx, entityType, id)
}
+63 -25
View File
@@ -123,21 +123,22 @@ var AuthzService_ServiceDesc = grpc.ServiceDesc{
}
const (
AuthService_Issue_FullMethodName = "/magistrala.AuthService/Issue"
AuthService_Refresh_FullMethodName = "/magistrala.AuthService/Refresh"
AuthService_Identify_FullMethodName = "/magistrala.AuthService/Identify"
AuthService_Authorize_FullMethodName = "/magistrala.AuthService/Authorize"
AuthService_AddPolicy_FullMethodName = "/magistrala.AuthService/AddPolicy"
AuthService_AddPolicies_FullMethodName = "/magistrala.AuthService/AddPolicies"
AuthService_DeletePolicyFilter_FullMethodName = "/magistrala.AuthService/DeletePolicyFilter"
AuthService_DeletePolicies_FullMethodName = "/magistrala.AuthService/DeletePolicies"
AuthService_ListObjects_FullMethodName = "/magistrala.AuthService/ListObjects"
AuthService_ListAllObjects_FullMethodName = "/magistrala.AuthService/ListAllObjects"
AuthService_CountObjects_FullMethodName = "/magistrala.AuthService/CountObjects"
AuthService_ListSubjects_FullMethodName = "/magistrala.AuthService/ListSubjects"
AuthService_ListAllSubjects_FullMethodName = "/magistrala.AuthService/ListAllSubjects"
AuthService_CountSubjects_FullMethodName = "/magistrala.AuthService/CountSubjects"
AuthService_ListPermissions_FullMethodName = "/magistrala.AuthService/ListPermissions"
AuthService_Issue_FullMethodName = "/magistrala.AuthService/Issue"
AuthService_Refresh_FullMethodName = "/magistrala.AuthService/Refresh"
AuthService_Identify_FullMethodName = "/magistrala.AuthService/Identify"
AuthService_Authorize_FullMethodName = "/magistrala.AuthService/Authorize"
AuthService_AddPolicy_FullMethodName = "/magistrala.AuthService/AddPolicy"
AuthService_AddPolicies_FullMethodName = "/magistrala.AuthService/AddPolicies"
AuthService_DeletePolicyFilter_FullMethodName = "/magistrala.AuthService/DeletePolicyFilter"
AuthService_DeletePolicies_FullMethodName = "/magistrala.AuthService/DeletePolicies"
AuthService_ListObjects_FullMethodName = "/magistrala.AuthService/ListObjects"
AuthService_ListAllObjects_FullMethodName = "/magistrala.AuthService/ListAllObjects"
AuthService_CountObjects_FullMethodName = "/magistrala.AuthService/CountObjects"
AuthService_ListSubjects_FullMethodName = "/magistrala.AuthService/ListSubjects"
AuthService_ListAllSubjects_FullMethodName = "/magistrala.AuthService/ListAllSubjects"
AuthService_CountSubjects_FullMethodName = "/magistrala.AuthService/CountSubjects"
AuthService_ListPermissions_FullMethodName = "/magistrala.AuthService/ListPermissions"
AuthService_DeleteEntityPolicies_FullMethodName = "/magistrala.AuthService/DeleteEntityPolicies"
)
// AuthServiceClient is the client API for AuthService service.
@@ -153,8 +154,8 @@ type AuthServiceClient interface {
Authorize(ctx context.Context, in *AuthorizeReq, opts ...grpc.CallOption) (*AuthorizeRes, error)
AddPolicy(ctx context.Context, in *AddPolicyReq, opts ...grpc.CallOption) (*AddPolicyRes, error)
AddPolicies(ctx context.Context, in *AddPoliciesReq, opts ...grpc.CallOption) (*AddPoliciesRes, error)
DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyFilterRes, error)
DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePoliciesRes, error)
DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyRes, error)
DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error)
ListObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error)
ListAllObjects(ctx context.Context, in *ListObjectsReq, opts ...grpc.CallOption) (*ListObjectsRes, error)
CountObjects(ctx context.Context, in *CountObjectsReq, opts ...grpc.CallOption) (*CountObjectsRes, error)
@@ -162,6 +163,7 @@ type AuthServiceClient interface {
ListAllSubjects(ctx context.Context, in *ListSubjectsReq, opts ...grpc.CallOption) (*ListSubjectsRes, error)
CountSubjects(ctx context.Context, in *CountSubjectsReq, opts ...grpc.CallOption) (*CountSubjectsRes, error)
ListPermissions(ctx context.Context, in *ListPermissionsReq, opts ...grpc.CallOption) (*ListPermissionsRes, error)
DeleteEntityPolicies(ctx context.Context, in *DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error)
}
type authServiceClient struct {
@@ -232,9 +234,9 @@ func (c *authServiceClient) AddPolicies(ctx context.Context, in *AddPoliciesReq,
return out, nil
}
func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyFilterRes, error) {
func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePolicyFilterReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePolicyFilterRes)
out := new(DeletePolicyRes)
err := c.cc.Invoke(ctx, AuthService_DeletePolicyFilter_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
@@ -242,9 +244,9 @@ func (c *authServiceClient) DeletePolicyFilter(ctx context.Context, in *DeletePo
return out, nil
}
func (c *authServiceClient) DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePoliciesRes, error) {
func (c *authServiceClient) DeletePolicies(ctx context.Context, in *DeletePoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePoliciesRes)
out := new(DeletePolicyRes)
err := c.cc.Invoke(ctx, AuthService_DeletePolicies_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
@@ -322,6 +324,16 @@ func (c *authServiceClient) ListPermissions(ctx context.Context, in *ListPermiss
return out, nil
}
func (c *authServiceClient) DeleteEntityPolicies(ctx context.Context, in *DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*DeletePolicyRes, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePolicyRes)
err := c.cc.Invoke(ctx, AuthService_DeleteEntityPolicies_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility
@@ -335,8 +347,8 @@ type AuthServiceServer interface {
Authorize(context.Context, *AuthorizeReq) (*AuthorizeRes, error)
AddPolicy(context.Context, *AddPolicyReq) (*AddPolicyRes, error)
AddPolicies(context.Context, *AddPoliciesReq) (*AddPoliciesRes, error)
DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyFilterRes, error)
DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePoliciesRes, error)
DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyRes, error)
DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePolicyRes, error)
ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error)
ListAllObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error)
CountObjects(context.Context, *CountObjectsReq) (*CountObjectsRes, error)
@@ -344,6 +356,7 @@ type AuthServiceServer interface {
ListAllSubjects(context.Context, *ListSubjectsReq) (*ListSubjectsRes, error)
CountSubjects(context.Context, *CountSubjectsReq) (*CountSubjectsRes, error)
ListPermissions(context.Context, *ListPermissionsReq) (*ListPermissionsRes, error)
DeleteEntityPolicies(context.Context, *DeleteEntityPoliciesReq) (*DeletePolicyRes, error)
mustEmbedUnimplementedAuthServiceServer()
}
@@ -369,10 +382,10 @@ func (UnimplementedAuthServiceServer) AddPolicy(context.Context, *AddPolicyReq)
func (UnimplementedAuthServiceServer) AddPolicies(context.Context, *AddPoliciesReq) (*AddPoliciesRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddPolicies not implemented")
}
func (UnimplementedAuthServiceServer) DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyFilterRes, error) {
func (UnimplementedAuthServiceServer) DeletePolicyFilter(context.Context, *DeletePolicyFilterReq) (*DeletePolicyRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePolicyFilter not implemented")
}
func (UnimplementedAuthServiceServer) DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePoliciesRes, error) {
func (UnimplementedAuthServiceServer) DeletePolicies(context.Context, *DeletePoliciesReq) (*DeletePolicyRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePolicies not implemented")
}
func (UnimplementedAuthServiceServer) ListObjects(context.Context, *ListObjectsReq) (*ListObjectsRes, error) {
@@ -396,6 +409,9 @@ func (UnimplementedAuthServiceServer) CountSubjects(context.Context, *CountSubje
func (UnimplementedAuthServiceServer) ListPermissions(context.Context, *ListPermissionsReq) (*ListPermissionsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListPermissions not implemented")
}
func (UnimplementedAuthServiceServer) DeleteEntityPolicies(context.Context, *DeleteEntityPoliciesReq) (*DeletePolicyRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteEntityPolicies not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
@@ -679,6 +695,24 @@ func _AuthService_ListPermissions_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _AuthService_DeleteEntityPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteEntityPoliciesReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).DeleteEntityPolicies(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_DeleteEntityPolicies_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).DeleteEntityPolicies(ctx, req.(*DeleteEntityPoliciesReq))
}
return interceptor(ctx, in, info, handler)
}
// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -746,6 +780,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListPermissions",
Handler: _AuthService_ListPermissions_Handler,
},
{
MethodName: "DeleteEntityPolicies",
Handler: _AuthService_DeleteEntityPolicies_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",
+6
View File
@@ -45,6 +45,7 @@ type remotes struct {
CertsURL string `toml:"certs_url"`
InvitationsURL string `toml:"invitations_url"`
JournalURL string `toml:"journal_url"`
HostURL string `toml:"host_url"`
TLSVerification bool `toml:"tls_verification"`
}
@@ -115,6 +116,7 @@ func ParseConfig(sdkConf mgxsdk.Config) (mgxsdk.Config, error) {
CertsURL: defCertsURL,
InvitationsURL: defInvitationsURL,
JournalURL: defJournalURL,
HostURL: defURL,
TLSVerification: defTLSVerification,
},
Filter: filter{
@@ -205,6 +207,10 @@ func ParseConfig(sdkConf mgxsdk.Config) (mgxsdk.Config, error) {
sdkConf.JournalURL = config.Remotes.JournalURL
}
if sdkConf.HostURL == "" && config.Remotes.HostURL != "" {
sdkConf.HostURL = config.Remotes.HostURL
}
sdkConf.TLSVerification = config.Remotes.TLSVerification || sdkConf.TLSVerification
return sdkConf, nil
+19 -2
View File
@@ -338,7 +338,24 @@ var cmdUsers = []cobra.Command{
logJSON(user)
},
},
{
Use: "delete <user_id> <user_auth_token>",
Short: "Delete user",
Long: "Delete user by id\n" +
"Usage:\n" +
"\tmagistrala-cli users delete <user_id> $USERTOKEN - delete user with <user_id>\n",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Use)
return
}
if err := sdk.DeleteUser(args[0], args[1]); err != nil {
logError(err)
return
}
logOK()
},
},
{
Use: "channels <user_id> <user_auth_token>",
Short: "List channels",
@@ -451,7 +468,7 @@ var cmdUsers = []cobra.Command{
// NewUsersCmd returns users command.
func NewUsersCmd() *cobra.Command {
cmd := cobra.Command{
Use: "users [create | get | update | token | password | enable | disable | channels | things | groups]",
Use: "users [create | get | update | token | password | enable | disable | delete | channels | things | groups]",
Short: "Users management",
Long: `Users management: create accounts and tokens"`,
}
-3
View File
@@ -12,13 +12,10 @@ import (
"github.com/spf13/cobra"
)
const defURL string = "http://localhost"
func main() {
msgContentType := string(sdk.CTJSONSenML)
sdkConf := sdk.Config{
MsgContentType: sdk.ContentType(msgContentType),
HostURL: defURL,
}
// Root
+18 -13
View File
@@ -64,19 +64,21 @@ const (
)
type config struct {
LogLevel string `env:"MG_USERS_LOG_LEVEL" envDefault:"info"`
AdminEmail string `env:"MG_USERS_ADMIN_EMAIL" envDefault:"admin@example.com"`
AdminPassword string `env:"MG_USERS_ADMIN_PASSWORD" envDefault:"12345678"`
PassRegexText string `env:"MG_USERS_PASS_REGEX" envDefault:"^.{8,}$"`
ResetURL string `env:"MG_TOKEN_RESET_ENDPOINT" envDefault:"/reset-request"`
JaegerURL url.URL `env:"MG_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"`
SendTelemetry bool `env:"MG_SEND_TELEMETRY" envDefault:"true"`
InstanceID string `env:"MG_USERS_INSTANCE_ID" envDefault:""`
ESURL string `env:"MG_ES_URL" envDefault:"nats://localhost:4222"`
TraceRatio float64 `env:"MG_JAEGER_TRACE_RATIO" envDefault:"1.0"`
SelfRegister bool `env:"MG_USERS_ALLOW_SELF_REGISTER" envDefault:"false"`
OAuthUIRedirectURL string `env:"MG_OAUTH_UI_REDIRECT_URL" envDefault:"http://localhost:9095/domains"`
OAuthUIErrorURL string `env:"MG_OAUTH_UI_ERROR_URL" envDefault:"http://localhost:9095/error"`
LogLevel string `env:"MG_USERS_LOG_LEVEL" envDefault:"info"`
AdminEmail string `env:"MG_USERS_ADMIN_EMAIL" envDefault:"admin@example.com"`
AdminPassword string `env:"MG_USERS_ADMIN_PASSWORD" envDefault:"12345678"`
PassRegexText string `env:"MG_USERS_PASS_REGEX" envDefault:"^.{8,}$"`
ResetURL string `env:"MG_TOKEN_RESET_ENDPOINT" envDefault:"/reset-request"`
JaegerURL url.URL `env:"MG_JAEGER_URL" envDefault:"http://localhost:14268/api/traces"`
SendTelemetry bool `env:"MG_SEND_TELEMETRY" envDefault:"true"`
InstanceID string `env:"MG_USERS_INSTANCE_ID" envDefault:""`
ESURL string `env:"MG_ES_URL" envDefault:"nats://localhost:4222"`
TraceRatio float64 `env:"MG_JAEGER_TRACE_RATIO" envDefault:"1.0"`
SelfRegister bool `env:"MG_USERS_ALLOW_SELF_REGISTER" envDefault:"false"`
OAuthUIRedirectURL string `env:"MG_OAUTH_UI_REDIRECT_URL" envDefault:"http://localhost:9095/domains"`
OAuthUIErrorURL string `env:"MG_OAUTH_UI_ERROR_URL" envDefault:"http://localhost:9095/error"`
DeleteInterval time.Duration `env:"MG_USERS_DELETE_INTERVAL" envDefault:"24h"`
DeleteAfter time.Duration `env:"MG_USERS_DELETE_AFTER" envDefault:"720h"`
PassRegex *regexp.Regexp
}
@@ -248,6 +250,9 @@ func newService(ctx context.Context, authClient magistrala.AuthServiceClient, db
if err := createAdminPolicy(ctx, clientID, authClient); err != nil {
return nil, nil, err
}
users.NewDeleteHandler(ctx, cRepo, authClient, c.DeleteInterval, c.DeleteAfter, logger)
return csvc, gsvc, err
}
+1
View File
@@ -14,6 +14,7 @@ user_token = ""
bootstrap_url = "http://localhost:9013"
certs_url = "http://localhost:9019"
domains_url = "http://localhost:8189"
host_url = "http://localhost"
http_adapter_url = "http://localhost:8008"
invitations_url = "http://localhost:9020"
reader_url = "http://localhost:9011"
+2
View File
@@ -193,6 +193,8 @@ MG_USERS_INSTANCE_ID=
MG_USERS_ALLOW_SELF_REGISTER=true
MG_OAUTH_UI_REDIRECT_URL=http://localhost:9095${MG_UI_PATH_PREFIX}/tokens/secure
MG_OAUTH_UI_ERROR_URL=http://localhost:9095${MG_UI_PATH_PREFIX}/error
MG_USERS_DELETE_INTERVAL=24h
MG_USERS_DELETE_AFTER=720h
### Email utility
MG_EMAIL_HOST=smtp.mailtrap.io
+2
View File
@@ -449,6 +449,8 @@ services:
MG_GOOGLE_STATE: ${MG_GOOGLE_STATE}
MG_OAUTH_UI_REDIRECT_URL: ${MG_OAUTH_UI_REDIRECT_URL}
MG_OAUTH_UI_ERROR_URL: ${MG_OAUTH_UI_ERROR_URL}
MG_USERS_DELETE_INTERVAL: ${MG_USERS_DELETE_INTERVAL}
MG_USERS_DELETE_AFTER: ${MG_USERS_DELETE_AFTER}
ports:
- ${MG_USERS_HTTP_PORT}:${MG_USERS_HTTP_PORT}
networks:
+3
View File
@@ -66,6 +66,9 @@ var (
// ErrMalformedPolicyAct indicates missing policies action.
ErrMalformedPolicyAct = errors.New("malformed policy action")
// ErrMissingPolicyEntityType indicates missing policies entity type.
ErrMissingPolicyEntityType = errors.New("missing policy entity type")
// ErrMalformedPolicyPer indicates missing policies relation.
ErrMalformedPolicyPer = errors.New("malformed policy permission")
+11 -37
View File
@@ -581,54 +581,28 @@ func (svc service) Unassign(ctx context.Context, token, groupID, relation, membe
return nil
}
func (svc service) DeleteGroup(ctx context.Context, token, groupID string) error {
func (svc service) DeleteGroup(ctx context.Context, token, id string) error {
res, err := svc.identify(ctx, token)
if err != nil {
return err
}
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.GroupType, groupID); err != nil {
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.GroupType, id); err != nil {
return err
}
// Remove policy of child groups
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.GroupType,
Subject: groupID,
ObjectType: auth.GroupType,
}); err != nil {
deleteRes, err := svc.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{
EntityType: auth.GroupType,
Id: id,
})
if err != nil {
return errors.Wrap(svcerr.ErrDeletePolicies, err)
}
// Remove policy of things
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.GroupType,
Subject: groupID,
ObjectType: auth.ThingType,
}); err != nil {
return errors.Wrap(svcerr.ErrDeletePolicies, err)
if !deleteRes.Deleted {
return svcerr.ErrAuthorization
}
// Remove policy from domain
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.DomainType,
Object: groupID,
ObjectType: auth.GroupType,
}); err != nil {
return errors.Wrap(svcerr.ErrDeletePolicies, err)
}
// Remove group from database
if err := svc.groups.Delete(ctx, groupID); err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
// Remove policy of users
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.UserType,
Object: groupID,
ObjectType: auth.GroupType,
}); err != nil {
return errors.Wrap(svcerr.ErrDeletePolicies, err)
if err := svc.groups.Delete(ctx, id); err != nil {
return err
}
return nil
+46 -153
View File
@@ -66,7 +66,7 @@ func TestCreateGroup(t *testing.T) {
repoErr error
addPolResp *magistrala.AddPoliciesRes
addPolErr error
deletePolResp *magistrala.DeletePoliciesRes
deletePolResp *magistrala.DeletePolicyRes
deletePolErr error
err error
}{
@@ -1666,7 +1666,7 @@ func TestAssign(t *testing.T) {
repoErr error
addParentPoliciesRes *magistrala.AddPoliciesRes
addParentPoliciesErr error
deleteParentPoliciesRes *magistrala.DeletePoliciesRes
deleteParentPoliciesRes *magistrala.DeletePolicyRes
deleteParentPoliciesErr error
repoParentGroupErr error
err error
@@ -1890,7 +1890,7 @@ func TestAssign(t *testing.T) {
addPoliciesRes: &magistrala.AddPoliciesRes{
Added: true,
},
deleteParentPoliciesRes: &magistrala.DeletePoliciesRes{
deleteParentPoliciesRes: &magistrala.DeletePolicyRes{
Deleted: false,
},
deleteParentPoliciesErr: svcerr.ErrAuthorization,
@@ -2069,13 +2069,13 @@ func TestUnassign(t *testing.T) {
idErr error
authzResp *magistrala.AuthorizeRes
authzErr error
deletePoliciesRes *magistrala.DeletePoliciesRes
deletePoliciesRes *magistrala.DeletePolicyRes
deletePoliciesErr error
repoResp mggroups.Page
repoErr error
addParentPoliciesRes *magistrala.AddPoliciesRes
addParentPoliciesErr error
deleteParentPoliciesRes *magistrala.DeletePoliciesRes
deleteParentPoliciesRes *magistrala.DeletePolicyRes
deleteParentPoliciesErr error
repoParentGroupErr error
err error
@@ -2094,7 +2094,7 @@ func TestUnassign(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
},
@@ -2112,7 +2112,7 @@ func TestUnassign(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
},
@@ -2137,7 +2137,7 @@ func TestUnassign(t *testing.T) {
validGroup,
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
repoParentGroupErr: nil,
@@ -2156,7 +2156,7 @@ func TestUnassign(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
},
@@ -2242,7 +2242,7 @@ func TestUnassign(t *testing.T) {
validGroup,
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: false,
},
deletePoliciesErr: svcerr.ErrAuthorization,
@@ -2269,7 +2269,7 @@ func TestUnassign(t *testing.T) {
validGroup,
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
repoParentGroupErr: repoerr.ErrConflict,
@@ -2296,7 +2296,7 @@ func TestUnassign(t *testing.T) {
validGroup,
},
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
repoParentGroupErr: repoerr.ErrConflict,
@@ -2364,7 +2364,7 @@ func TestUnassign(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deletePoliciesRes: &magistrala.DeletePoliciesRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: false,
},
deletePoliciesErr: svcerr.ErrAuthorization,
@@ -2468,23 +2468,17 @@ func TestDeleteGroup(t *testing.T) {
svc := groups.NewService(repo, idProvider, authsvc)
cases := []struct {
desc string
token string
groupID string
idResp *magistrala.IdentityRes
idErr error
authzResp *magistrala.AuthorizeRes
authzErr error
deleteChildPoliciesRes *magistrala.DeletePolicyFilterRes
deleteChildPoliciesErr error
deleteThingsPoliciesRes *magistrala.DeletePolicyFilterRes
deleteThingsPoliciesErr error
deleteDomainsPoliciesRes *magistrala.DeletePolicyFilterRes
deleteDomainsPoliciesErr error
deleteUsersPoliciesRes *magistrala.DeletePolicyFilterRes
deleteUsersPoliciesErr error
repoErr error
err error
desc string
token string
groupID string
idResp *magistrala.IdentityRes
idErr error
authzResp *magistrala.AuthorizeRes
authzErr error
deletePoliciesRes *magistrala.DeletePolicyRes
deletePoliciesErr error
repoErr error
err error
}{
{
desc: "successfully",
@@ -2497,26 +2491,18 @@ func TestDeleteGroup(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteUsersPoliciesRes: &magistrala.DeletePolicyFilterRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
},
{
desc: "unsuccessfully with invalid token",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{},
idErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
desc: "unsuccessfully with invalid token",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{},
deletePoliciesRes: &magistrala.DeletePolicyRes{},
idErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "unsuccessfully with authorization error",
@@ -2529,11 +2515,12 @@ func TestDeleteGroup(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: false,
},
authzErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
deletePoliciesRes: &magistrala.DeletePolicyRes{},
authzErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "unsuccessfully with failed to remove child group policy",
desc: "unsuccessfully with failed to remove policy",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{
@@ -2543,80 +2530,11 @@ func TestDeleteGroup(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: false,
},
deleteChildPoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "unsuccessfully with failed to remove things policy",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{
Id: testsutil.GenerateUUID(t),
DomainId: testsutil.GenerateUUID(t),
},
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: false,
},
deleteThingsPoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "unsuccessfully with failed to remove domain policy",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{
Id: testsutil.GenerateUUID(t),
DomainId: testsutil.GenerateUUID(t),
},
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: false,
},
deleteDomainsPoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "unsuccessfully with failed to remove user policy",
token: token,
groupID: testsutil.GenerateUUID(t),
idResp: &magistrala.IdentityRes{
Id: testsutil.GenerateUUID(t),
DomainId: testsutil.GenerateUUID(t),
},
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteUsersPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: false,
},
deleteUsersPoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
deletePoliciesErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "unsuccessfully with repo err",
@@ -2629,13 +2547,7 @@ func TestDeleteGroup(t *testing.T) {
authzResp: &magistrala.AuthorizeRes{
Authorized: true,
},
deleteChildPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteThingsPoliciesRes: &magistrala.DeletePolicyFilterRes{
Deleted: true,
},
deleteDomainsPoliciesRes: &magistrala.DeletePolicyFilterRes{
deletePoliciesRes: &magistrala.DeletePolicyRes{
Deleted: true,
},
repoErr: repoerr.ErrNotFound,
@@ -2655,36 +2567,17 @@ func TestDeleteGroup(t *testing.T) {
Object: tc.groupID,
ObjectType: auth.GroupType,
}).Return(tc.authzResp, tc.authzErr)
authcall2 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{
SubjectType: auth.GroupType,
Subject: tc.groupID,
ObjectType: auth.GroupType,
}).Return(tc.deleteChildPoliciesRes, tc.deleteChildPoliciesErr)
authcall3 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{
SubjectType: auth.GroupType,
Subject: tc.groupID,
ObjectType: auth.ThingType,
}).Return(tc.deleteThingsPoliciesRes, tc.deleteThingsPoliciesErr)
authcall4 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{
SubjectType: auth.DomainType,
Object: tc.groupID,
ObjectType: auth.GroupType,
}).Return(tc.deleteDomainsPoliciesRes, tc.deleteDomainsPoliciesErr)
authcall5 := repo.On("Delete", context.Background(), tc.groupID).Return(tc.repoErr)
authcall6 := authsvc.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{
SubjectType: auth.UserType,
Object: tc.groupID,
ObjectType: auth.GroupType,
}).Return(tc.deleteUsersPoliciesRes, tc.deleteUsersPoliciesErr)
authcall2 := authsvc.On("DeleteEntityPolicies", context.Background(), &magistrala.DeleteEntityPoliciesReq{
EntityType: auth.GroupType,
Id: tc.groupID,
}).Return(tc.deletePoliciesRes, tc.deletePoliciesErr)
repocall := repo.On("Delete", context.Background(), tc.groupID).Return(tc.repoErr)
err := svc.DeleteGroup(context.Background(), tc.token, tc.groupID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("expected error %v to contain %v", err, tc.err))
authcall.Unset()
authcall1.Unset()
authcall2.Unset()
authcall3.Unset()
authcall4.Unset()
authcall5.Unset()
authcall6.Unset()
repocall.Unset()
})
}
}
+3
View File
@@ -101,6 +101,9 @@ type Repository interface {
// ChangeStatus changes client status to enabled or disabled
ChangeStatus(ctx context.Context, client Client) (Client, error)
// Delete deletes client with given id
Delete(ctx context.Context, id string) error
}
// Validate returns an error if client representation is invalid.
+14
View File
@@ -320,6 +320,20 @@ func (repo *Repository) update(ctx context.Context, client clients.Client, query
return clients.Client{}, repoerr.ErrNotFound
}
func (repo *Repository) Delete(ctx context.Context, id string) error {
q := "DELETE FROM clients AS c WHERE c.id = $1 ;"
result, err := repo.DB.ExecContext(ctx, q, id)
if err != nil {
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
}
return nil
}
type DBClient struct {
ID string `db:"id"`
Name string `db:"name,omitempty"`
+37
View File
@@ -1798,6 +1798,43 @@ func TestUpdateRole(t *testing.T) {
}
}
func TestDelete(t *testing.T) {
t.Cleanup(func() {
_, err := db.Exec("DELETE FROM clients")
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
})
repo := &postgres.Repository{database}
client := generateClient(t, mgclients.EnabledStatus, mgclients.UserRole, repo)
cases := []struct {
desc string
id string
err error
}{
{
desc: "delete client successfully",
id: client.ID,
err: nil,
},
{
desc: "delete client with invalid id",
id: testsutil.GenerateUUID(t),
err: repoerr.ErrNotFound,
},
{
desc: "delete client with empty id",
id: "",
err: repoerr.ErrNotFound,
},
}
for _, tc := range cases {
err := repo.Delete(context.Background(), tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func findClients(clis []mgclients.Client, query string, offset, limit uint64) []mgclients.Client {
rclients := []mgclients.Client{}
for _, client := range clis {
+7
View File
@@ -19,6 +19,8 @@ const (
EnabledStatus Status = iota
// DisabledStatus represents disabled Client.
DisabledStatus
// DeletedStatus represents a client that will be deleted.
DeletedStatus
// AllStatus is used for querying purposes to list clients irrespective
// of their status - both enabled and disabled. It is never stored in the
@@ -31,6 +33,7 @@ const (
const (
Disabled = "disabled"
Enabled = "enabled"
Deleted = "deleted"
All = "all"
Unknown = "unknown"
)
@@ -42,6 +45,8 @@ func (s Status) String() string {
return Disabled
case EnabledStatus:
return Enabled
case DeletedStatus:
return Deleted
case AllStatus:
return All
default:
@@ -56,6 +61,8 @@ func ToStatus(status string) (Status, error) {
return EnabledStatus, nil
case Disabled:
return DisabledStatus, nil
case Deleted:
return DeletedStatus, nil
case All:
return AllStatus, nil
}
+29
View File
@@ -27,6 +27,11 @@ func TestStatusString(t *testing.T) {
status: clients.DisabledStatus,
expected: "disabled",
},
{
desc: "Deleted",
status: clients.DeletedStatus,
expected: "deleted",
},
{
desc: "All",
status: clients.AllStatus,
@@ -66,6 +71,12 @@ func TestToStatus(t *testing.T) {
expetcted: clients.DisabledStatus,
err: nil,
},
{
desc: "Deleted",
status: "deleted",
expetcted: clients.DeletedStatus,
err: nil,
},
{
desc: "All",
status: "all",
@@ -108,6 +119,12 @@ func TestStatusMarshalJSON(t *testing.T) {
status: clients.DisabledStatus,
err: nil,
},
{
desc: "Deleted",
expected: []byte(`"deleted"`),
status: clients.DeletedStatus,
err: nil,
},
{
desc: "All",
expected: []byte(`"all"`),
@@ -150,6 +167,12 @@ func TestStatusUnmarshalJSON(t *testing.T) {
status: []byte(`"disabled"`),
err: nil,
},
{
desc: "Deleted",
expected: clients.DeletedStatus,
status: []byte(`"deleted"`),
err: nil,
},
{
desc: "All",
expected: clients.AllStatus,
@@ -193,6 +216,12 @@ func TestUserMarshalJSON(t *testing.T) {
user: clients.Client{Status: clients.DisabledStatus},
err: nil,
},
{
desc: "Deleted",
expected: []byte(`{"id":"","credentials":{},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","status":"deleted"}`),
user: clients.Client{Status: clients.DeletedStatus},
err: nil,
},
{
desc: "All",
expected: []byte(`{"id":"","credentials":{},"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","status":"all"}`),
+2 -2
View File
@@ -140,7 +140,7 @@ func TestCreateChannel(t *testing.T) {
authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil)
authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil)
repoCall := grepo.On("Save", mock.Anything, mock.Anything).Return(convertChannel(sdk.Channel{}), tc.err)
rChannel, err := mgsdk.CreateChannel(tc.channel, validToken)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
@@ -771,7 +771,7 @@ func TestDeleteChannel(t *testing.T) {
authCall1.Unset()
repoCall.Unset()
authCall = auth.On("DeletePolicyFilter", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil)
authCall = auth.On("DeleteEntityPolicies", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
authCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall = grepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
+7
View File
@@ -260,6 +260,13 @@ type SDK interface {
// fmt.Println(user)
DisableUser(id, token string) (User, errors.SDKError)
// DeleteUser deletes a user with the given id.
//
// example:
// err := sdk.DeleteUser("userID", "token")
// fmt.Println(err)
DeleteUser(id, token string) errors.SDKError
// CreateToken receives credentials and returns user token.
//
// example:
-6
View File
@@ -65,12 +65,6 @@ func generateUUID(t *testing.T) string {
return ulid
}
func convertClientsPage(cp sdk.UsersPage) mgclients.ClientsPage {
return mgclients.ClientsPage{
Clients: convertClients(cp.Users),
}
}
func convertThingsPage(cp sdk.ThingsPage) mgclients.ClientsPage {
return mgclients.ClientsPage{
Clients: convertThings(cp.Things...),
+17 -17
View File
@@ -186,7 +186,7 @@ func TestCreateThing(t *testing.T) {
authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil)
authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil)
repoCall3 := cRepo.On("Save", mock.Anything, mock.Anything).Return(convertThings(tc.response), tc.repoErr)
rThing, err := mgsdk.CreateThing(tc.client, tc.token)
@@ -275,7 +275,7 @@ func TestCreateThings(t *testing.T) {
authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil)
authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: false}, nil)
authCall3 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, nil)
repoCall1 := cRepo.On("Save", mock.Anything, mock.Anything).Return(convertThings(tc.response...), tc.err)
if len(tc.things) > 0 {
repoCall1 = cRepo.On("Save", mock.Anything, mock.Anything, mock.Anything).Return(convertThings(tc.response...), tc.err)
@@ -1272,27 +1272,27 @@ func TestDeleteThing(t *testing.T) {
Status: mgclients.EnabledStatus.String(),
}
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil)
repoCall2 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
repoCall4 := cache.On("Remove", mock.Anything, thing.ID).Return(nil)
authCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil)
repoCall := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
cacheCall := cache.On("Remove", mock.Anything, thing.ID).Return(nil)
err := mgsdk.DeleteThing("wrongID", validToken)
assert.Equal(t, err, errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), fmt.Sprintf("Delete thing with wrong id: expected %v got %v", svcerr.ErrNotFound, err))
authCall.Unset()
authCall1.Unset()
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall = auth.On("DeletePolicyFilter", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil)
repoCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
repoCall2 = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall3 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
authCall = auth.On("DeleteEntityPolicies", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
authCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
authCall2 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall = cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
err = mgsdk.DeleteThing(thing.ID, validToken)
assert.Nil(t, err, fmt.Sprintf("Delete thing with correct id: expected %v got %v", nil, err))
ok := repoCall3.Parent.AssertCalled(t, "Delete", mock.Anything, thing.ID)
ok := repoCall.Parent.AssertCalled(t, "Delete", mock.Anything, thing.ID)
assert.True(t, ok, "Delete was not called on deleting thing with correct id")
authCall.Unset()
authCall1.Unset()
authCall2.Unset()
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall3.Unset()
repoCall4.Unset()
cacheCall.Unset()
}
+6
View File
@@ -344,3 +344,9 @@ func (sdk mgSDK) changeClientStatus(token, id, status string) (User, errors.SDKE
return user, nil
}
func (sdk mgSDK) DeleteUser(id, token string) errors.SDKError {
url := fmt.Sprintf("%s/%s/%s", sdk.usersURL, usersEndpoint, id)
_, _, sdkerr := sdk.processRequest(http.MethodDelete, url, token, nil, nil, http.StatusNoContent)
return sdkerr
}
+59 -90
View File
@@ -181,7 +181,7 @@ func TestCreateClient(t *testing.T) {
repoCall = auth.On("Identify", mock.Anything, mock.Anything).Return(&magistrala.IdentityRes{}, svcerr.ErrAuthentication)
}
repoCall1 := auth.On("AddPolicies", mock.Anything, mock.Anything).Return(&magistrala.AddPoliciesRes{Added: true}, nil)
repoCall2 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePoliciesRes{Deleted: true}, nil)
repoCall2 := auth.On("DeletePolicies", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
repoCall3 := crepo.On("Save", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.err)
rClient, err := mgsdk.CreateUser(tc.client, tc.token)
tc.response.ID = rClient.ID
@@ -957,7 +957,7 @@ func TestUpdateClientRole(t *testing.T) {
repoCall = auth.On("Identify", mock.Anything, mock.Anything).Return(&magistrala.IdentityRes{}, svcerr.ErrAuthentication)
}
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall2 := auth.On("DeletePolicyFilter", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyFilterRes{Deleted: true}, nil)
repoCall2 := auth.On("DeletePolicyFilter", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
repoCall3 := auth.On("AddPolicy", mock.Anything, mock.Anything).Return(&magistrala.AddPolicyRes{Added: true}, nil)
repoCall4 := crepo.On("UpdateRole", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.err)
uClient, err := mgsdk.UpdateUserRole(tc.client, tc.token)
@@ -1051,59 +1051,6 @@ func TestEnableClient(t *testing.T) {
repoCall2.Unset()
repoCall3.Unset()
}
cases2 := []struct {
desc string
token string
status string
metadata sdk.Metadata
response sdk.UsersPage
size uint64
}{
{
desc: "list enabled clients",
status: mgclients.EnabledStatus.String(),
size: 2,
response: sdk.UsersPage{
Users: []sdk.User{enabledClient1, endisabledClient1},
},
},
{
desc: "list disabled clients",
status: mgclients.DisabledStatus.String(),
size: 1,
response: sdk.UsersPage{
Users: []sdk.User{disabledClient1},
},
},
{
desc: "list enabled and disabled clients",
status: mgclients.AllStatus.String(),
size: 3,
response: sdk.UsersPage{
Users: []sdk.User{enabledClient1, disabledClient1, endisabledClient1},
},
},
}
for _, tc := range cases2 {
pm := sdk.PageMetadata{
Total: 100,
Offset: 0,
Limit: 100,
Status: tc.status,
}
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: validID}, nil)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall2 := crepo.On("RetrieveAll", mock.Anything, mock.Anything).Return(convertClientsPage(tc.response), nil)
clientsPage, err := mgsdk.Users(pm, validToken)
assert.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
size := uint64(len(clientsPage.Users))
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size))
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
}
}
func TestDisableClient(t *testing.T) {
@@ -1180,57 +1127,79 @@ func TestDisableClient(t *testing.T) {
repoCall2.Unset()
repoCall3.Unset()
}
}
cases2 := []struct {
func TestDeleteUser(t *testing.T) {
ts, crepo, _, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
enabledClient1 := sdk.User{ID: testsutil.GenerateUUID(t), Credentials: sdk.Credentials{Identity: "client1@example.com", Secret: "password"}, Status: mgclients.EnabledStatus.String()}
deletedClient1 := sdk.User{ID: testsutil.GenerateUUID(t), Credentials: sdk.Credentials{Identity: "client3@example.com", Secret: "password"}, Status: mgclients.DeletedStatus.String()}
deletedenabledClient1 := enabledClient1
deletedenabledClient1.Status = mgclients.DisabledStatus.String()
deletedenabledClient1.ID = testsutil.GenerateUUID(t)
cases := []struct {
desc string
id string
token string
status string
metadata sdk.Metadata
response sdk.UsersPage
size uint64
client sdk.User
response sdk.User
repoErr error
err errors.SDKError
}{
{
desc: "list enabled clients",
status: mgclients.EnabledStatus.String(),
size: 2,
response: sdk.UsersPage{
Users: []sdk.User{enabledClient1, disenabledClient1},
},
desc: "delete enabled client",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
response: deletedenabledClient1,
err: nil,
repoErr: nil,
},
{
desc: "list disabled clients",
status: mgclients.DisabledStatus.String(),
size: 1,
response: sdk.UsersPage{
Users: []sdk.User{disabledClient1},
},
desc: "delete disabled client",
id: deletedClient1.ID,
token: validToken,
client: deletedClient1,
response: sdk.User{},
repoErr: sdk.ErrFailedDisable,
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
},
{
desc: "list enabled and disabled clients",
status: mgclients.AllStatus.String(),
size: 3,
response: sdk.UsersPage{
Users: []sdk.User{enabledClient1, disabledClient1, disenabledClient1},
},
desc: "delete non-existing client",
id: wrongID,
client: sdk.User{},
token: validToken,
response: sdk.User{},
repoErr: sdk.ErrFailedDisable,
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
},
}
for _, tc := range cases2 {
pm := sdk.PageMetadata{
Total: 100,
Offset: 0,
Limit: 100,
Status: tc.status,
}
for _, tc := range cases {
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: validID}, nil)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall2 := crepo.On("RetrieveAll", mock.Anything, mock.Anything).Return(convertClientsPage(tc.response), nil)
page, err := mgsdk.Users(pm, validToken)
assert.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
size := uint64(len(page.Users))
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size))
repoCall2 := crepo.On("RetrieveByID", mock.Anything, tc.id).Return(convertClient(tc.client), tc.repoErr)
repoCall3 := crepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(convertClient(tc.response), tc.repoErr)
err := mgsdk.DeleteUser(tc.id, tc.token)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err))
if tc.err == nil {
ok := repoCall.Parent.AssertCalled(t, "Identify", mock.Anything, mock.Anything)
assert.True(t, ok, fmt.Sprintf("Identify was not called on %s", tc.desc))
ok = repoCall2.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, tc.id)
assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc))
ok = repoCall3.Parent.AssertCalled(t, "ChangeStatus", mock.Anything, mock.Anything)
assert.True(t, ok, fmt.Sprintf("ChangeStatus was not called on %s", tc.desc))
}
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall3.Unset()
}
}
+20
View File
@@ -766,6 +766,26 @@ func (_m *SDK) DeleteThing(id string, token string) errors.SDKError {
return r0
}
// DeleteUser provides a mock function with given fields: id, token
func (_m *SDK) DeleteUser(id string, token string) errors.SDKError {
ret := _m.Called(id, token)
if len(ret) == 0 {
panic("no return value specified for DeleteUser")
}
var r0 errors.SDKError
if rf, ok := ret.Get(0).(func(string, string) errors.SDKError); ok {
r0 = rf(id, token)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(errors.SDKError)
}
}
return r0
}
// DisableChannel provides a mock function with given fields: id, token
func (_m *SDK) DisableChannel(id string, token string) (sdk.Channel, errors.SDKError) {
ret := _m.Called(id, token)
-17
View File
@@ -33,9 +33,6 @@ type Repository interface {
// RetrieveBySecret retrieves a client based on the secret (key).
RetrieveBySecret(ctx context.Context, key string) (mgclients.Client, error)
// Delete deletes client with given id
Delete(ctx context.Context, id string) error
}
// NewRepository instantiates a PostgreSQL
@@ -124,17 +121,3 @@ func (repo clientRepo) RetrieveBySecret(ctx context.Context, key string) (mgclie
return mgclients.Client{}, repoerr.ErrNotFound
}
func (repo clientRepo) Delete(ctx context.Context, id string) error {
q := "DELETE FROM clients AS c WHERE c.id = $1 ;"
result, err := repo.DB.ExecContext(ctx, q, id)
if err != nil {
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
}
if rows, _ := result.RowsAffected(); rows == 0 {
return repoerr.ErrNotFound
}
return nil
}
-49
View File
@@ -25,7 +25,6 @@ var (
invalidName = strings.Repeat("m", maxNameSize+10)
clientIdentity = "client-identity@example.com"
clientName = "client name"
invalidClientID = "invalidClientID"
invalidDomainID = strings.Repeat("m", maxNameSize+10)
namesgen = namegenerator.NewGenerator()
)
@@ -365,51 +364,3 @@ func TestClientsRetrieveBySecret(t *testing.T) {
assert.Equal(t, res, tc.response, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, res))
}
}
func TestDelete(t *testing.T) {
t.Cleanup(func() {
_, err := db.Exec("DELETE FROM clients")
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
})
repo := postgres.NewRepository(database)
client := clients.Client{
ID: testsutil.GenerateUUID(t),
Name: clientName,
Credentials: clients.Credentials{
Identity: clientIdentity,
Secret: testsutil.GenerateUUID(t),
},
Status: clients.EnabledStatus,
}
_, err := repo.Save(context.Background(), client)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
cases := []struct {
desc string
id string
err error
}{
{
desc: "delete client successfully",
id: client.ID,
err: nil,
},
{
desc: "delete client with invalid id",
id: invalidClientID,
err: repoerr.ErrNotFound,
},
{
desc: "delete client with empty id",
id: "",
err: repoerr.ErrNotFound,
},
}
for _, tc := range cases {
err := repo.Delete(context.Background(), tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
+7 -25
View File
@@ -438,43 +438,25 @@ func (svc service) DeleteClient(ctx context.Context, token, id string) error {
return err
}
// Remove from cache
if err := svc.clientCache.Remove(ctx, id); err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
// Remove policy of groups
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.GroupType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
deleteRes, err := svc.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{
EntityType: auth.ThingType,
Id: id,
})
if err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
// Remove policy from domain
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.DomainType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
if !deleteRes.Deleted {
return svcerr.ErrAuthorization
}
// Remove thing from database
if err := svc.clients.Delete(ctx, id); err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
// Remove policy of users
if _, err := svc.auth.DeletePolicyFilter(ctx, &magistrala.DeletePolicyFilterReq{
SubjectType: auth.UserType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
return errors.Wrap(svcerr.ErrRemoveEntity, err)
}
return nil
}
+39 -82
View File
@@ -65,7 +65,7 @@ func TestCreateThings(t *testing.T) {
token string
authResponse *magistrala.AuthorizeRes
addPolicyResponse *magistrala.AddPoliciesRes
deletePolicyRes *magistrala.DeletePoliciesRes
deletePolicyRes *magistrala.DeletePolicyRes
authorizeErr error
identifyErr error
addPolicyErr error
@@ -320,7 +320,7 @@ func TestCreateThings(t *testing.T) {
authResponse: &magistrala.AuthorizeRes{Authorized: true},
addPolicyResponse: &magistrala.AddPoliciesRes{Added: true},
saveErr: repoerr.ErrConflict,
deletePolicyRes: &magistrala.DeletePoliciesRes{Deleted: false},
deletePolicyRes: &magistrala.DeletePolicyRes{Deleted: false},
deletePolicyErr: svcerr.ErrInvalidPolicy,
err: repoerr.ErrConflict,
},
@@ -1609,33 +1609,27 @@ func TestDeleteClient(t *testing.T) {
invalidClientID := "invalidClientID"
_ = invalidClientID
cases := []struct {
desc string
token string
identifyResponse *magistrala.IdentityRes
authorizeResponse *magistrala.AuthorizeRes
deletePolicyResponse *magistrala.DeletePolicyFilterRes
deletePolicyResponse1 *magistrala.DeletePolicyFilterRes
deletePolicyResponse2 *magistrala.DeletePolicyFilterRes
clientID string
identifyErr error
authorizeErr error
removeErr error
deleteErr error
deletePolicyErr error
deletePolicyErr1 error
deletePolicyErr2 error
err error
desc string
token string
identifyResponse *magistrala.IdentityRes
authorizeResponse *magistrala.AuthorizeRes
deletePolicyResponse *magistrala.DeletePolicyRes
clientID string
identifyErr error
authorizeErr error
removeErr error
deleteErr error
deletePolicyErr error
err error
}{
{
desc: "Delete client with authorized token",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse2: &magistrala.DeletePolicyFilterRes{Deleted: true},
err: nil,
desc: "Delete client with authorized token",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
desc: "Delete client with unauthorized token",
@@ -1655,15 +1649,14 @@ func TestDeleteClient(t *testing.T) {
err: svcerr.ErrAuthorization,
},
{
desc: "Delete client with repo error ",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true},
deleteErr: repoerr.ErrRemoveEntity,
err: repoerr.ErrRemoveEntity,
desc: "Delete client with repo error ",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: true},
deleteErr: repoerr.ErrRemoveEntity,
err: repoerr.ErrRemoveEntity,
},
{
desc: "Delete client with cache error ",
@@ -1675,60 +1668,26 @@ func TestDeleteClient(t *testing.T) {
err: repoerr.ErrRemoveEntity,
},
{
desc: "Delete client with failed to delete groups policy",
desc: "Delete client with failed to delete policy",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyResponse: &magistrala.DeletePolicyRes{Deleted: false},
deletePolicyErr: errRemovePolicies,
err: errRemovePolicies,
},
{
desc: "Delete client with failed to delete domains policy",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyErr1: errRemovePolicies,
err: errRemovePolicies,
},
{
desc: "Delete client with failed to delete users policy",
token: validToken,
clientID: client.ID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse1: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyResponse2: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyErr2: errRemovePolicies,
err: errRemovePolicies,
},
}
for _, tc := range cases {
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(tc.identifyResponse, tc.identifyErr)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(tc.authorizeResponse, tc.authorizeErr)
repoCall2 := cache.On("Remove", mock.Anything, tc.clientID).Return(tc.removeErr)
repoCall3 := auth.On("DeletePolicyFilter", context.Background(), &magistrala.DeletePolicyFilterReq{
SubjectType: authsvc.GroupType,
Object: tc.clientID,
ObjectType: authsvc.ThingType,
repoCall3 := auth.On("DeleteEntityPolicies", context.Background(), &magistrala.DeleteEntityPoliciesReq{
EntityType: authsvc.ThingType,
Id: tc.clientID,
}).Return(tc.deletePolicyResponse, tc.deletePolicyErr)
repoCall4 := auth.On("DeletePolicyFilter", mock.Anything, &magistrala.DeletePolicyFilterReq{
SubjectType: authsvc.DomainType,
Object: tc.clientID,
ObjectType: authsvc.ThingType,
}).Return(tc.deletePolicyResponse1, tc.deletePolicyErr1)
repoCall5 := cRepo.On("Delete", context.Background(), tc.clientID).Return(tc.deleteErr)
repoCall6 := auth.On("DeletePolicyFilter", mock.Anything, &magistrala.DeletePolicyFilterReq{
SubjectType: authsvc.UserType,
Object: tc.clientID,
ObjectType: authsvc.ThingType,
}).Return(tc.deletePolicyResponse2, tc.deletePolicyErr2)
repoCall4 := cRepo.On("Delete", context.Background(), tc.clientID).Return(tc.deleteErr)
err := svc.DeleteClient(context.Background(), tc.token, tc.clientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
@@ -1736,8 +1695,6 @@ func TestDeleteClient(t *testing.T) {
repoCall2.Unset()
repoCall3.Unset()
repoCall4.Unset()
repoCall5.Unset()
repoCall6.Unset()
}
}
@@ -1832,7 +1789,7 @@ func TestUnShare(t *testing.T) {
userID string
identifyResponse *magistrala.IdentityRes
authorizeResponse *magistrala.AuthorizeRes
deletePoliciesResponse *magistrala.DeletePoliciesRes
deletePoliciesResponse *magistrala.DeletePolicyRes
identifyErr error
authorizeErr error
deletePoliciesErr error
@@ -1844,7 +1801,7 @@ func TestUnShare(t *testing.T) {
clientID: clientID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
@@ -1870,7 +1827,7 @@ func TestUnShare(t *testing.T) {
clientID: clientID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{},
deletePoliciesResponse: &magistrala.DeletePolicyRes{},
deletePoliciesErr: svcerr.ErrInvalidPolicy,
err: svcerr.ErrInvalidPolicy,
},
@@ -1880,7 +1837,7 @@ func TestUnShare(t *testing.T) {
clientID: clientID,
identifyResponse: &magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false},
err: nil,
},
}
+6 -2
View File
@@ -62,11 +62,11 @@ func (repo singleUserRepo) AddPolicies(ctx context.Context, in *magistrala.AddPo
return nil, nil
}
func (repo singleUserRepo) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyFilterRes, error) {
func (repo singleUserRepo) DeletePolicyFilter(ctx context.Context, in *magistrala.DeletePolicyFilterReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
return nil, nil
}
func (repo singleUserRepo) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) {
func (repo singleUserRepo) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
return nil, nil
}
@@ -97,3 +97,7 @@ func (repo singleUserRepo) CountSubjects(ctx context.Context, in *magistrala.Cou
func (repo singleUserRepo) ListPermissions(ctx context.Context, in *magistrala.ListPermissionsReq, opts ...grpc.CallOption) (*magistrala.ListPermissionsRes, error) {
return nil, nil
}
func (repo singleUserRepo) DeleteEntityPolicies(ctx context.Context, in *magistrala.DeleteEntityPoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePolicyRes, error) {
return nil, nil
}
+8
View File
@@ -48,6 +48,10 @@ The service is configured using the environment variables presented in the follo
| MG_EMAIL_TEMPLATE | Email template for sending emails with password reset link | email.tmpl |
| MG_USERS_ES_URL | Event store URL | <nats://localhost:4222> |
| MG_JAEGER_URL | Jaeger server URL | <http://localhost:14268/api/traces> |
| MG_OAUTH_UI_REDIRECT_URL | OAuth UI redirect URL | <http://localhost:9095/domains> |
| MG_OAUTH_UI_ERROR_URL | OAuth UI error URL | <http://localhost:9095/error> |
| MG_USERS_DELETE_INTERVAL | Interval for deleting users | 24h |
| MG_USERS_DELETE_AFTER | Time after which users are deleted | 720h |
| MG_JAEGER_TRACE_RATIO | Jaeger sampling ratio | 1.0 |
| MG_SEND_TELEMETRY | Send telemetry to magistrala call home server. | true |
| MG_USERS_INSTANCE_ID | Magistrala instance ID | "" |
@@ -107,6 +111,10 @@ MG_USERS_ES_URL=nats://localhost:4222 \
MG_JAEGER_URL=http://localhost:14268/api/traces \
MG_JAEGER_TRACE_RATIO=1.0 \
MG_SEND_TELEMETRY=true \
MG_OAUTH_UI_REDIRECT_URL=http://localhost:9095/domains \
MG_OAUTH_UI_ERROR_URL=http://localhost:9095/error \
MG_USERS_DELETE_INTERVAL=24h \
MG_USERS_DELETE_AFTER=720h \
MG_USERS_INSTANCE_ID="" \
$GOBIN/magistrala-users
```
+7
View File
@@ -124,6 +124,13 @@ func clientsHandler(svc users.Service, r *chi.Mux, logger *slog.Logger, pr *rege
api.EncodeResponse,
opts...,
), "disable_client").ServeHTTP)
r.Delete("/{id}", otelhttp.NewHandler(kithttp.NewServer(
deleteClientEndpoint(svc),
decodeChangeClientStatus,
api.EncodeResponse,
opts...,
), "delete_client").ServeHTTP)
})
r.Route("/password", func(r chi.Router) {
+68
View File
@@ -1734,6 +1734,74 @@ func TestDisableClient(t *testing.T) {
}
}
func TestDeleteClient(t *testing.T) {
us, svc, _ := newUsersServer()
defer us.Close()
cases := []struct {
desc string
client mgclients.Client
response mgclients.Client
token string
status int
err error
}{
{
desc: "delete user with valid token",
client: client,
response: mgclients.Client{
ID: client.ID,
},
token: validToken,
status: http.StatusNoContent,
err: nil,
},
{
desc: "delete user with invalid token",
client: client,
token: inValidToken,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthentication,
},
{
desc: "delete user with empty id",
client: mgclients.Client{
ID: "",
},
token: validToken,
status: http.StatusMethodNotAllowed,
err: apiutil.ErrMissingID,
},
{
desc: "delete user with invalid id",
client: mgclients.Client{
ID: "invalid",
},
token: validToken,
status: http.StatusForbidden,
err: svcerr.ErrAuthorization,
},
}
for _, tc := range cases {
data := toJSON(tc.client)
req := testRequest{
client: us.Client(),
method: http.MethodDelete,
url: fmt.Sprintf("%s/users/%s", us.URL, tc.client.ID),
contentType: contentType,
token: tc.token,
body: strings.NewReader(data),
}
repoCall := svc.On("DeleteClient", mock.Anything, mock.Anything, mock.Anything).Return(tc.err)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
repoCall.Unset()
}
}
func TestListUsersByUserGroupId(t *testing.T) {
us, svc, _ := newUsersServer()
defer us.Close()
+17 -2
View File
@@ -360,7 +360,7 @@ func enableClientEndpoint(svc users.Service) endpoint.Endpoint {
return nil, err
}
return deleteClientRes{Client: client}, nil
return changeClientStatusClientRes{Client: client}, nil
}
}
@@ -376,7 +376,22 @@ func disableClientEndpoint(svc users.Service) endpoint.Endpoint {
return nil, err
}
return deleteClientRes{Client: client}, nil
return changeClientStatusClientRes{Client: client}, nil
}
}
func deleteClientEndpoint(svc users.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(changeClientStatusReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.DeleteClient(ctx, req.token, req.id); err != nil {
return nil, err
}
return deleteClientRes{true}, nil
}
}
+17
View File
@@ -413,3 +413,20 @@ func (lm *loggingMiddleware) OAuthCallback(ctx context.Context, client mgclients
}(time.Now())
return lm.svc.OAuthCallback(ctx, client)
}
// DeleteClient logs the delete_client request. It logs the client id and token and the time it took to complete the request.
func (lm *loggingMiddleware) DeleteClient(ctx context.Context, token, id string) (err error) {
defer func(begin time.Time) {
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.String("user_id", id),
}
if err != nil {
args = append(args, slog.Any("error", err))
lm.logger.Warn("Delete user failed to complete successfully", args...)
return
}
lm.logger.Info("Delete user completed successfully", args...)
}(time.Now())
return lm.svc.DeleteClient(ctx, token, id)
}
+9
View File
@@ -199,3 +199,12 @@ func (ms *metricsMiddleware) OAuthCallback(ctx context.Context, client mgclients
}(time.Now())
return ms.svc.OAuthCallback(ctx, client)
}
// DeleteClient instruments DeleteClient method with metrics.
func (ms *metricsMiddleware) DeleteClient(ctx context.Context, token, id string) error {
defer func(begin time.Time) {
ms.counter.With("method", "delete_client").Add(1)
ms.latency.With("method", "delete_client").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.DeleteClient(ctx, token, id)
}
+32 -5
View File
@@ -18,9 +18,16 @@ var (
_ magistrala.Response = (*tokenRes)(nil)
_ magistrala.Response = (*viewClientRes)(nil)
_ magistrala.Response = (*createClientRes)(nil)
_ magistrala.Response = (*deleteClientRes)(nil)
_ magistrala.Response = (*changeClientStatusClientRes)(nil)
_ magistrala.Response = (*clientsPageRes)(nil)
_ magistrala.Response = (*viewMembersRes)(nil)
_ magistrala.Response = (*passwResetReqRes)(nil)
_ magistrala.Response = (*passwChangeRes)(nil)
_ magistrala.Response = (*assignUsersRes)(nil)
_ magistrala.Response = (*unassignUsersRes)(nil)
_ magistrala.Response = (*updateClientRes)(nil)
_ magistrala.Response = (*tokenRes)(nil)
_ magistrala.Response = (*deleteClientRes)(nil)
)
type pageRes struct {
@@ -139,19 +146,19 @@ func (res viewMembersRes) Empty() bool {
return false
}
type deleteClientRes struct {
type changeClientStatusClientRes struct {
mgclients.Client `json:",inline"`
}
func (res deleteClientRes) Code() int {
func (res changeClientStatusClientRes) Code() int {
return http.StatusOK
}
func (res deleteClientRes) Headers() map[string]string {
func (res changeClientStatusClientRes) Headers() map[string]string {
return map[string]string{}
}
func (res deleteClientRes) Empty() bool {
func (res changeClientStatusClientRes) Empty() bool {
return false
}
@@ -212,3 +219,23 @@ func (res unassignUsersRes) Headers() map[string]string {
func (res unassignUsersRes) Empty() bool {
return true
}
type deleteClientRes struct {
deleted bool
}
func (res deleteClientRes) Code() int {
if res.deleted {
return http.StatusNoContent
}
return http.StatusOK
}
func (res deleteClientRes) Headers() map[string]string {
return map[string]string{}
}
func (res deleteClientRes) Empty() bool {
return true
}
+3
View File
@@ -63,6 +63,9 @@ type Service interface {
// DisableClient logically disables the client identified with the provided ID.
DisableClient(ctx context.Context, token, id string) (clients.Client, error)
// DeleteClient deletes client with given ID.
DeleteClient(ctx context.Context, token, id string) error
// Identify returns the client id from the given token.
Identify(ctx context.Context, tkn string) (string, error)
+100
View File
@@ -0,0 +1,100 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// The DeleteHandler is a cron job that runs periodically to delete users that have been marked as deleted
// for a certain period of time together with the user's policies from the auth service.
// The handler runs in a separate goroutine and checks for users that have been marked as deleted for a certain period of time.
// If the user has been marked as deleted for more than the specified period,
// the handler deletes the user's policies from the auth service and deletes the user from the database.
package users
import (
"context"
"log/slog"
"time"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
mgclients "github.com/absmach/magistrala/pkg/clients"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/users/postgres"
)
const defLimit = uint64(100)
type handler struct {
clients postgres.Repository
auth magistrala.AuthServiceClient
checkInterval time.Duration
deleteAfter time.Duration
logger *slog.Logger
}
func NewDeleteHandler(ctx context.Context, clients postgres.Repository, auth magistrala.AuthServiceClient, defCheckInterval, deleteAfter time.Duration, logger *slog.Logger) {
handler := &handler{
clients: clients,
auth: auth,
checkInterval: defCheckInterval,
deleteAfter: deleteAfter,
logger: logger,
}
go func() {
ticker := time.NewTicker(handler.checkInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
handler.handle(ctx)
}
}
}()
}
func (h *handler) handle(ctx context.Context) {
pm := mgclients.Page{Limit: defLimit, Offset: 0, Status: mgclients.DeletedStatus}
for {
dbUsers, err := h.clients.RetrieveAll(ctx, pm)
if err != nil {
h.logger.Error("failed to retrieve users", slog.Any("error", err))
break
}
if dbUsers.Total == 0 {
break
}
for _, u := range dbUsers.Clients {
if time.Since(u.UpdatedAt) < h.deleteAfter {
continue
}
deleteRes, err := h.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{
Id: u.ID,
EntityType: auth.UserType,
})
if err != nil {
h.logger.Error("failed to delete user policies", slog.Any("error", err))
continue
}
if !deleteRes.Deleted {
h.logger.Error("failed to delete user policies", slog.Any("error", svcerr.ErrAuthorization))
continue
}
if err := h.clients.Delete(ctx, u.ID); err != nil {
h.logger.Error("failed to delete user", slog.Any("error", err))
continue
}
h.logger.Info("user deleted", slog.Group("user",
slog.String("id", u.ID),
slog.String("name", u.Name),
))
}
}
}
+13
View File
@@ -26,6 +26,7 @@ const (
resetSecret = clientPrefix + "reset_secret"
sendPasswordReset = clientPrefix + "send_password_reset"
oauthCallback = clientPrefix + "oauth_callback"
deleteClient = clientPrefix + "delete"
)
var (
@@ -43,6 +44,7 @@ var (
_ events.Event = (*resetSecretEvent)(nil)
_ events.Event = (*sendPasswordResetEvent)(nil)
_ events.Event = (*oauthCallbackEvent)(nil)
_ events.Event = (*deleteClientEvent)(nil)
)
type createClientEvent struct {
@@ -386,3 +388,14 @@ func (oce oauthCallbackEvent) Encode() (map[string]interface{}, error) {
"client_id": oce.clientID,
}, nil
}
type deleteClientEvent struct {
id string
}
func (dce deleteClientEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": deleteClient,
"id": dce.id,
}, nil
}
+12
View File
@@ -312,3 +312,15 @@ func (es *eventStore) OAuthCallback(ctx context.Context, client mgclients.Client
return token, nil
}
func (es *eventStore) DeleteClient(ctx context.Context, token, id string) error {
if err := es.svc.DeleteClient(ctx, token, id); err != nil {
return err
}
event := deleteClientEvent{
id: id,
}
return es.Publish(ctx, event)
}
+18
View File
@@ -63,6 +63,24 @@ func (_m *Repository) CheckSuperAdmin(ctx context.Context, adminID string) error
return r0
}
// Delete provides a mock function with given fields: ctx, id
func (_m *Repository) Delete(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// RetrieveAll provides a mock function with given fields: ctx, pm
func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
ret := _m.Called(ctx, pm)
+18
View File
@@ -19,6 +19,24 @@ type Service struct {
mock.Mock
}
// DeleteClient provides a mock function with given fields: ctx, token, id
func (_m *Service) DeleteClient(ctx context.Context, token string, id string) error {
ret := _m.Called(ctx, token, id)
if len(ret) == 0 {
panic("no return value specified for DeleteClient")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, token, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// DisableClient provides a mock function with given fields: ctx, token, id
func (_m *Service) DisableClient(ctx context.Context, token string, id string) (clients.Client, error) {
ret := _m.Called(ctx, token, id)
+18 -2
View File
@@ -435,8 +435,10 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client
if err != nil {
return mgclients.Client{}, err
}
if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil {
return mgclients.Client{}, err
if tokenUserID != client.ID {
if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil {
return mgclients.Client{}, err
}
}
dbClient, err := svc.clients.RetrieveByID(ctx, client.ID)
if err != nil {
@@ -454,6 +456,20 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client
return client, nil
}
func (svc service) DeleteClient(ctx context.Context, token, id string) error {
client := mgclients.Client{
ID: id,
UpdatedAt: time.Now(),
Status: mgclients.DeletedStatus,
}
if _, err := svc.changeClientStatus(ctx, token, client); err != nil {
return err
}
return nil
}
func (svc service) ListMembers(ctx context.Context, token, objectKind, objectID string, pm mgclients.Page) (mgclients.MembersPage, error) {
res, err := svc.identify(ctx, token)
if err != nil {
+136 -150
View File
@@ -23,7 +23,6 @@ import (
"github.com/absmach/magistrala/users/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var (
@@ -66,7 +65,7 @@ func TestRegisterClient(t *testing.T) {
client mgclients.Client
identifyResponse *magistrala.IdentityRes
addPoliciesResponse *magistrala.AddPoliciesRes
deletePoliciesResponse *magistrala.DeletePoliciesRes
deletePoliciesResponse *magistrala.DeletePolicyRes
token string
identifyErr error
addPoliciesResponseErr error
@@ -85,7 +84,7 @@ func TestRegisterClient(t *testing.T) {
desc: "register existing client",
client: client,
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
token: validToken,
saveErr: repoerr.ErrConflict,
err: repoerr.ErrConflict,
@@ -144,7 +143,7 @@ func TestRegisterClient(t *testing.T) {
},
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
saveErr: errors.ErrMalformedEntity,
err: errors.ErrMalformedEntity,
token: validToken,
@@ -159,7 +158,7 @@ func TestRegisterClient(t *testing.T) {
},
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: nil,
},
{
@@ -172,7 +171,7 @@ func TestRegisterClient(t *testing.T) {
},
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: repoerr.ErrMalformedEntity,
},
{
@@ -186,7 +185,7 @@ func TestRegisterClient(t *testing.T) {
Status: mgclients.AllStatus,
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: svcerr.ErrInvalidStatus,
},
{
@@ -200,7 +199,7 @@ func TestRegisterClient(t *testing.T) {
Role: 2,
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: true},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: true},
err: svcerr.ErrInvalidRole,
},
{
@@ -241,7 +240,7 @@ func TestRegisterClient(t *testing.T) {
Role: mgclients.AdminRole,
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false},
deletePoliciesResponseErr: svcerr.ErrConflict,
saveErr: repoerr.ErrConflict,
err: svcerr.ErrConflict,
@@ -257,7 +256,7 @@ func TestRegisterClient(t *testing.T) {
Role: mgclients.AdminRole,
},
addPoliciesResponse: &magistrala.AddPoliciesRes{Added: true},
deletePoliciesResponse: &magistrala.DeletePoliciesRes{Deleted: false},
deletePoliciesResponse: &magistrala.DeletePolicyRes{Deleted: false},
saveErr: repoerr.ErrConflict,
err: svcerr.ErrConflict,
},
@@ -292,7 +291,7 @@ func TestRegisterClient(t *testing.T) {
identifyResponse *magistrala.IdentityRes
authorizeResponse *magistrala.AuthorizeRes
addPoliciesResponse *magistrala.AddPoliciesRes
deletePoliciesResponse *magistrala.DeletePoliciesRes
deletePoliciesResponse *magistrala.DeletePolicyRes
token string
identifyErr error
authorizeErr error
@@ -1005,7 +1004,7 @@ func TestUpdateClientRole(t *testing.T) {
membershipAuthReq *magistrala.AuthorizeReq
superAdminAuthRes *magistrala.AuthorizeRes
membershipAuthRes *magistrala.AuthorizeRes
deletePolicyFilterResponse *magistrala.DeletePolicyFilterRes
deletePolicyFilterResponse *magistrala.DeletePolicyRes
addPolicyResponse *magistrala.AddPolicyRes
updateRoleResponse mgclients.Client
token string
@@ -1095,7 +1094,7 @@ func TestUpdateClientRole(t *testing.T) {
superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true},
membershipAuthReq: membershipAuthReq,
membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: true},
updateRoleResponse: client2,
token: validToken,
err: nil,
@@ -1108,7 +1107,7 @@ func TestUpdateClientRole(t *testing.T) {
superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true},
membershipAuthReq: membershipAuthReq,
membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false},
updateRoleResponse: mgclients.Client{},
token: validToken,
deletePolicyErr: svcerr.ErrAuthorization,
@@ -1122,7 +1121,7 @@ func TestUpdateClientRole(t *testing.T) {
superAdminAuthRes: &magistrala.AuthorizeRes{Authorized: true},
membershipAuthReq: membershipAuthReq,
membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false},
updateRoleResponse: mgclients.Client{},
token: validToken,
deletePolicyErr: svcerr.ErrMalformedEntity,
@@ -1137,7 +1136,7 @@ func TestUpdateClientRole(t *testing.T) {
membershipAuthReq: membershipAuthReq,
membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true},
addPolicyResponse: &magistrala.AddPolicyRes{Added: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: true},
updateRoleResponse: mgclients.Client{},
token: validToken,
updateRoleErr: svcerr.ErrAuthentication,
@@ -1152,7 +1151,7 @@ func TestUpdateClientRole(t *testing.T) {
membershipAuthReq: membershipAuthReq,
membershipAuthRes: &magistrala.AuthorizeRes{Authorized: true},
addPolicyResponse: &magistrala.AddPolicyRes{Added: true},
deletePolicyFilterResponse: &magistrala.DeletePolicyFilterRes{Deleted: false},
deletePolicyFilterResponse: &magistrala.DeletePolicyRes{Deleted: false},
updateRoleResponse: mgclients.Client{},
token: validToken,
updateRoleErr: svcerr.ErrAuthentication,
@@ -1375,17 +1374,14 @@ func TestEnableClient(t *testing.T) {
err: svcerr.ErrAuthentication,
},
{
desc: "enable disabled client with failed to authorize",
id: disabledClient1.ID,
token: validToken,
client: disabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: disabledClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
retrieveByIDResponse: mgclients.Client{},
changeStatusResponse: mgclients.Client{},
response: mgclients.Client{},
identifyErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
desc: "enable disabled client with failed to authorize",
id: disabledClient1.ID,
token: validToken,
client: disabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: disabledClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
identifyErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "enable disabled client with normal user token",
@@ -1453,72 +1449,6 @@ func TestEnableClient(t *testing.T) {
repoCall1.Unset()
repoCall2.Unset()
}
cases2 := []struct {
desc string
status mgclients.Status
size uint64
response mgclients.ClientsPage
}{
{
desc: "list enabled clients",
status: mgclients.EnabledStatus,
size: 2,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 2,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{enabledClient1, endisabledClient1},
},
},
{
desc: "list disabled clients",
status: mgclients.DisabledStatus,
size: 1,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 1,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{disabledClient1},
},
},
{
desc: "list enabled and disabled clients",
status: mgclients.AllStatus,
size: 3,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 3,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{enabledClient1, disabledClient1, endisabledClient1},
},
},
}
for _, tc := range cases2 {
pm := mgclients.Page{
Offset: 0,
Limit: 100,
Status: tc.status,
}
authCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: client.ID}, nil)
authCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall := cRepo.On("RetrieveAll", context.Background(), mock.Anything).Return(tc.response, nil)
page, err := svc.ListClients(context.Background(), validToken, pm)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
size := uint64(len(page.Clients))
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size))
authCall.Unset()
authCall1.Unset()
repoCall.Unset()
}
}
func TestDisableClient(t *testing.T) {
@@ -1582,7 +1512,7 @@ func TestDisableClient(t *testing.T) {
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID},
identifyResponse: &magistrala.IdentityRes{UserId: validID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
checkSuperAdminErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
@@ -1643,71 +1573,128 @@ func TestDisableClient(t *testing.T) {
repoCall1.Unset()
repoCall2.Unset()
}
}
cases2 := []struct {
desc string
status mgclients.Status
size uint64
response mgclients.ClientsPage
func TestDeleteClient(t *testing.T) {
svc, cRepo, auth, _ := newService(true)
enabledClient1 := mgclients.Client{ID: testsutil.GenerateUUID(t), Credentials: mgclients.Credentials{Identity: "client1@example.com", Secret: "password"}, Status: mgclients.EnabledStatus}
deletedClient1 := mgclients.Client{ID: testsutil.GenerateUUID(t), Credentials: mgclients.Credentials{Identity: "client3@example.com", Secret: "password"}, Status: mgclients.DeletedStatus}
disenabledClient1 := enabledClient1
disenabledClient1.Status = mgclients.DeletedStatus
cases := []struct {
desc string
id string
token string
client mgclients.Client
identifyResponse *magistrala.IdentityRes
authorizeResponse *magistrala.AuthorizeRes
retrieveByIDResponse mgclients.Client
changeStatusResponse mgclients.Client
response mgclients.Client
identifyErr error
authorizeErr error
retrieveByIDErr error
changeStatusErr error
checkSuperAdminErr error
err error
}{
{
desc: "list enabled clients",
status: mgclients.EnabledStatus,
size: 1,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 1,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{enabledClient1},
},
desc: "delete enabled client",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
retrieveByIDResponse: enabledClient1,
changeStatusResponse: disenabledClient1,
response: disenabledClient1,
err: nil,
},
{
desc: "list disabled clients",
status: mgclients.DisabledStatus,
size: 2,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 2,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{disenabledClient1, disabledClient1},
},
desc: "delete enabled client with invalid token",
id: enabledClient1.ID,
token: inValidToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{},
identifyErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "list enabled and disabled clients",
status: mgclients.AllStatus,
size: 3,
response: mgclients.ClientsPage{
Page: mgclients.Page{
Total: 3,
Offset: 0,
Limit: 100,
},
Clients: []mgclients.Client{enabledClient1, disabledClient1, disenabledClient1},
},
desc: "delete enabled client with failed to authorize",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: deletedClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
err: svcerr.ErrAuthorization,
},
{
desc: "delete enabled client with normal user token",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: validID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
checkSuperAdminErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "delete enabled client with failed to retrieve client by ID",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
retrieveByIDResponse: mgclients.Client{},
retrieveByIDErr: repoerr.ErrNotFound,
err: repoerr.ErrNotFound,
},
{
desc: "delete already deleted client",
id: deletedClient1.ID,
token: validToken,
client: deletedClient1,
identifyResponse: &magistrala.IdentityRes{UserId: deletedClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
retrieveByIDResponse: deletedClient1,
err: errors.ErrStatusAlreadyAssigned,
},
{
desc: "delete enabled client with failed to change status",
id: enabledClient1.ID,
token: validToken,
client: enabledClient1,
identifyResponse: &magistrala.IdentityRes{UserId: enabledClient1.ID},
authorizeResponse: &magistrala.AuthorizeRes{Authorized: true},
retrieveByIDResponse: enabledClient1,
changeStatusResponse: mgclients.Client{},
changeStatusErr: repoerr.ErrMalformedEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases2 {
pm := mgclients.Page{
Offset: 0,
Limit: 100,
Status: tc.status,
}
authCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: client.ID}, nil)
authCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall := cRepo.On("RetrieveAll", context.Background(), mock.Anything).Return(tc.response, nil)
for _, tc := range cases {
repoCall := auth.On("Identify", context.Background(), &magistrala.IdentityReq{Token: tc.token}).Return(tc.identifyResponse, tc.identifyErr)
repoCall1 := auth.On("Authorize", context.Background(), mock.Anything).Return(tc.authorizeResponse, tc.authorizeErr)
repoCall2 := cRepo.On("CheckSuperAdmin", context.Background(), mock.Anything).Return(tc.checkSuperAdminErr)
repoCall3 := cRepo.On("RetrieveByID", context.Background(), tc.id).Return(tc.retrieveByIDResponse, tc.retrieveByIDErr)
repoCall4 := cRepo.On("ChangeStatus", context.Background(), mock.Anything).Return(tc.changeStatusResponse, tc.changeStatusErr)
page, err := svc.ListClients(context.Background(), validToken, pm)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
size := uint64(len(page.Clients))
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected size %d got %d\n", tc.desc, tc.size, size))
authCall.Unset()
authCall1.Unset()
err := svc.DeleteClient(context.Background(), tc.token, tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
if tc.err == nil {
ok := repoCall3.Parent.AssertCalled(t, "RetrieveByID", context.Background(), tc.id)
assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc))
ok = repoCall4.Parent.AssertCalled(t, "ChangeStatus", context.Background(), mock.Anything)
assert.True(t, ok, fmt.Sprintf("ChangeStatus was not called on %s", tc.desc))
}
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall3.Unset()
repoCall4.Unset()
}
}
@@ -2603,7 +2590,7 @@ func TestOAuthCallback(t *testing.T) {
addPoliciesErr error
saveResponse mgclients.Client
saveErr error
deletePoliciesResponse *magistrala.DeletePoliciesRes
deletePoliciesResponse *magistrala.DeletePolicyRes
deletePoliciesErr error
authorizeResponse *magistrala.AuthorizeRes
authorizeErr error
@@ -2728,7 +2715,6 @@ func TestOAuthCallback(t *testing.T) {
err: svcerr.ErrAuthorization,
},
}
for _, tc := range cases {
id := tc.saveResponse.ID
if tc.retrieveByIdentityResponse.ID != "" {
+8
View File
@@ -202,3 +202,11 @@ func (tm *tracingMiddleware) OAuthCallback(ctx context.Context, client mgclients
return tm.svc.OAuthCallback(ctx, client)
}
// DeleteClient traces the "DeleteClient" operation of the wrapped clients.Service.
func (tm *tracingMiddleware) DeleteClient(ctx context.Context, token, id string) error {
ctx, span := tm.tracer.Start(ctx, "svc_delete_client", trace.WithAttributes(attribute.String("id", id)))
defer span.End()
return tm.svc.DeleteClient(ctx, token, id)
}