mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 07:14:46 +00:00
SMQ-2718 - Add actions on view entities response in groups (#2750)
Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
+1
-1
@@ -221,7 +221,7 @@ func TestGetClientssCmd(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
sdkCall := sdkMock.On("Clients", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.page, tc.sdkErr)
|
||||
sdkCall1 := sdkMock.On("Client", mock.Anything, mock.Anything, mock.Anything).Return(tc.client, tc.sdkErr)
|
||||
sdkCall1 := sdkMock.On("Client", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.client, tc.sdkErr)
|
||||
|
||||
out := executeCommand(t, rootCmd, append([]string{getCmd}, tc.args...)...)
|
||||
|
||||
|
||||
@@ -219,6 +219,15 @@ func main() {
|
||||
"",
|
||||
"Subscription contact query parameter",
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().BoolVarP(
|
||||
&sdkConf.Roles,
|
||||
"roles",
|
||||
"R",
|
||||
false,
|
||||
"Adds option to display roles for entities",
|
||||
)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -69,9 +69,16 @@ func DecodeGroupUpdate(_ context.Context, r *http.Request) (interface{}, error)
|
||||
}
|
||||
|
||||
func DecodeGroupRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := groupReq{
|
||||
id: chi.URLParam(r, "groupID"),
|
||||
roles, err := apiutil.ReadBoolQuery(r, api.RolesKey, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
req := groupReq{
|
||||
id: chi.URLParam(r, "groupID"),
|
||||
roles: roles,
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
token string
|
||||
id string
|
||||
domainID string
|
||||
roles bool
|
||||
session smqauthn.Session
|
||||
svcResp groups.Group
|
||||
svcErr error
|
||||
@@ -248,6 +249,7 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
desc: "view group successfully",
|
||||
token: validToken,
|
||||
domainID: validID,
|
||||
roles: false,
|
||||
id: validID,
|
||||
svcResp: validGroupResp,
|
||||
svcErr: nil,
|
||||
@@ -260,6 +262,7 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
token: invalidToken,
|
||||
session: smqauthn.Session{},
|
||||
domainID: validID,
|
||||
roles: false,
|
||||
id: validID,
|
||||
svcResp: validGroupResp,
|
||||
svcErr: nil,
|
||||
@@ -272,6 +275,7 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
token: "",
|
||||
session: smqauthn.Session{},
|
||||
domainID: validID,
|
||||
roles: false,
|
||||
id: validID,
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
@@ -288,6 +292,7 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
token: validToken,
|
||||
id: validID,
|
||||
domainID: validID,
|
||||
roles: false,
|
||||
svcResp: validGroupResp,
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
status: http.StatusForbidden,
|
||||
@@ -300,14 +305,14 @@ func TestViewGroupEndpoint(t *testing.T) {
|
||||
req := testRequest{
|
||||
client: gs.Client(),
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/%s/groups/%s", gs.URL, tc.domainID, tc.id),
|
||||
url: fmt.Sprintf("%s/%s/groups/%s?roles=%v", gs.URL, tc.domainID, tc.id, tc.roles),
|
||||
token: tc.token,
|
||||
}
|
||||
if tc.token == validToken {
|
||||
tc.session = smqauthn.Session{DomainUserID: validID + "_" + validID, UserID: validID, DomainID: validID}
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("ViewGroup", mock.Anything, tc.session, tc.id).Return(tc.svcResp, tc.svcErr)
|
||||
svcCall := svc.On("ViewGroup", mock.Anything, tc.session, tc.id, tc.roles).Return(tc.svcResp, tc.svcErr)
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var errRes respBody
|
||||
|
||||
@@ -48,7 +48,7 @@ func ViewGroupEndpoint(svc groups.Service) endpoint.Endpoint {
|
||||
return viewGroupRes{}, svcerr.ErrAuthentication
|
||||
}
|
||||
|
||||
group, err := svc.ViewGroup(ctx, session, req.id)
|
||||
group, err := svc.ViewGroup(ctx, session, req.id, req.roles)
|
||||
if err != nil {
|
||||
return viewGroupRes{}, err
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ func (req listGroupsReq) validate() error {
|
||||
}
|
||||
|
||||
type groupReq struct {
|
||||
id string
|
||||
id string
|
||||
roles bool
|
||||
}
|
||||
|
||||
func (req groupReq) validate() error {
|
||||
|
||||
@@ -80,8 +80,8 @@ func (es eventStore) UpdateGroup(ctx context.Context, session authn.Session, gro
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (es eventStore) ViewGroup(ctx context.Context, session authn.Session, id string) (groups.Group, error) {
|
||||
group, err := es.svc.ViewGroup(ctx, session, id)
|
||||
func (es eventStore) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (groups.Group, error) {
|
||||
group, err := es.svc.ViewGroup(ctx, session, id, withRoles)
|
||||
if err != nil {
|
||||
return group, err
|
||||
}
|
||||
|
||||
+26
-22
@@ -25,27 +25,29 @@ type Metadata map[string]interface{}
|
||||
// Path in a tree consisting of group IDs
|
||||
// Paths are unique per domain.
|
||||
type Group struct {
|
||||
ID string `json:"id"`
|
||||
Domain string `json:"domain_id,omitempty"`
|
||||
Parent string `json:"parent_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Level int `json:"level,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []*Group `json:"children,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
RoleID string `json:"role_id,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
AccessType string `json:"access_type,omitempty"`
|
||||
AccessProviderId string `json:"access_provider_id,omitempty"`
|
||||
AccessProviderRoleId string `json:"access_provider_role_id,omitempty"`
|
||||
AccessProviderRoleName string `json:"access_provider_role_name,omitempty"`
|
||||
AccessProviderRoleActions []string `json:"access_provider_role_actions,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Domain string `json:"domain_id,omitempty"`
|
||||
Parent string `json:"parent_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Level int `json:"level,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []*Group `json:"children,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
RoleID string `json:"role_id,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
AccessType string `json:"access_type,omitempty"`
|
||||
AccessProviderId string `json:"access_provider_id,omitempty"`
|
||||
AccessProviderRoleId string `json:"access_provider_role_id,omitempty"`
|
||||
AccessProviderRoleName string `json:"access_provider_role_name,omitempty"`
|
||||
AccessProviderRoleActions []string `json:"access_provider_role_actions,omitempty"`
|
||||
MemberId string `json:"member_id,omitempty"`
|
||||
Roles []roles.MemberRoleActions `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
@@ -96,6 +98,8 @@ type Repository interface {
|
||||
|
||||
RetrieveByIDAndUser(ctx context.Context, domainID, userID, groupID string) (Group, error)
|
||||
|
||||
RetrieveByIDWithRoles(ctx context.Context, groupID, memberID string) (Group, error)
|
||||
|
||||
// RetrieveAll retrieves all groups.
|
||||
RetrieveAll(ctx context.Context, pm PageMeta) (Page, error)
|
||||
|
||||
@@ -140,7 +144,7 @@ type Service interface {
|
||||
UpdateGroup(ctx context.Context, session authn.Session, g Group) (Group, error)
|
||||
|
||||
// ViewGroup retrieves data about the group identified by ID.
|
||||
ViewGroup(ctx context.Context, session authn.Session, id string) (Group, error)
|
||||
ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (Group, error)
|
||||
|
||||
// ListGroups retrieves groups for given filters.
|
||||
ListGroups(ctx context.Context, session authn.Session, pm PageMeta) (Page, error)
|
||||
|
||||
@@ -150,7 +150,7 @@ func (am *authorizationMiddleware) UpdateGroup(ctx context.Context, session auth
|
||||
return am.svc.UpdateGroup(ctx, session, g)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string) (groups.Group, error) {
|
||||
func (am *authorizationMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (groups.Group, error) {
|
||||
if session.Type == authn.PersonalAccessToken {
|
||||
if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{
|
||||
UserID: session.UserID,
|
||||
@@ -175,7 +175,7 @@ func (am *authorizationMiddleware) ViewGroup(ctx context.Context, session authn.
|
||||
return groups.Group{}, errors.Wrap(errView, err)
|
||||
}
|
||||
|
||||
return am.svc.ViewGroup(ctx, session, id)
|
||||
return am.svc.ViewGroup(ctx, session, id, withRoles)
|
||||
}
|
||||
|
||||
func (am *authorizationMiddleware) ListGroups(ctx context.Context, session authn.Session, gm groups.PageMeta) (groups.Page, error) {
|
||||
|
||||
@@ -77,7 +77,7 @@ func (lm *loggingMiddleware) UpdateGroup(ctx context.Context, session authn.Sess
|
||||
|
||||
// ViewGroup logs the view_group request. It logs the group name, id and the time it took to complete the request.
|
||||
// If the request fails, it logs the error.
|
||||
func (lm *loggingMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string) (g groups.Group, err error) {
|
||||
func (lm *loggingMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (g groups.Group, err error) {
|
||||
defer func(begin time.Time) {
|
||||
args := []any{
|
||||
slog.String("duration", time.Since(begin).String()),
|
||||
@@ -86,6 +86,7 @@ func (lm *loggingMiddleware) ViewGroup(ctx context.Context, session authn.Sessio
|
||||
slog.Group("group",
|
||||
slog.String("id", g.ID),
|
||||
slog.String("name", g.Name),
|
||||
slog.Bool("with_roles", withRoles),
|
||||
),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -95,7 +96,7 @@ func (lm *loggingMiddleware) ViewGroup(ctx context.Context, session authn.Sessio
|
||||
}
|
||||
lm.logger.Info("View group completed successfully", args...)
|
||||
}(time.Now())
|
||||
return lm.svc.ViewGroup(ctx, session, id)
|
||||
return lm.svc.ViewGroup(ctx, session, id, withRoles)
|
||||
}
|
||||
|
||||
// ListGroups logs the list_groups request. It logs the page metadata and the time it took to complete the request.
|
||||
|
||||
@@ -53,12 +53,12 @@ func (ms *metricsMiddleware) UpdateGroup(ctx context.Context, session authn.Sess
|
||||
}
|
||||
|
||||
// ViewGroup instruments ViewGroup method with metrics.
|
||||
func (ms *metricsMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string) (g groups.Group, err error) {
|
||||
func (ms *metricsMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (g groups.Group, err error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "view_group").Add(1)
|
||||
ms.latency.With("method", "view_group").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
return ms.svc.ViewGroup(ctx, session, id)
|
||||
return ms.svc.ViewGroup(ctx, session, id, withRoles)
|
||||
}
|
||||
|
||||
// ListGroups instruments ListGroups method with metrics.
|
||||
|
||||
@@ -334,6 +334,34 @@ func (_m *Repository) RetrieveByIDAndUser(ctx context.Context, domainID string,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveByIDWithRoles provides a mock function with given fields: ctx, groupID, memberID
|
||||
func (_m *Repository) RetrieveByIDWithRoles(ctx context.Context, groupID string, memberID string) (groups.Group, error) {
|
||||
ret := _m.Called(ctx, groupID, memberID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RetrieveByIDWithRoles")
|
||||
}
|
||||
|
||||
var r0 groups.Group
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) (groups.Group, error)); ok {
|
||||
return rf(ctx, groupID, memberID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) groups.Group); ok {
|
||||
r0 = rf(ctx, groupID, memberID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(groups.Group)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
||||
r1 = rf(ctx, groupID, memberID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RetrieveByIDs provides a mock function with given fields: ctx, pm, ids
|
||||
func (_m *Repository) RetrieveByIDs(ctx context.Context, pm groups.PageMeta, ids ...string) (groups.Page, error) {
|
||||
ret := _m.Called(ctx, pm, ids)
|
||||
|
||||
@@ -832,9 +832,9 @@ func (_m *Service) UpdateRoleName(ctx context.Context, session authn.Session, en
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ViewGroup provides a mock function with given fields: ctx, session, id
|
||||
func (_m *Service) ViewGroup(ctx context.Context, session authn.Session, id string) (groups.Group, error) {
|
||||
ret := _m.Called(ctx, session, id)
|
||||
// ViewGroup provides a mock function with given fields: ctx, session, id, withRoles
|
||||
func (_m *Service) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (groups.Group, error) {
|
||||
ret := _m.Called(ctx, session, id, withRoles)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ViewGroup")
|
||||
@@ -842,17 +842,17 @@ func (_m *Service) ViewGroup(ctx context.Context, session authn.Session, id stri
|
||||
|
||||
var r0 groups.Group
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) (groups.Group, error)); ok {
|
||||
return rf(ctx, session, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, bool) (groups.Group, error)); ok {
|
||||
return rf(ctx, session, id, withRoles)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string) groups.Group); ok {
|
||||
r0 = rf(ctx, session, id)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, authn.Session, string, bool) groups.Group); ok {
|
||||
r0 = rf(ctx, session, id, withRoles)
|
||||
} else {
|
||||
r0 = ret.Get(0).(groups.Group)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string) error); ok {
|
||||
r1 = rf(ctx, session, id)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, authn.Session, string, bool) error); ok {
|
||||
r1 = rf(ctx, session, id, withRoles)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
+189
-20
@@ -16,6 +16,7 @@ import (
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
"github.com/absmach/supermq/pkg/policies"
|
||||
"github.com/absmach/supermq/pkg/postgres"
|
||||
"github.com/absmach/supermq/pkg/roles"
|
||||
rolesPostgres "github.com/absmach/supermq/pkg/roles/repo/postgres"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
@@ -165,6 +166,164 @@ func (repo groupRepository) RetrieveByID(ctx context.Context, id string) (groups
|
||||
return toGroup(dbg)
|
||||
}
|
||||
|
||||
func (repo groupRepository) RetrieveByIDWithRoles(ctx context.Context, id, memberID string) (groups.Group, error) {
|
||||
query := `
|
||||
WITH selected_group AS (
|
||||
SELECT
|
||||
g.id,
|
||||
g.parent_id,
|
||||
g.domain_id,
|
||||
g.path AS parent_group_path
|
||||
FROM
|
||||
groups g
|
||||
WHERE
|
||||
g.id = :id
|
||||
LIMIT 1
|
||||
),
|
||||
selected_group_roles AS (
|
||||
SELECT
|
||||
sg.id AS group_id,
|
||||
grm.member_id AS member_id,
|
||||
gr.id AS role_id,
|
||||
gr.name AS role_name,
|
||||
jsonb_agg(DISTINCT gra.action) AS actions,
|
||||
g.path AS access_provider_path,
|
||||
gr.entity_id AS access_provider_id,
|
||||
CASE
|
||||
WHEN gr.entity_id = sg.id THEN 'direct'
|
||||
WHEN gr.entity_id = sg.parent_id THEN 'direct_group'
|
||||
ELSE 'indirect_group'
|
||||
END AS access_type
|
||||
FROM
|
||||
groups g
|
||||
JOIN
|
||||
groups_roles gr ON gr.entity_id = g.id
|
||||
JOIN
|
||||
groups_role_members grm ON gr.id = grm.role_id
|
||||
JOIN
|
||||
groups_role_actions gra ON gr.id = gra.role_id
|
||||
JOIN
|
||||
selected_group sg ON g.path @> sg.parent_group_path
|
||||
WHERE
|
||||
grm.member_id = :member_id
|
||||
AND (
|
||||
(gr.entity_id = sg.id)
|
||||
OR (gr.entity_id <> sg.id AND gra.action LIKE 'subgroup%%')
|
||||
)
|
||||
GROUP BY
|
||||
sg.id, gr.entity_id, gr.id, gr.name, g.path, grm.member_id, sg.parent_id
|
||||
),
|
||||
selected_domain_roles AS (
|
||||
SELECT
|
||||
sg.id AS group_id,
|
||||
drm.member_id AS member_id,
|
||||
dr.id AS role_id,
|
||||
dr.name AS role_name,
|
||||
jsonb_agg(DISTINCT all_actions.action) AS actions,
|
||||
''::::ltree access_provider_path,
|
||||
'domain' AS access_type,
|
||||
dr.entity_id AS access_provider_id
|
||||
FROM
|
||||
domains d
|
||||
JOIN
|
||||
selected_group sg ON sg.domain_id = d.id
|
||||
JOIN
|
||||
domains_roles dr ON dr.entity_id = d.id
|
||||
JOIN
|
||||
domains_role_members drm ON dr.id = drm.role_id
|
||||
JOIN
|
||||
domains_role_actions dra ON dr.id = dra.role_id
|
||||
JOIN
|
||||
domains_role_actions all_actions ON dr.id = all_actions.role_id
|
||||
WHERE
|
||||
drm.member_id = :member_id
|
||||
AND dra.action LIKE 'group%%'
|
||||
GROUP BY
|
||||
sg.id, dr.entity_id, dr.id, dr.name, drm.member_id
|
||||
),
|
||||
all_roles AS (
|
||||
SELECT
|
||||
sgr.group_id,
|
||||
sgr.member_id,
|
||||
sgr.role_id AS role_id,
|
||||
sgr.role_name AS role_name,
|
||||
sgr.actions AS actions,
|
||||
sgr.access_type AS access_type,
|
||||
sgr.access_provider_path AS access_provider_path,
|
||||
sgr.access_provider_id AS access_provider_id
|
||||
FROM
|
||||
selected_group_roles sgr
|
||||
UNION
|
||||
SELECT
|
||||
sdr.group_id,
|
||||
sdr.member_id,
|
||||
sdr.role_id AS role_id,
|
||||
sdr.role_name AS role_name,
|
||||
sdr.actions AS actions,
|
||||
sdr.access_type AS access_type,
|
||||
sdr.access_provider_path AS access_provider_path,
|
||||
sdr.access_provider_id AS access_provider_id
|
||||
FROM
|
||||
selected_domain_roles sdr
|
||||
),
|
||||
final_roles AS (
|
||||
SELECT
|
||||
ar.group_id,
|
||||
ar.member_id,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'role_id', ar.role_id,
|
||||
'role_name', ar.role_name,
|
||||
'actions', ar.actions,
|
||||
'access_type', ar.access_type,
|
||||
'access_provider_path', ar.access_provider_path,
|
||||
'access_provider_id', ar.access_provider_id
|
||||
)
|
||||
) AS roles
|
||||
FROM all_roles ar
|
||||
GROUP BY
|
||||
ar.group_id, ar.member_id
|
||||
)
|
||||
SELECT
|
||||
g.id,
|
||||
g.parent_id,
|
||||
g.domain_id,
|
||||
g.name,
|
||||
g.description,
|
||||
g.path,
|
||||
g.metadata,
|
||||
g.created_at,
|
||||
g.updated_at,
|
||||
g.updated_by,
|
||||
g.status,
|
||||
fr.member_id,
|
||||
fr.roles
|
||||
FROM groups g
|
||||
JOIN final_roles fr ON fr.group_id = g.id
|
||||
`
|
||||
|
||||
parameters := map[string]interface{}{
|
||||
"id": id,
|
||||
"member_id": memberID,
|
||||
}
|
||||
row, err := repo.db.NamedQueryContext(ctx, query, parameters)
|
||||
if err != nil {
|
||||
return groups.Group{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
dbg := dbGroup{}
|
||||
if !row.Next() {
|
||||
return groups.Group{}, errors.Wrap(repoerr.ErrNotFound, err)
|
||||
}
|
||||
|
||||
if err := row.StructScan(&dbg); err != nil {
|
||||
return groups.Group{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
return toGroup(dbg)
|
||||
}
|
||||
|
||||
func (repo groupRepository) RetrieveByIDAndUser(ctx context.Context, domainID, userID, groupID string) (groups.Group, error) {
|
||||
baseQuery := repo.userGroupsBaseQuery(domainID, userID)
|
||||
|
||||
@@ -934,26 +1093,28 @@ func buildQuery(gm groups.PageMeta, ids ...string) string {
|
||||
}
|
||||
|
||||
type dbGroup struct {
|
||||
ID string `db:"id"`
|
||||
ParentID *string `db:"parent_id,omitempty"`
|
||||
DomainID string `db:"domain_id,omitempty"`
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description,omitempty"`
|
||||
Level int `db:"level"`
|
||||
Path string `db:"path,omitempty"`
|
||||
Metadata []byte `db:"metadata,omitempty"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt sql.NullTime `db:"updated_at,omitempty"`
|
||||
UpdatedBy *string `db:"updated_by,omitempty"`
|
||||
Status groups.Status `db:"status"`
|
||||
RoleID string `db:"role_id"`
|
||||
RoleName string `db:"role_name"`
|
||||
Actions pq.StringArray `db:"actions"`
|
||||
AccessType string `db:"access_type"`
|
||||
AccessProviderId string `db:"access_provider_id"`
|
||||
AccessProviderRoleId string `db:"access_provider_role_id"`
|
||||
AccessProviderRoleName string `db:"access_provider_role_name"`
|
||||
AccessProviderRoleActions pq.StringArray `db:"access_provider_role_actions"`
|
||||
ID string `db:"id"`
|
||||
ParentID *string `db:"parent_id,omitempty"`
|
||||
DomainID string `db:"domain_id,omitempty"`
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description,omitempty"`
|
||||
Level int `db:"level"`
|
||||
Path string `db:"path,omitempty"`
|
||||
Metadata []byte `db:"metadata,omitempty"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt sql.NullTime `db:"updated_at,omitempty"`
|
||||
UpdatedBy *string `db:"updated_by,omitempty"`
|
||||
Status groups.Status `db:"status"`
|
||||
RoleID string `db:"role_id"`
|
||||
RoleName string `db:"role_name"`
|
||||
Actions pq.StringArray `db:"actions"`
|
||||
AccessType string `db:"access_type"`
|
||||
AccessProviderId string `db:"access_provider_id"`
|
||||
AccessProviderRoleId string `db:"access_provider_role_id"`
|
||||
AccessProviderRoleName string `db:"access_provider_role_name"`
|
||||
AccessProviderRoleActions pq.StringArray `db:"access_provider_role_actions"`
|
||||
MemberID string `db:"member_id,omitempty"`
|
||||
Roles json.RawMessage `db:"roles,omitempty"`
|
||||
}
|
||||
|
||||
func toDBGroup(g groups.Group) (dbGroup, error) {
|
||||
@@ -1012,6 +1173,13 @@ func toGroup(g dbGroup) (groups.Group, error) {
|
||||
updatedBy = *g.UpdatedBy
|
||||
}
|
||||
|
||||
var roles []roles.MemberRoleActions
|
||||
if g.Roles != nil {
|
||||
if err := json.Unmarshal(g.Roles, &roles); err != nil {
|
||||
return groups.Group{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
return groups.Group{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
@@ -1033,6 +1201,7 @@ func toGroup(g dbGroup) (groups.Group, error) {
|
||||
AccessProviderRoleId: g.AccessProviderRoleId,
|
||||
AccessProviderRoleName: g.AccessProviderRoleName,
|
||||
AccessProviderRoleActions: g.AccessProviderRoleActions,
|
||||
Roles: roles,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -105,8 +105,15 @@ func (svc service) CreateGroup(ctx context.Context, session smqauthn.Session, g
|
||||
return saved, nrps, nil
|
||||
}
|
||||
|
||||
func (svc service) ViewGroup(ctx context.Context, session smqauthn.Session, id string) (Group, error) {
|
||||
group, err := svc.repo.RetrieveByIDAndUser(ctx, session.DomainID, session.UserID, id)
|
||||
func (svc service) ViewGroup(ctx context.Context, session smqauthn.Session, id string, withRoles bool) (Group, error) {
|
||||
var group Group
|
||||
var err error
|
||||
switch withRoles {
|
||||
case true:
|
||||
group, err = svc.repo.RetrieveByIDWithRoles(ctx, id, session.UserID)
|
||||
default:
|
||||
group, err = svc.repo.RetrieveByID(ctx, id)
|
||||
}
|
||||
if err != nil {
|
||||
return Group{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
+55
-20
@@ -43,6 +43,22 @@ var (
|
||||
},
|
||||
Status: groups.EnabledStatus,
|
||||
}
|
||||
validGroupWithRoles = groups.Group{
|
||||
ID: testsutil.GenerateUUID(&testing.T{}),
|
||||
Name: namegen.Generate(),
|
||||
Description: namegen.Generate(),
|
||||
Metadata: map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Status: groups.EnabledStatus,
|
||||
Roles: []roles.MemberRoleActions{
|
||||
{
|
||||
RoleID: "test-id",
|
||||
RoleName: "test-name",
|
||||
AccessType: "direct",
|
||||
},
|
||||
},
|
||||
}
|
||||
parentGroupID = testsutil.GenerateUUID(&testing.T{})
|
||||
childGroupID = testsutil.GenerateUUID(&testing.T{})
|
||||
childGroup = groups.Group{
|
||||
@@ -226,39 +242,58 @@ func TestViewGroup(t *testing.T) {
|
||||
svc := newService(t)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
session smqauthn.Session
|
||||
id string
|
||||
repoResp groups.Group
|
||||
repoErr error
|
||||
err error
|
||||
desc string
|
||||
session smqauthn.Session
|
||||
id string
|
||||
withRoles bool
|
||||
repoResp groups.Group
|
||||
repoErr error
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "view group successfully",
|
||||
id: validGroup.ID,
|
||||
session: validSession,
|
||||
repoResp: validGroup,
|
||||
desc: "view group successfully",
|
||||
id: validGroup.ID,
|
||||
session: validSession,
|
||||
withRoles: false,
|
||||
repoResp: validGroup,
|
||||
},
|
||||
{
|
||||
desc: "view group with failed to retrieve",
|
||||
id: testsutil.GenerateUUID(t),
|
||||
session: validSession,
|
||||
repoErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrViewEntity,
|
||||
desc: "view group successfully with roles",
|
||||
id: validGroupWithRoles.ID,
|
||||
session: validSession,
|
||||
withRoles: true,
|
||||
repoResp: validGroupWithRoles,
|
||||
},
|
||||
{
|
||||
desc: "view group with failed to retrieve",
|
||||
id: testsutil.GenerateUUID(t),
|
||||
session: validSession,
|
||||
withRoles: false,
|
||||
repoErr: repoerr.ErrNotFound,
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
repoCall := repo.On("RetrieveByIDAndUser", context.Background(), tc.session.DomainID, tc.session.UserID, tc.id).Return(tc.repoResp, tc.repoErr)
|
||||
got, err := svc.ViewGroup(context.Background(), validSession, tc.id)
|
||||
repoCall := repo.On("RetrieveByID", context.Background(), tc.id).Return(tc.repoResp, tc.repoErr)
|
||||
repoCall1 := repo.On("RetrieveByIDWithRoles", context.Background(), tc.id, tc.session.UserID).Return(tc.repoResp, tc.repoErr)
|
||||
got, err := svc.ViewGroup(context.Background(), validSession, tc.id, tc.withRoles)
|
||||
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("expected error %v to contain %v", err, tc.err))
|
||||
if err == nil {
|
||||
assert.Equal(t, tc.repoResp, got)
|
||||
ok := repo.AssertCalled(t, "RetrieveByIDAndUser", context.Background(), tc.session.DomainID, tc.session.UserID, tc.id)
|
||||
assert.True(t, ok, fmt.Sprintf("RetrieveByIDAndUser was not called on %s", tc.desc))
|
||||
switch tc.withRoles {
|
||||
case true:
|
||||
assert.Equal(t, tc.repoResp, got)
|
||||
ok := repo.AssertCalled(t, "RetrieveByIDWithRoles", context.Background(), tc.id, tc.session.UserID)
|
||||
assert.True(t, ok, fmt.Sprintf("RetrieveByIDWithRoles was not called on %s", tc.desc))
|
||||
default:
|
||||
assert.Equal(t, tc.repoResp, got)
|
||||
ok := repo.AssertCalled(t, "RetrieveByID", context.Background(), tc.id)
|
||||
assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc))
|
||||
}
|
||||
}
|
||||
repoCall.Unset()
|
||||
repoCall1.Unset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,11 @@ func (tm *tracingMiddleware) CreateGroup(ctx context.Context, session authn.Sess
|
||||
}
|
||||
|
||||
// ViewGroup traces the "ViewGroup" operation of the wrapped groups.Service.
|
||||
func (tm *tracingMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string) (groups.Group, error) {
|
||||
ctx, span := tracing.StartSpan(ctx, tm.tracer, "svc_view_group", trace.WithAttributes(attribute.String("id", id)))
|
||||
func (tm *tracingMiddleware) ViewGroup(ctx context.Context, session authn.Session, id string, withRoles bool) (groups.Group, error) {
|
||||
ctx, span := tracing.StartSpan(ctx, tm.tracer, "svc_view_group", trace.WithAttributes(attribute.String("id", id), attribute.Bool("with_roles", withRoles)))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.ViewGroup(ctx, session, id)
|
||||
return tm.svc.ViewGroup(ctx, session, id, withRoles)
|
||||
}
|
||||
|
||||
// ListGroups traces the "ListGroups" operation of the wrapped groups.Service.
|
||||
|
||||
+13
-11
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/roles"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,17 +21,18 @@ const (
|
||||
|
||||
// Channel represents supermq channel.
|
||||
type Channel struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
ParentGroup string `json:"parent_group_id,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
ParentGroup string `json:"parent_group_id,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
Roles []roles.MemberRoleActions `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
func (sdk mgSDK) CreateChannel(c Channel, domainID, token string) (Channel, errors.SDKError) {
|
||||
|
||||
@@ -627,11 +627,18 @@ func TestViewChannel(t *testing.T) {
|
||||
}
|
||||
mgsdk := sdk.NewSDK(conf)
|
||||
|
||||
channelResRoles := sdk.Config{
|
||||
ChannelsURL: ts.URL,
|
||||
Roles: true,
|
||||
}
|
||||
mgsdkRoles := sdk.NewSDK(channelResRoles)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
token string
|
||||
session smqauthn.Session
|
||||
withRoles bool
|
||||
channelID string
|
||||
svcRes channels.Channel
|
||||
svcErr error
|
||||
@@ -643,6 +650,18 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
channelID: channelRes.ID,
|
||||
svcRes: channelRes,
|
||||
svcErr: nil,
|
||||
response: channel,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view channel successfully with roles",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: true,
|
||||
channelID: channelRes.ID,
|
||||
svcRes: channelRes,
|
||||
svcErr: nil,
|
||||
@@ -653,6 +672,7 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel with invalid token",
|
||||
domainID: domainID,
|
||||
token: invalidToken,
|
||||
withRoles: false,
|
||||
channelID: channelRes.ID,
|
||||
svcRes: channels.Channel{},
|
||||
authenticateErr: svcerr.ErrAuthentication,
|
||||
@@ -663,6 +683,7 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
withRoles: false,
|
||||
channelID: channelRes.ID,
|
||||
svcRes: channels.Channel{},
|
||||
svcErr: nil,
|
||||
@@ -673,6 +694,7 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel for wrong id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
channelID: wrongID,
|
||||
svcRes: channels.Channel{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
@@ -683,6 +705,7 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel with empty channel id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
channelID: "",
|
||||
svcRes: channels.Channel{},
|
||||
svcErr: nil,
|
||||
@@ -693,6 +716,7 @@ func TestViewChannel(t *testing.T) {
|
||||
desc: "view channel with service response that can't be unmarshalled",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
channelID: channelRes.ID,
|
||||
svcRes: channels.Channel{
|
||||
ID: generateUUID(t),
|
||||
@@ -712,12 +736,25 @@ func TestViewChannel(t *testing.T) {
|
||||
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
|
||||
}
|
||||
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
|
||||
svcCall := gsvc.On("ViewChannel", mock.Anything, tc.session, tc.channelID, false).Return(tc.svcRes, tc.svcErr)
|
||||
resp, err := mgsdk.Channel(tc.channelID, tc.domainID, tc.token)
|
||||
svcCall := gsvc.On("ViewChannel", mock.Anything, tc.session, tc.channelID, tc.withRoles).Return(tc.svcRes, tc.svcErr)
|
||||
|
||||
var resp sdk.Channel
|
||||
var err error
|
||||
|
||||
switch tc.withRoles {
|
||||
case true:
|
||||
resp, err = mgsdkRoles.Channel(tc.channelID, tc.domainID, tc.token)
|
||||
default:
|
||||
resp, err = mgsdk.Channel(tc.channelID, tc.domainID, tc.token)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.response, resp)
|
||||
if tc.withRoles {
|
||||
assert.Equal(t, resp.Roles, validRoles, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, validRoles, resp.Roles))
|
||||
}
|
||||
if tc.err == nil {
|
||||
ok := svcCall.Parent.AssertCalled(t, "ViewChannel", mock.Anything, tc.session, tc.channelID, false)
|
||||
ok := svcCall.Parent.AssertCalled(t, "ViewChannel", mock.Anything, tc.session, tc.channelID, tc.withRoles)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
svcCall.Unset()
|
||||
@@ -2083,6 +2120,7 @@ func generateTestChannel(t *testing.T) sdk.Channel {
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Status: channels.EnabledStatus.String(),
|
||||
Roles: validRoles,
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
+14
-12
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/roles"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,18 +26,19 @@ const (
|
||||
|
||||
// Client represents supermq client.
|
||||
type Client struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
ParentGroup string `json:"parent_group_id,omitempty"`
|
||||
Credentials ClientCredentials `json:"credentials"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
ParentGroup string `json:"parent_group_id,omitempty"`
|
||||
Credentials ClientCredentials `json:"credentials"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
Roles []roles.MemberRoleActions `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
type ClientCredentials struct {
|
||||
|
||||
+91
-50
@@ -45,7 +45,7 @@ func TestCreateClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
client := generateTestClient(t)
|
||||
client := generateTestClient(t, false)
|
||||
createClientReq := sdk.Client{
|
||||
Name: client.Name,
|
||||
Tags: client.Tags,
|
||||
@@ -213,7 +213,7 @@ func TestCreateClients(t *testing.T) {
|
||||
|
||||
sdkClients := []sdk.Client{}
|
||||
for i := 0; i < 3; i++ {
|
||||
client := generateTestClient(t)
|
||||
client := generateTestClient(t, false)
|
||||
sdkClients = append(sdkClients, client)
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ func TestListClients(t *testing.T) {
|
||||
|
||||
var sdkClients []sdk.Client
|
||||
for i := 10; i < 100; i++ {
|
||||
c := generateTestClient(t)
|
||||
c := generateTestClient(t, false)
|
||||
if i == 50 {
|
||||
c.Status = clients.DisabledStatus.String()
|
||||
c.Tags = []string{"tag1", "tag2"}
|
||||
@@ -589,17 +589,25 @@ func TestViewClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
sdkClient := generateTestClient(t)
|
||||
sdkClient := generateTestClient(t, false)
|
||||
sdkClientWithRoles := generateTestClient(t, true)
|
||||
conf := sdk.Config{
|
||||
ClientsURL: ts.URL,
|
||||
}
|
||||
mgsdk := sdk.NewSDK(conf)
|
||||
|
||||
confRoles := sdk.Config{
|
||||
ClientsURL: ts.URL,
|
||||
Roles: true,
|
||||
}
|
||||
mgsdkRoles := sdk.NewSDK(confRoles)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
token string
|
||||
session smqauthn.Session
|
||||
withRoles bool
|
||||
clientID string
|
||||
svcRes clients.Client
|
||||
svcErr error
|
||||
@@ -608,19 +616,32 @@ func TestViewClient(t *testing.T) {
|
||||
err errors.SDKError
|
||||
}{
|
||||
{
|
||||
desc: "view client successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: convertClient(sdkClient),
|
||||
svcErr: nil,
|
||||
response: sdkClient,
|
||||
err: nil,
|
||||
desc: "view client successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: convertClient(sdkClient),
|
||||
svcErr: nil,
|
||||
response: sdkClient,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view client successfully with roles",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: true,
|
||||
clientID: sdkClientWithRoles.ID,
|
||||
svcRes: convertClient(sdkClientWithRoles),
|
||||
svcErr: nil,
|
||||
response: sdkClientWithRoles,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view client with an invalid token",
|
||||
domainID: domainID,
|
||||
token: invalidToken,
|
||||
withRoles: false,
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: clients.Client{},
|
||||
authenticateErr: svcerr.ErrAuthorization,
|
||||
@@ -628,40 +649,44 @@ func TestViewClient(t *testing.T) {
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
|
||||
},
|
||||
{
|
||||
desc: "view client with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: clients.Client{},
|
||||
svcErr: nil,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
desc: "view client with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
withRoles: false,
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: clients.Client{},
|
||||
svcErr: nil,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "view client with an invalid client id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
clientID: wrongID,
|
||||
svcRes: clients.Client{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
desc: "view client with an invalid client id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
clientID: wrongID,
|
||||
svcRes: clients.Client{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "view client with empty client id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
clientID: "",
|
||||
svcRes: clients.Client{},
|
||||
svcErr: nil,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKError(apiutil.ErrMissingID),
|
||||
desc: "view client with empty client id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
clientID: "",
|
||||
svcRes: clients.Client{},
|
||||
svcErr: nil,
|
||||
response: sdk.Client{},
|
||||
err: errors.NewSDKError(apiutil.ErrMissingID),
|
||||
},
|
||||
{
|
||||
desc: "view client with response that can't be unmarshalled",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
clientID: sdkClient.ID,
|
||||
desc: "view client with response that can't be unmarshalled",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
clientID: sdkClient.ID,
|
||||
svcRes: clients.Client{
|
||||
Name: sdkClient.Name,
|
||||
Tags: sdkClient.Tags,
|
||||
@@ -681,12 +706,23 @@ func TestViewClient(t *testing.T) {
|
||||
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
|
||||
}
|
||||
authCall := auth.On("Authenticate", mock.Anything, mock.Anything).Return(tc.session, tc.authenticateErr)
|
||||
svcCall := tsvc.On("View", mock.Anything, tc.session, tc.clientID, false).Return(tc.svcRes, tc.svcErr)
|
||||
resp, err := mgsdk.Client(tc.clientID, tc.domainID, tc.token)
|
||||
svcCall := tsvc.On("View", mock.Anything, tc.session, tc.clientID, tc.withRoles).Return(tc.svcRes, tc.svcErr)
|
||||
|
||||
var resp sdk.Client
|
||||
var err error
|
||||
switch tc.withRoles {
|
||||
case true:
|
||||
resp, err = mgsdkRoles.Client(tc.clientID, tc.domainID, tc.token)
|
||||
default:
|
||||
resp, err = mgsdk.Client(tc.clientID, tc.domainID, tc.token)
|
||||
}
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.response, resp)
|
||||
if tc.withRoles {
|
||||
assert.Equal(t, resp.Roles, validRoles, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, validRoles, resp.Roles))
|
||||
}
|
||||
if tc.err == nil {
|
||||
ok := svcCall.Parent.AssertCalled(t, "View", mock.Anything, tc.session, tc.clientID, false)
|
||||
ok := svcCall.Parent.AssertCalled(t, "View", mock.Anything, tc.session, tc.clientID, tc.withRoles)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
svcCall.Unset()
|
||||
@@ -699,7 +735,7 @@ func TestUpdateClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
sdkClient := generateTestClient(t)
|
||||
sdkClient := generateTestClient(t, false)
|
||||
updatedClient := sdkClient
|
||||
updatedClient.Name = "newName"
|
||||
updatedClient.Metadata = map[string]interface{}{
|
||||
@@ -857,7 +893,7 @@ func TestUpdateClientTags(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
sdkClient := generateTestClient(t)
|
||||
sdkClient := generateTestClient(t, false)
|
||||
updatedClient := sdkClient
|
||||
updatedClient.Tags = []string{"newTag1", "newTag2"}
|
||||
updateClientReq := sdk.Client{
|
||||
@@ -1009,7 +1045,7 @@ func TestUpdateClientSecret(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
sdkClient := generateTestClient(t)
|
||||
sdkClient := generateTestClient(t, false)
|
||||
newSecret := generateUUID(t)
|
||||
updatedClient := sdkClient
|
||||
updatedClient.Credentials.Secret = newSecret
|
||||
@@ -1141,7 +1177,7 @@ func TestEnableClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
client := generateTestClient(t)
|
||||
client := generateTestClient(t, false)
|
||||
enabledClient := client
|
||||
enabledClient.Status = clients.EnabledStatus.String()
|
||||
|
||||
@@ -1244,7 +1280,7 @@ func TestDisableClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
client := generateTestClient(t)
|
||||
client := generateTestClient(t, false)
|
||||
disabledClient := client
|
||||
disabledClient.Status = clients.DisabledStatus.String()
|
||||
|
||||
@@ -1347,7 +1383,7 @@ func TestDeleteClient(t *testing.T) {
|
||||
ts, tsvc, auth := setupClients()
|
||||
defer ts.Close()
|
||||
|
||||
client := generateTestClient(t)
|
||||
client := generateTestClient(t, false)
|
||||
|
||||
conf := sdk.Config{
|
||||
ClientsURL: ts.URL,
|
||||
@@ -3213,10 +3249,14 @@ func TestListAvailableClientRoleActions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestClient(t *testing.T) sdk.Client {
|
||||
func generateTestClient(t *testing.T, withRoles bool) sdk.Client {
|
||||
createdAt, err := time.Parse(time.RFC3339, "2023-03-03T00:00:00Z")
|
||||
assert.Nil(t, err, fmt.Sprintf("unexpected error %s", err))
|
||||
updatedAt := createdAt
|
||||
var rl []roles.MemberRoleActions
|
||||
if withRoles {
|
||||
rl = validRoles
|
||||
}
|
||||
return sdk.Client{
|
||||
ID: testsutil.GenerateUUID(t),
|
||||
Name: "clientname",
|
||||
@@ -3229,5 +3269,6 @@ func generateTestClient(t *testing.T) sdk.Client {
|
||||
Status: clients.EnabledStatus.String(),
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Roles: rl,
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -345,6 +345,12 @@ func TestViewDomain(t *testing.T) {
|
||||
|
||||
mgsdk := sdk.NewSDK(sdkConf)
|
||||
|
||||
sdkConfRoles := sdk.Config{
|
||||
DomainsURL: ds.URL,
|
||||
Roles: true,
|
||||
}
|
||||
mgsdkRoles := sdk.NewSDK(sdkConfRoles)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
@@ -371,7 +377,7 @@ func TestViewDomain(t *testing.T) {
|
||||
desc: "view domain successfully with roles",
|
||||
token: validToken,
|
||||
domainID: sdkDomain.ID,
|
||||
withRoles: false,
|
||||
withRoles: true,
|
||||
svcRes: authDomain,
|
||||
svcErr: nil,
|
||||
response: sdkDomain,
|
||||
@@ -441,7 +447,16 @@ func TestViewDomain(t *testing.T) {
|
||||
}
|
||||
authCall := authn.On("Authenticate", mock.Anything, mock.Anything).Return(tc.session, tc.authnErr)
|
||||
svcCall := svc.On("RetrieveDomain", mock.Anything, tc.session, tc.domainID, tc.withRoles).Return(tc.svcRes, tc.svcErr)
|
||||
resp, err := mgsdk.Domain(tc.domainID, tc.token)
|
||||
|
||||
var resp sdk.Domain
|
||||
var err error
|
||||
|
||||
switch tc.withRoles {
|
||||
case true:
|
||||
resp, err = mgsdkRoles.Domain(tc.domainID, tc.token)
|
||||
default:
|
||||
resp, err = mgsdk.Domain(tc.domainID, tc.token)
|
||||
}
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.response, resp)
|
||||
if tc.withRoles {
|
||||
@@ -2332,6 +2347,7 @@ func generateTestDomain(t *testing.T) (domains.Domain, sdk.Domain) {
|
||||
CreatedAt: createdAt,
|
||||
UpdatedBy: ownerID,
|
||||
UpdatedAt: createdAt,
|
||||
Roles: validRoles,
|
||||
}
|
||||
|
||||
sd := sdk.Domain{
|
||||
@@ -2345,6 +2361,7 @@ func generateTestDomain(t *testing.T) (domains.Domain, sdk.Domain) {
|
||||
CreatedAt: ad.CreatedAt,
|
||||
UpdatedBy: ad.UpdatedBy,
|
||||
UpdatedAt: ad.UpdatedAt,
|
||||
Roles: ad.Roles,
|
||||
}
|
||||
return ad, sd
|
||||
}
|
||||
|
||||
+23
-21
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/roles"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,27 +26,28 @@ const (
|
||||
// Path in a tree consisting of group IDs
|
||||
// Paths are unique per owner.
|
||||
type Group struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Level int `json:"level,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []*Group `json:"children,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
RoleID string `json:"role_id,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
AccessType string `json:"access_type,omitempty"`
|
||||
AccessProviderId string `json:"access_provider_id,omitempty"`
|
||||
AccessProviderRoleId string `json:"access_provider_role_id,omitempty"`
|
||||
AccessProviderRoleName string `json:"access_provider_role_name,omitempty"`
|
||||
AccessProviderRoleActions []string `json:"access_provider_role_actions,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Level int `json:"level,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []*Group `json:"children,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
UpdatedBy string `json:"updated_by,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
RoleID string `json:"role_id,omitempty"`
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
AccessType string `json:"access_type,omitempty"`
|
||||
AccessProviderId string `json:"access_provider_id,omitempty"`
|
||||
AccessProviderRoleId string `json:"access_provider_role_id,omitempty"`
|
||||
AccessProviderRoleName string `json:"access_provider_role_name,omitempty"`
|
||||
AccessProviderRoleActions []string `json:"access_provider_role_actions,omitempty"`
|
||||
Roles []roles.MemberRoleActions `json:"roles,omitempty"`
|
||||
}
|
||||
|
||||
func (sdk mgSDK) CreateGroup(g Group, domainID, token string) (Group, errors.SDKError) {
|
||||
|
||||
+77
-39
@@ -523,11 +523,18 @@ func TestViewGroup(t *testing.T) {
|
||||
}
|
||||
mgsdk := sdk.NewSDK(conf)
|
||||
|
||||
confRoles := sdk.Config{
|
||||
GroupsURL: ts.URL,
|
||||
Roles: true,
|
||||
}
|
||||
mgsdkRoles := sdk.NewSDK(confRoles)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
domainID string
|
||||
token string
|
||||
session smqauthn.Session
|
||||
withRoles bool
|
||||
groupID string
|
||||
svcRes groups.Group
|
||||
svcErr error
|
||||
@@ -536,19 +543,32 @@ func TestViewGroup(t *testing.T) {
|
||||
err errors.SDKError
|
||||
}{
|
||||
{
|
||||
desc: "view group successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
groupID: group.ID,
|
||||
svcRes: group,
|
||||
svcErr: nil,
|
||||
response: sdkGroup,
|
||||
err: nil,
|
||||
desc: "view group successfully",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
groupID: group.ID,
|
||||
svcRes: group,
|
||||
svcErr: nil,
|
||||
response: sdkGroup,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view group successfully with roles",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: true,
|
||||
groupID: group.ID,
|
||||
svcRes: group,
|
||||
svcErr: nil,
|
||||
response: sdkGroup,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "view group with invalid token",
|
||||
domainID: domainID,
|
||||
token: invalidToken,
|
||||
withRoles: false,
|
||||
groupID: group.ID,
|
||||
svcRes: groups.Group{},
|
||||
authenticateErr: svcerr.ErrAuthentication,
|
||||
@@ -556,30 +576,33 @@ func TestViewGroup(t *testing.T) {
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "view group with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
groupID: group.ID,
|
||||
svcRes: groups.Group{},
|
||||
svcErr: nil,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
desc: "view group with empty token",
|
||||
domainID: domainID,
|
||||
token: "",
|
||||
withRoles: false,
|
||||
groupID: group.ID,
|
||||
svcRes: groups.Group{},
|
||||
svcErr: nil,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "view group with invalid group id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
groupID: wrongID,
|
||||
svcRes: groups.Group{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
desc: "view group with invalid group id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
groupID: wrongID,
|
||||
svcRes: groups.Group{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "view group with service response that cannot be unmarshalled",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
groupID: group.ID,
|
||||
desc: "view group with service response that cannot be unmarshalled",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
groupID: group.ID,
|
||||
svcRes: groups.Group{
|
||||
ID: group.ID,
|
||||
Name: "group_1",
|
||||
@@ -592,14 +615,15 @@ func TestViewGroup(t *testing.T) {
|
||||
err: errors.NewSDKError(errors.New("unexpected end of JSON input")),
|
||||
},
|
||||
{
|
||||
desc: "view group with empty id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
groupID: "",
|
||||
svcRes: groups.Group{},
|
||||
svcErr: nil,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKError(apiutil.ErrMissingID),
|
||||
desc: "view group with empty id",
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
withRoles: false,
|
||||
groupID: "",
|
||||
svcRes: groups.Group{},
|
||||
svcErr: nil,
|
||||
response: sdk.Group{},
|
||||
err: errors.NewSDKError(apiutil.ErrMissingID),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -609,12 +633,25 @@ func TestViewGroup(t *testing.T) {
|
||||
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
|
||||
}
|
||||
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
|
||||
svcCall := gsvc.On("ViewGroup", mock.Anything, tc.session, tc.groupID).Return(tc.svcRes, tc.svcErr)
|
||||
resp, err := mgsdk.Group(tc.groupID, tc.domainID, tc.token)
|
||||
svcCall := gsvc.On("ViewGroup", mock.Anything, tc.session, tc.groupID, tc.withRoles).Return(tc.svcRes, tc.svcErr)
|
||||
|
||||
var resp sdk.Group
|
||||
var err error
|
||||
|
||||
switch tc.withRoles {
|
||||
case true:
|
||||
resp, err = mgsdkRoles.Group(tc.groupID, tc.domainID, tc.token)
|
||||
default:
|
||||
resp, err = mgsdk.Group(tc.groupID, tc.domainID, tc.token)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.err, err)
|
||||
assert.Equal(t, tc.response, resp)
|
||||
if tc.withRoles {
|
||||
assert.Equal(t, resp.Roles, validRoles, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, validRoles, resp.Roles))
|
||||
}
|
||||
if tc.err == nil {
|
||||
ok := svcCall.Parent.AssertCalled(t, "ViewGroup", mock.Anything, tc.session, tc.groupID)
|
||||
ok := svcCall.Parent.AssertCalled(t, "ViewGroup", mock.Anything, tc.session, tc.groupID, tc.withRoles)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
svcCall.Unset()
|
||||
@@ -3631,6 +3668,7 @@ func generateTestGroup(t *testing.T) sdk.Group {
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Status: groups.EnabledStatus.String(),
|
||||
Roles: validRoles,
|
||||
}
|
||||
return gr
|
||||
}
|
||||
|
||||
@@ -1324,6 +1324,7 @@ type mgSDK struct {
|
||||
msgContentType ContentType
|
||||
client *http.Client
|
||||
curlFlag bool
|
||||
roles bool
|
||||
}
|
||||
|
||||
// Config contains sdk configuration parameters.
|
||||
@@ -1341,6 +1342,7 @@ type Config struct {
|
||||
MsgContentType ContentType
|
||||
TLSVerification bool
|
||||
CurlFlag bool
|
||||
Roles bool
|
||||
}
|
||||
|
||||
// NewSDK returns new supermq SDK instance.
|
||||
@@ -1365,12 +1367,16 @@ func NewSDK(conf Config) SDK {
|
||||
},
|
||||
},
|
||||
curlFlag: conf.CurlFlag,
|
||||
roles: conf.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
// processRequest creates and send a new HTTP request, and checks for errors in the HTTP response.
|
||||
// It then returns the response headers, the response body, and the associated error(s) (if any).
|
||||
func (sdk mgSDK) processRequest(method, reqUrl, token string, data []byte, headers map[string]string, expectedRespCodes ...int) (http.Header, []byte, errors.SDKError) {
|
||||
if sdk.roles {
|
||||
reqUrl = reqUrl + fmt.Sprintf("?roles=%v", true)
|
||||
}
|
||||
req, err := http.NewRequest(method, reqUrl, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return make(http.Header), []byte{}, errors.NewSDKError(err)
|
||||
|
||||
@@ -128,6 +128,7 @@ func convertGroup(g sdk.Group) groups.Group {
|
||||
AccessProviderRoleId: g.AccessProviderRoleId,
|
||||
AccessProviderRoleName: g.AccessProviderRoleName,
|
||||
AccessProviderRoleActions: g.AccessProviderRoleActions,
|
||||
Roles: g.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +195,7 @@ func convertClient(c sdk.Client) clients.Client {
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
UpdatedBy: c.UpdatedBy,
|
||||
Status: status,
|
||||
Roles: c.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +218,7 @@ func convertChannel(g sdk.Channel) mgchannels.Channel {
|
||||
UpdatedAt: g.UpdatedAt,
|
||||
UpdatedBy: g.UpdatedBy,
|
||||
Status: status,
|
||||
Roles: g.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user