mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 07:40:17 +00:00
MG-1987 - Enable entity endpoints to return basic info for non-admins (#2168)
Signed-off-by: WashingtonKK <washingtonkigan@gmail.com>
This commit is contained in:
committed by
Dusan Borovcanin
parent
cbb97c1d2e
commit
3e70fedb14
@@ -99,10 +99,10 @@ paths:
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/DomainRes"
|
||||
"400":
|
||||
description: Failed due to malformed query parameters.
|
||||
"401":
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: A non-existent entity request.
|
||||
"422":
|
||||
|
||||
@@ -57,6 +57,8 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: A non-existent entity request.
|
||||
"409":
|
||||
description: Failed due to using an existing identity.
|
||||
"415":
|
||||
@@ -122,6 +124,8 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: A non-existent entity request.
|
||||
"415":
|
||||
description: Missing or invalid content type.
|
||||
"422":
|
||||
@@ -207,6 +211,8 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Unauthorized access to thing id.
|
||||
"404":
|
||||
description: Missing thing.
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
/things/{thingID}/tags:
|
||||
@@ -446,6 +452,8 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
descripttion: A non-existent entity request.
|
||||
"409":
|
||||
description: Failed due to using an existing identity.
|
||||
"415":
|
||||
@@ -568,6 +576,8 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Unauthorized access to thing id.
|
||||
"404":
|
||||
descripttion: A non-existent entity request.
|
||||
"500":
|
||||
$ref: "#/components/responses/ServiceError"
|
||||
|
||||
|
||||
@@ -137,8 +137,6 @@ paths:
|
||||
description: Failed due to malformed query parameters.
|
||||
"401":
|
||||
description: Missing or invalid access token provided.
|
||||
"403":
|
||||
description: Failed to perform authorization over the entity.
|
||||
"404":
|
||||
description: A non-existent entity request.
|
||||
"422":
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ type DomainsRepository interface {
|
||||
// RetrievePermissions retrieves domain permissions.
|
||||
RetrievePermissions(ctx context.Context, subject, id string) ([]string, error)
|
||||
|
||||
// RetrieveAllByIDs retrieves for given Domain IDs .
|
||||
// RetrieveAllByIDs retrieves for given Domain IDs.
|
||||
RetrieveAllByIDs(ctx context.Context, pm Page) (DomainsPage, error)
|
||||
|
||||
// Update updates the client name and metadata.
|
||||
|
||||
+15
-11
@@ -570,21 +570,25 @@ func (svc service) CreateDomain(ctx context.Context, token string, d Domain) (do
|
||||
}
|
||||
|
||||
func (svc service) RetrieveDomain(ctx context.Context, token, id string) (Domain, error) {
|
||||
if err := svc.Authorize(ctx, PolicyReq{
|
||||
Subject: token,
|
||||
SubjectType: UserType,
|
||||
SubjectKind: TokenKind,
|
||||
Object: id,
|
||||
ObjectType: DomainType,
|
||||
Permission: ViewPermission,
|
||||
}); err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrAuthorization, err)
|
||||
res, err := svc.Identify(ctx, token)
|
||||
if err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrAuthentication, err)
|
||||
}
|
||||
dom, err := svc.domains.RetrieveByID(ctx, id)
|
||||
domain, err := svc.domains.RetrieveByID(ctx, id)
|
||||
if err != nil {
|
||||
return Domain{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
return dom, nil
|
||||
if err = svc.Authorize(ctx, PolicyReq{
|
||||
Subject: res.Subject,
|
||||
SubjectType: UserType,
|
||||
SubjectKind: UsersKind,
|
||||
Object: id,
|
||||
ObjectType: DomainType,
|
||||
Permission: MembershipPermission,
|
||||
}); err != nil {
|
||||
return Domain{ID: domain.ID, Name: domain.Name, Alias: domain.Alias}, nil
|
||||
}
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func (svc service) RetrieveDomainPermissions(ctx context.Context, token, id string) (Permissions, error) {
|
||||
|
||||
+11
-11
@@ -1161,7 +1161,6 @@ func TestAuthorize(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
fmt.Println(tc.desc)
|
||||
repoCall := prepo.On("CheckPolicy", mock.Anything, tc.checkPolicyReq3).Return(tc.checkPolicyErr)
|
||||
repoCall1 := drepo.On("RetrieveByID", mock.Anything, mock.Anything).Return(tc.retrieveDomainRes, nil)
|
||||
repoCall2 := prepo.On("CheckPolicy", mock.Anything, tc.checkAdminPolicyReq).Return(tc.checkPolicyErr1)
|
||||
@@ -1175,7 +1174,6 @@ func TestAuthorize(t *testing.T) {
|
||||
repoCall3.Unset()
|
||||
repoCall4.Unset()
|
||||
}
|
||||
fmt.Println("Cases 1 end")
|
||||
cases2 := []struct {
|
||||
desc string
|
||||
policyReq auth.PolicyReq
|
||||
@@ -1849,17 +1847,19 @@ func TestRetrieveDomain(t *testing.T) {
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "retrieve domain with empty domainID",
|
||||
token: accessToken,
|
||||
domainID: "",
|
||||
err: nil,
|
||||
desc: "retrieve domain with empty domain id",
|
||||
token: accessToken,
|
||||
domainID: "",
|
||||
err: svcerr.ErrViewEntity,
|
||||
domainRepoErr1: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve non-existing domain",
|
||||
token: accessToken,
|
||||
domainID: inValid,
|
||||
domainRepoErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrAuthorization,
|
||||
desc: "retrieve non-existing domain",
|
||||
token: accessToken,
|
||||
domainID: inValid,
|
||||
domainRepoErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrViewEntity,
|
||||
domainRepoErr1: repoerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "retrieve domain with failed to retrieve by id",
|
||||
|
||||
+66
-33
@@ -33,6 +33,7 @@ import (
|
||||
var (
|
||||
id = generateUUID(&testing.T{})
|
||||
validToken = "token"
|
||||
adminToken = "adminToken"
|
||||
validID = "d4ebb847-5d0e-4e46-bdd9-b6aceaaa3a22"
|
||||
wrongID = testsutil.GenerateUUID(&testing.T{})
|
||||
)
|
||||
@@ -379,68 +380,100 @@ func TestClient(t *testing.T) {
|
||||
Metadata: validMetadata,
|
||||
Status: mgclients.EnabledStatus.String(),
|
||||
}
|
||||
|
||||
basicUser := sdk.User{
|
||||
Name: "clientname",
|
||||
Status: mgclients.EnabledStatus.String(),
|
||||
}
|
||||
conf := sdk.Config{
|
||||
UsersURL: ts.URL,
|
||||
}
|
||||
mgsdk := sdk.NewSDK(conf)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
clientID string
|
||||
response sdk.User
|
||||
err errors.SDKError
|
||||
desc string
|
||||
token string
|
||||
clientID string
|
||||
response sdk.User
|
||||
retrieveByIDResponse sdk.User
|
||||
err errors.SDKError
|
||||
authorizeErr error
|
||||
retrieveByIDErr error
|
||||
checkSuperAdminErr errors.Error
|
||||
identifyErr errors.Error
|
||||
}{
|
||||
{
|
||||
desc: "view client successfully",
|
||||
desc: "view client successfully",
|
||||
response: basicUser,
|
||||
token: validToken,
|
||||
clientID: generateUUID(t),
|
||||
authorizeErr: svcerr.ErrAuthentication,
|
||||
checkSuperAdminErr: svcerr.ErrAuthentication,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view client successfully as admin",
|
||||
response: user,
|
||||
token: validToken,
|
||||
token: adminToken,
|
||||
clientID: generateUUID(t),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view client with an invalid token",
|
||||
response: sdk.User{},
|
||||
token: invalidToken,
|
||||
clientID: generateUUID(t),
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
|
||||
desc: "view client with an invalid token",
|
||||
response: sdk.User{},
|
||||
token: invalidToken,
|
||||
clientID: generateUUID(t),
|
||||
identifyErr: svcerr.ErrAuthentication,
|
||||
authorizeErr: svcerr.ErrAuthentication,
|
||||
checkSuperAdminErr: svcerr.ErrAuthentication,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
|
||||
retrieveByIDErr: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "view client with valid token and invalid client id",
|
||||
response: sdk.User{},
|
||||
token: validToken,
|
||||
clientID: wrongID,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
desc: "view client with valid token and invalid client id",
|
||||
response: sdk.User{},
|
||||
token: validToken,
|
||||
clientID: wrongID,
|
||||
authorizeErr: svcerr.ErrAuthentication,
|
||||
checkSuperAdminErr: svcerr.ErrAuthentication,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
retrieveByIDErr: svcerr.ErrViewEntity,
|
||||
},
|
||||
{
|
||||
desc: "view client with an invalid token and invalid client id",
|
||||
response: sdk.User{},
|
||||
token: invalidToken,
|
||||
clientID: wrongID,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
|
||||
desc: "view client with an invalid token and invalid client id",
|
||||
response: sdk.User{},
|
||||
token: invalidToken,
|
||||
identifyErr: svcerr.ErrAuthentication,
|
||||
authorizeErr: svcerr.ErrAuthentication,
|
||||
clientID: wrongID,
|
||||
checkSuperAdminErr: svcerr.ErrAuthentication,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
|
||||
retrieveByIDErr: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "view client as normal user with failed check on admin",
|
||||
response: basicUser,
|
||||
token: validToken,
|
||||
authorizeErr: svcerr.ErrAuthentication,
|
||||
checkSuperAdminErr: svcerr.ErrAuthentication,
|
||||
clientID: generateUUID(t),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{UserId: validID}, nil)
|
||||
if tc.token != validToken {
|
||||
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)
|
||||
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{UserId: validID}, tc.identifyErr)
|
||||
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, tc.authorizeErr)
|
||||
repoCall2 := crepo.On("RetrieveByID", mock.Anything, tc.clientID).Return(convertClient(tc.response), tc.err)
|
||||
superAdminCall := crepo.On("CheckSuperAdmin", mock.Anything, mock.Anything).Return(tc.checkSuperAdminErr)
|
||||
rClient, err := mgsdk.User(tc.clientID, tc.token)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err))
|
||||
tc.response.Credentials.Secret = ""
|
||||
assert.Equal(t, tc.response, rClient, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, rClient))
|
||||
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.clientID)
|
||||
assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc))
|
||||
}
|
||||
repoCall2.Unset()
|
||||
repoCall1.Unset()
|
||||
repoCall.Unset()
|
||||
superAdminCall.Unset()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-6
@@ -148,16 +148,17 @@ func (svc service) ViewClient(ctx context.Context, token, id string) (mgclients.
|
||||
return mgclients.Client{}, err
|
||||
}
|
||||
|
||||
if tokenUserID != id {
|
||||
if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil {
|
||||
return mgclients.Client{}, err
|
||||
}
|
||||
}
|
||||
|
||||
client, err := svc.clients.RetrieveByID(ctx, id)
|
||||
if err != nil {
|
||||
return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
if tokenUserID != id {
|
||||
if err := svc.checkSuperAdmin(ctx, tokenUserID); err != nil {
|
||||
return mgclients.Client{Name: client.Name, ID: client.ID}, nil
|
||||
}
|
||||
}
|
||||
|
||||
client.Credentials.Secret = ""
|
||||
|
||||
return client, nil
|
||||
@@ -202,6 +203,11 @@ func (svc service) ListClients(ctx context.Context, token string, pm mgclients.P
|
||||
if err != nil {
|
||||
return mgclients.ClientsPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
for i, c := range pg.Clients {
|
||||
pg.Clients[i] = mgclients.Client{ID: c.ID, Name: c.Name}
|
||||
}
|
||||
|
||||
return pg, nil
|
||||
}
|
||||
|
||||
@@ -498,6 +504,10 @@ func (svc service) ListMembers(ctx context.Context, token, objectKind, objectID
|
||||
return mgclients.MembersPage{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
for i, c := range cp.Clients {
|
||||
cp.Clients[i] = mgclients.Client{ID: c.ID, Name: c.Name}
|
||||
}
|
||||
|
||||
if pm.ListPerms && len(cp.Clients) > 0 {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
|
||||
+38
-26
@@ -32,14 +32,19 @@ var (
|
||||
phasher = hasher.New()
|
||||
secret = "strongsecret"
|
||||
validCMetadata = mgclients.Metadata{"role": "client"}
|
||||
clientID = testsutil.GenerateUUID(&testing.T{})
|
||||
client = mgclients.Client{
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
ID: clientID,
|
||||
Name: "clientname",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
Credentials: mgclients.Credentials{Identity: "clientidentity", Secret: secret},
|
||||
Metadata: validCMetadata,
|
||||
Status: mgclients.EnabledStatus,
|
||||
}
|
||||
basicClient = mgclients.Client{
|
||||
Name: "clientname",
|
||||
ID: clientID,
|
||||
}
|
||||
validToken = "token"
|
||||
inValidToken = "invalid"
|
||||
validID = "d4ebb847-5d0e-4e46-bdd9-b6aceaaa3a22"
|
||||
@@ -387,13 +392,15 @@ func TestViewClient(t *testing.T) {
|
||||
token: validToken,
|
||||
clientID: client.ID,
|
||||
err: nil,
|
||||
checkSuperAdminErr: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "view client with an invalid token",
|
||||
identifyResponse: &magistrala.IdentityRes{},
|
||||
response: mgclients.Client{},
|
||||
token: inValidToken,
|
||||
err: svcerr.ErrAuthentication,
|
||||
desc: "view client with an invalid token",
|
||||
identifyResponse: &magistrala.IdentityRes{},
|
||||
response: mgclients.Client{},
|
||||
token: inValidToken,
|
||||
err: svcerr.ErrAuthentication,
|
||||
checkSuperAdminErr: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "view client as normal user with failed to retrieve client",
|
||||
@@ -403,6 +410,7 @@ func TestViewClient(t *testing.T) {
|
||||
clientID: client.ID,
|
||||
retrieveByIDErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrNotFound,
|
||||
checkSuperAdminErr: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "view client as admin user successfully",
|
||||
@@ -422,21 +430,25 @@ func TestViewClient(t *testing.T) {
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
desc: "view client as admin user with invalid ID",
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: wrongID},
|
||||
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
|
||||
token: validToken,
|
||||
clientID: client.ID,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
desc: "view client as admin user with failed check on super admin",
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: adminID},
|
||||
desc: "view client as admin user with invalid ID",
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: wrongID},
|
||||
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
|
||||
token: validToken,
|
||||
clientID: client.ID,
|
||||
checkSuperAdminErr: svcerr.ErrAuthorization,
|
||||
identifyErr: svcerr.ErrAuthorization,
|
||||
err: svcerr.ErrAuthorization,
|
||||
checkSuperAdminErr: nil,
|
||||
},
|
||||
{
|
||||
desc: "view client as admin user with failed check on super admin",
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: adminID},
|
||||
authorizeResponse: &magistrala.AuthorizeRes{Authorized: false},
|
||||
token: validToken,
|
||||
retrieveByIDResponse: basicClient,
|
||||
response: basicClient,
|
||||
clientID: client.ID,
|
||||
checkSuperAdminErr: svcerr.ErrAuthorization,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -562,7 +574,7 @@ func TestListClients(t *testing.T) {
|
||||
Page: mgclients.Page{
|
||||
Total: 1,
|
||||
},
|
||||
Clients: []mgclients.Client{client},
|
||||
Clients: []mgclients.Client{basicClient},
|
||||
},
|
||||
token: validToken,
|
||||
err: nil,
|
||||
@@ -1627,7 +1639,7 @@ func TestListMembers(t *testing.T) {
|
||||
svc, cRepo, auth, _ := newService(true)
|
||||
|
||||
validPolicy := fmt.Sprintf("%s_%s", validID, client.ID)
|
||||
permissionsClient := client
|
||||
permissionsClient := basicClient
|
||||
permissionsClient.Permissions = []string{"read"}
|
||||
|
||||
cases := []struct {
|
||||
@@ -1725,7 +1737,7 @@ func TestListMembers(t *testing.T) {
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
Members: []mgclients.Client{client},
|
||||
Members: []mgclients.Client{basicClient},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
@@ -1736,7 +1748,7 @@ func TestListMembers(t *testing.T) {
|
||||
objectKind: authsvc.ThingsKind,
|
||||
objectID: validID,
|
||||
page: mgclients.Page{Offset: 0, Limit: 100, Permission: "read", ListPerms: true},
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: client.ID},
|
||||
identifyResponse: &magistrala.IdentityRes{UserId: basicClient.ID},
|
||||
authorizeReq: &magistrala.AuthorizeReq{
|
||||
SubjectType: authsvc.UserType,
|
||||
SubjectKind: authsvc.TokenKind,
|
||||
@@ -1761,7 +1773,7 @@ func TestListMembers(t *testing.T) {
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
Clients: []mgclients.Client{client},
|
||||
Clients: []mgclients.Client{basicClient},
|
||||
},
|
||||
listPermissionsResponse: &magistrala.ListPermissionsRes{Permissions: []string{"read"}},
|
||||
response: mgclients.MembersPage{
|
||||
@@ -1955,7 +1967,7 @@ func TestListMembers(t *testing.T) {
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
Clients: []mgclients.Client{client},
|
||||
Clients: []mgclients.Client{basicClient},
|
||||
},
|
||||
response: mgclients.MembersPage{
|
||||
Page: mgclients.Page{
|
||||
@@ -1963,7 +1975,7 @@ func TestListMembers(t *testing.T) {
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
Members: []mgclients.Client{client},
|
||||
Members: []mgclients.Client{basicClient},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
@@ -2020,7 +2032,7 @@ func TestListMembers(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "list members with policies successsfully of the domains kind",
|
||||
desc: "list members with policies successsfully of the groups kind",
|
||||
token: validToken,
|
||||
groupID: validID,
|
||||
objectKind: authsvc.GroupsKind,
|
||||
@@ -2059,7 +2071,7 @@ func TestListMembers(t *testing.T) {
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
},
|
||||
Members: []mgclients.Client{client},
|
||||
Members: []mgclients.Client{basicClient},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user