Skip to content

Commit 3852780

Browse files
abhishekSingh1007Harness
authored andcommitted
feat: [PL-64144]: Implement MCP Server Tool For Access-Control. (#62)
* fix: [PL-64144]: Updated tools.go File * fix: [PL-64144]: Updated config.go File * fix: [PL-64144]: Updated main.go file * fix: [PL-64144_access_control]: Resolved Conflicts * fix: [PL-64144]: Separated RBAC And Principals Clients, Added Enums for valid inputs * fix: [PL-64144]: Removed Unused DTOs. * fix: [PL-64144]: Removed Logging Statement. * feat: [PL-64144]: Added Access Control Tool.
1 parent 0ec4c52 commit 3852780

File tree

6 files changed

+854
-1
lines changed

6 files changed

+854
-1
lines changed

client/accesscontrol.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/harness/harness-mcp/client/dto"
10+
)
11+
12+
const (
13+
getUsersPath = "/user/aggregate"
14+
getRolePath = "/api/roles"
15+
listPermissionsPath = "/api/permissions"
16+
listRoleAssignmentsPath = "/api/roleassignments/filter"
17+
getUserGroupPath = "/v2/user-groups"
18+
getServiceAccountPath = "/serviceaccount/aggregate"
19+
)
20+
21+
type RBACService struct {
22+
Client *Client
23+
}
24+
25+
type PrincipalService struct {
26+
Client *Client
27+
}
28+
29+
func (u *PrincipalService) GetAllUsers(ctx context.Context, scope dto.Scope, searchTerm string, page int, size int, opts *dto.UsersRequestBody) (*dto.AccessControlOutput[dto.UsersOutput], error) {
30+
if opts == nil {
31+
opts = &dto.UsersRequestBody{}
32+
}
33+
34+
params := make(map[string]string)
35+
params["pageIndex"] = fmt.Sprintf("%d", page)
36+
params["pageSize"] = fmt.Sprintf("%d", size)
37+
params["searchTerm"] = searchTerm
38+
39+
addScope(scope, params)
40+
41+
resp := &dto.AccessControlOutput[dto.UsersOutput]{}
42+
err := u.Client.Post(ctx, getUsersPath, params, opts, resp)
43+
if err != nil {
44+
return nil, fmt.Errorf("failed to list the users: %w", err)
45+
}
46+
47+
return resp, nil
48+
}
49+
50+
func (u *RBACService) GetRoleInfo(ctx context.Context, scope dto.Scope, roleID string) (*dto.AccessControlOutput[dto.RoleInfoOutputData], error) {
51+
params := make(map[string]string)
52+
53+
path := fmt.Sprintf(getRolePath+"/%s", roleID)
54+
55+
addScope(scope, params)
56+
resp := &dto.AccessControlOutput[dto.RoleInfoOutputData]{}
57+
err := u.Client.Get(ctx, path, params, map[string]string{}, resp)
58+
if err != nil {
59+
return nil, fmt.Errorf("failed to get role info: %w", err)
60+
}
61+
return resp, nil
62+
}
63+
64+
func (r *RBACService) ListAvailableRoles(ctx context.Context, scope dto.Scope, page int, size int) (*dto.AccessControlOutput[dto.RolesOutputData], error) {
65+
66+
params := make(map[string]string)
67+
params["accountIdentifier"] = scope.AccountID
68+
params["pageIndex"] = fmt.Sprintf("%d", page)
69+
params["pageSize"] = fmt.Sprintf("%d", size)
70+
71+
addScope(scope, params)
72+
73+
resp := &dto.AccessControlOutput[dto.RolesOutputData]{}
74+
err := r.Client.Get(ctx, getRolePath, params, map[string]string{}, resp)
75+
if err != nil {
76+
return nil, fmt.Errorf("failed to list the roles assigned to the user: %w", err)
77+
}
78+
79+
return resp, nil
80+
81+
}
82+
83+
func (p *RBACService) ListAvailablePermissions(ctx context.Context, scope dto.Scope, page int, size int) (*dto.AccessControlOutput[[]dto.PermissionsOutputData], error) {
84+
85+
params := make(map[string]string)
86+
params["accountIdentifier"] = scope.AccountID
87+
params["pageIndex"] = fmt.Sprintf("%d", page)
88+
params["pageSize"] = fmt.Sprintf("%d", size)
89+
90+
addScope(scope, params)
91+
92+
resp := &dto.AccessControlOutput[[]dto.PermissionsOutputData]{}
93+
err := p.Client.Get(ctx, listPermissionsPath, params, map[string]string{}, resp)
94+
if err != nil {
95+
return nil, fmt.Errorf("failed to list the permissions of the user: %w", err)
96+
}
97+
98+
return resp, nil
99+
}
100+
101+
func (ra *RBACService) ListRoleAssignmentsTool(ctx context.Context, scope dto.Scope, page int, size int, opts *dto.RoleAssignmentRequestBody, resourceGroupNames []string, roleNames []string, principalTypes string, principalScopeLevelFilter string, principalFilter []dto.RoleAssignmentPrincipalFilter) (*dto.AccessControlOutput[dto.RoleAssignmentsOutputData], error) {
102+
if opts == nil {
103+
opts = &dto.RoleAssignmentRequestBody{}
104+
}
105+
106+
params := make(map[string]string)
107+
108+
addScope(scope, params)
109+
110+
if resourceGroupNames != nil && len(resourceGroupNames) > 0 {
111+
opts.ResourceGroupFilter = resourceGroupNames
112+
}
113+
if roleNames != nil && len(roleNames) > 0 {
114+
opts.RoleFilter = roleNames
115+
}
116+
117+
if strings.TrimSpace(principalTypes) != "" {
118+
rawPrincipalTypes := strings.Split(principalTypes, ",")
119+
principalFilter := make([]string, 0)
120+
for _, pt := range rawPrincipalTypes {
121+
pt = strings.ToUpper(strings.TrimSpace(pt))
122+
if _, ok := dto.AllowedPrincipalTypes[pt]; ok {
123+
principalFilter = append(principalFilter, pt)
124+
} else {
125+
return nil, fmt.Errorf("Invalid principal type: %s\n", pt)
126+
}
127+
}
128+
if len(principalFilter) > 0 {
129+
opts.PrincipalTypeFilter = principalFilter
130+
}
131+
}
132+
133+
if strings.TrimSpace(principalScopeLevelFilter) != "" {
134+
rawScopeList := strings.Split(principalScopeLevelFilter, ",")
135+
scopeFilter := make([]string, 0)
136+
for _, scope := range rawScopeList {
137+
scope = strings.ToLower(strings.TrimSpace(scope))
138+
if _, ok := dto.AllowedScopeLevels[scope]; ok {
139+
scopeFilter = append(scopeFilter, scope)
140+
} else {
141+
return nil, fmt.Errorf("Invalid scope level: %s\n", scope)
142+
}
143+
}
144+
if len(scopeFilter) > 0 {
145+
opts.PrincipalScopeLevelFilter = scopeFilter
146+
}
147+
}
148+
149+
if principalFilter != nil && len(principalFilter) > 0 {
150+
opts.PrincipalFilter = principalFilter
151+
}
152+
153+
resp := &dto.AccessControlOutput[dto.RoleAssignmentsOutputData]{}
154+
err := ra.Client.Post(ctx, listRoleAssignmentsPath, params, opts, resp)
155+
if err != nil {
156+
optsJSON, _ := json.MarshalIndent(opts, "", " ")
157+
return nil, fmt.Errorf("failed to list the role assignments: %w\nRequest body: %s", err, string(optsJSON))
158+
}
159+
160+
return resp, nil
161+
}
162+
163+
func (uInfo *PrincipalService) GetUserInfo(ctx context.Context, scope dto.Scope, userID string, page int, size int) (*dto.AccessControlOutput[dto.UserInfoData], error) {
164+
165+
params := make(map[string]string)
166+
path := fmt.Sprintf(getUsersPath+"/%s", userID)
167+
168+
addScope(scope, params)
169+
170+
resp := &dto.AccessControlOutput[dto.UserInfoData]{}
171+
err := uInfo.Client.Get(ctx, path, params, map[string]string{}, resp)
172+
if err != nil {
173+
return nil, fmt.Errorf("Failed to list the user info: %w", err)
174+
}
175+
176+
return resp, nil
177+
}
178+
179+
func (uGroupInfo *PrincipalService) GetUserGroupInfo(ctx context.Context, scope dto.Scope, userGroupID string) (*dto.AccessControlOutput[dto.UserGroupInfoData], error) {
180+
181+
params := make(map[string]string)
182+
path := fmt.Sprintf(getUserGroupPath+"/%s", userGroupID)
183+
184+
addScope(scope, params)
185+
186+
resp := &dto.AccessControlOutput[dto.UserGroupInfoData]{}
187+
err := uGroupInfo.Client.Get(ctx, path, params, map[string]string{}, resp)
188+
if err != nil {
189+
return nil, fmt.Errorf("Failed to list the user group info: %w", err)
190+
}
191+
192+
return resp, nil
193+
}
194+
195+
func (sAccount *PrincipalService) GetServiceAccount(ctx context.Context, scope dto.Scope, serviceAccountID string) (*dto.AccessControlOutput[dto.ServiceAccountData], error) {
196+
197+
params := make(map[string]string)
198+
path := fmt.Sprintf(getServiceAccountPath+"/%s", serviceAccountID)
199+
200+
addScope(scope, params)
201+
202+
resp := &dto.AccessControlOutput[dto.ServiceAccountData]{}
203+
204+
err := sAccount.Client.Get(ctx, path, params, map[string]string{}, resp)
205+
if err != nil {
206+
return nil, fmt.Errorf("Failed to list the service account info: %w", err)
207+
}
208+
209+
return resp, nil
210+
}

0 commit comments

Comments
 (0)