1 package resolver
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "reflect"
8
9 "edge-infra.dev/pkg/edge/api/apierror"
10 "edge-infra.dev/pkg/edge/api/graph/model"
11 "edge-infra.dev/pkg/edge/api/middleware"
12 "edge-infra.dev/pkg/edge/api/utils"
13 "edge-infra.dev/pkg/edge/bsl"
14
15 "github.com/99designs/gqlgen/graphql"
16 )
17
18 func UserHasMatchingRole(ctx context.Context, apiRoles []model.Role) bool {
19 u := middleware.ForContext(ctx)
20 if u == nil || len(u.Roles) == 0 {
21 return false
22 }
23 for _, role := range apiRoles {
24 if utils.Contains(u.Roles, string(role)) {
25 return true
26 }
27 }
28 return false
29 }
30
31 func (r *Resolver) CanAssignRole() func(ctx context.Context, obj interface{}, next graphql.Resolver, abilityMap map[string]interface{}) (res interface{}, err error) {
32 return func(ctx context.Context, obj interface{}, next graphql.Resolver, abilityMap map[string]interface{}) (res interface{}, err error) {
33 userRoles, err := middleware.GetEdgeRoles(ctx)
34 if err != nil {
35 return nil, err
36 }
37 bm, ok := obj.(map[string]interface{})
38 if !ok {
39 return nil, fmt.Errorf("input object is not a map")
40 }
41
42 requestedRoles, ok := bm["roles"]
43 requestedRole, okRole := bm["role"].(string)
44 if !ok {
45 if !okRole {
46 return nil, fmt.Errorf("there is no role field in the input map")
47 }
48 }
49
50 accessibleRoles := []string{}
51 for _, userRole := range userRoles {
52 if abilityMap[userRole] != nil {
53 for _, role := range abilityMap[userRole].([]interface{}) {
54 accessibleRoles = append(accessibleRoles, role.(string))
55 }
56 }
57 }
58
59
60 if requestedRole != "" {
61 if utils.Contains(accessibleRoles, requestedRole) {
62 return next(ctx)
63 }
64 return nil, fmt.Errorf("access denied: user cannot assign the requested role %s", requestedRole)
65 }
66
67
68
69 userToAssign, okUser := bm["username"].(string)
70 if !okUser {
71 return nil, fmt.Errorf("fail to grab user to assign info")
72 }
73
74 userCurrentRoles, err := r.RoleService.GetEdgeGroupsForUserUser(ctx, userToAssign)
75 if err != nil {
76 return nil, fmt.Errorf("fail to get role for current user being assigned")
77 }
78
79 if rqRole, okRq := requestedRoles.([]interface{}); okRq {
80 for _, roleValue := range rqRole {
81 role := roleValue.(string)
82 if role == "" {
83 return nil, fmt.Errorf("empty role input")
84 }
85 if !utils.Contains(accessibleRoles, role) && !utils.Contains(userCurrentRoles, role) {
86
87 return nil, fmt.Errorf("access denied: user cannot assign the requested role %s", role)
88 }
89 }
90 }
91 return next(ctx)
92 }
93 }
94
95 func (r *Resolver) HasHelmWorkloadAccess() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
96 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
97 if UserHasMatchingRole(ctx, []model.Role{model.RoleTotpRole}) {
98 return next(ctx)
99 }
100 if UserHasMatchingRole(ctx, []model.Role{model.RoleEdgeBootstrap}) {
101 return next(ctx)
102 }
103 id, ok, o := getUserOrg(ctx, obj, field)
104 if !ok || id == "" {
105 return next(ctx)
106 }
107 banners, errs := r.BannerService.GetBanners(ctx)
108 if errs != nil && errs.Error() != apierror.BannerCheckMessage {
109 return nil, apierror.AddEmptyAPIErrorToResponse(ctx, errs)
110 }
111 banner, errs := r.HelmService.GetBannerByHelmEdgeID(ctx, id)
112 if errs != nil {
113 return nil, apierror.AddEmptyAPIErrorToResponse(ctx, errs)
114 }
115 t, err := r.TenantService.GetTenantByBannerID(ctx, banner.BannerEdgeID)
116 if err != nil || o != t.OrgName {
117 return nil, fmt.Errorf("access denied: user org %s does not have access to requested organization", o)
118 }
119 if !isUserAssignedToBanner(banners, banner.BannerEdgeID) {
120 return nil, fmt.Errorf("access denied: user does not have access to the the requested workload resource %s", id)
121 }
122 addIDsToContext(ctx, t.TenantEdgeID, banner.BannerEdgeID, "")
123 return next(ctx)
124 }
125 }
126
127 func (r *Resolver) HasClusterAccess() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
128 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
129 if UserHasMatchingRole(ctx, []model.Role{model.RoleTotpRole}) {
130 return next(ctx)
131 }
132 if UserHasMatchingRole(ctx, []model.Role{model.RoleEdgeBootstrap}) {
133 return next(ctx)
134 }
135 id, ok, o := getUserOrg(ctx, obj, field)
136 if !ok || id == "" {
137 return next(ctx)
138 }
139 banners, errs := r.BannerService.GetBanners(ctx)
140 if errs != nil && errs.Error() != apierror.BannerCheckMessage {
141 return nil, apierror.AddEmptyAPIErrorToResponse(ctx, errs)
142 }
143 cluster, errs := r.StoreClusterService.GetCluster(ctx, id)
144 if errs != nil {
145 return nil, apierror.AddEmptyAPIErrorToResponse(ctx, errs)
146 }
147 t, err := r.TenantService.GetTenantByBannerID(ctx, cluster.BannerEdgeID)
148 if err != nil || o != t.OrgName {
149 return nil, fmt.Errorf("access denied: user org %s does not have access to requested organization", o)
150 }
151 if !isUserAssignedToBanner(banners, cluster.BannerEdgeID) {
152 return nil, fmt.Errorf("access denied: user does not have access to the banner %s that the requested cluster resource %s exists in", cluster.BannerEdgeID, id)
153 }
154 addIDsToContext(ctx, t.TenantEdgeID, cluster.BannerEdgeID, cluster.ClusterEdgeID)
155 return next(ctx)
156 }
157 }
158
159 func addIDsToContext(ctx context.Context, tenantID string, bannerID string, clusterEdgeID string) {
160 user := middleware.ForContext(ctx)
161 user.ClusterEdgeID = clusterEdgeID
162 user.BannerID = bannerID
163 user.TenantID = tenantID
164 }
165
166 func fullUsername(username, org string) string {
167 return fmt.Sprintf("acct:%s@%s", org, username)
168 }
169
170 func (r *Resolver) HasBannerAccess() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
171 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
172 if UserHasMatchingRole(ctx, []model.Role{model.RoleTotpRole}) {
173 return next(ctx)
174 }
175 id, ok, o := getUserOrg(ctx, obj, field)
176 if !ok || id == "" {
177 return next(ctx)
178 }
179 t, err := r.TenantService.GetTenantByBannerID(ctx, id)
180 if err != nil || o != t.OrgName {
181 return nil, fmt.Errorf("access denied: user org %s does not have access to requested organization", o)
182 }
183 banners, errs := r.BannerService.GetBanners(ctx)
184 if errs != nil && errs.Error() != apierror.BannerCheckMessage {
185 return nil, apierror.AddAPIErrorToResponse(ctx, err)
186 }
187 if !isUserAssignedToBanner(banners, id) {
188 return nil, fmt.Errorf("access denied: user does not have access to requested banner resource %s", id)
189 }
190 addIDsToContext(ctx, t.TenantEdgeID, id, "")
191 return next(ctx)
192 }
193 }
194
195 func (r *Resolver) HasLabelAccess() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
196 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
197
198 if UserHasMatchingRole(ctx, []model.Role{model.RoleTotpRole}) {
199 return next(ctx)
200 }
201 bm, o := verifyUserAccess(ctx, obj)
202 if bm[field] == nil {
203 return next(ctx)
204 }
205 return fieldInterceptor(ctx, next, bm, o, field, func(ctx context.Context, id, o string) error {
206 return r.checkIfUserHasLabelAccessByBanner(ctx, id, o)
207 })
208 }
209 }
210
211 func (r *Resolver) HasRole() func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []model.Role) (interface{}, error) {
212 return func(ctx context.Context, _ interface{}, next graphql.Resolver, roles []model.Role) (interface{}, error) {
213 if UserHasMatchingRole(ctx, roles) {
214 return next(ctx)
215 }
216 return nil, fmt.Errorf("access denied: user not one of %v", roles)
217 }
218 }
219
220
221 func (r *Resolver) HasTerminalAccess() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (interface{}, error) {
222 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
223 if UserHasMatchingRole(ctx, []model.Role{model.RoleTotpRole}) {
224 return next(ctx)
225 }
226 bm, o := verifyUserAccess(ctx, obj)
227 if bm[field] == nil {
228 return next(ctx)
229 }
230 return fieldInterceptor(ctx, next, bm, o, field, func(ctx context.Context, id, o string) error {
231 return r.checkUserTerminalAccess(ctx, id, o)
232 })
233 }
234 }
235
236
237 func (r *Resolver) HasValidProviderPinSetting() func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (interface{}, error) {
238 return func(ctx context.Context, obj interface{}, next graphql.Resolver, field string) (res interface{}, err error) {
239 o := obj.(map[string]interface{})
240 x := o[field].(int64)
241 if x >= 3 && x <= 5 {
242 return next(ctx)
243 }
244 return nil, fmt.Errorf("%s must be between 3-5", field)
245 }
246 }
247
248
249 func verifyUserAccess(ctx context.Context, obj interface{}) (map[string]interface{}, string) {
250 u := middleware.ForContext(ctx)
251 if u == nil || obj == nil {
252 return make(map[string]interface{}, 0), ""
253 }
254 bm := obj.(map[string]interface{})
255 o := bsl.GetOrgShortName(u.Organization)
256 return bm, o
257 }
258
259
260 func fieldInterceptor(ctx context.Context, next graphql.Resolver, bm map[string]interface{}, o, field string, cb func(ctx context.Context, id string, o string) error) (interface{}, error) {
261 _type := reflect.TypeOf(bm[field])
262 switch _type.Kind() {
263 case reflect.Slice:
264 fieldParams := bm[field].([]interface{})
265 for i := range fieldParams {
266 fieldParam := fieldParams[i]
267 id, ok := fieldParam.(string)
268 if !ok || id == "" {
269 return next(ctx)
270 }
271 err := cb(ctx, id, o)
272 if err != nil {
273 return nil, err
274 }
275 }
276 case reflect.String:
277 id, ok := bm[field].(string)
278 if !ok || id == "" {
279 return next(ctx)
280 }
281 err := cb(ctx, id, o)
282 if err != nil {
283 return nil, err
284 }
285 default:
286 return nil, errors.New("unknown entity type")
287 }
288 return next(ctx)
289 }
290
291 func getUserOrg(ctx context.Context, obj interface{}, field string) (string, bool, string) {
292 bm, o := verifyUserAccess(ctx, obj)
293 id, ok := bm[field].(string)
294 return id, ok, o
295 }
296
297 func isUserAssignedToBanner(banners []*model.Banner, bannerID string) bool {
298 for _, banner := range banners {
299 if banner.BannerEdgeID == bannerID {
300 return true
301 }
302 }
303 return false
304 }
305
306 func (r *Resolver) checkIfUserHasLabelAccessByBanner(ctx context.Context, labelEdgeID, userOrg string) error {
307 banners, err := r.BannerService.GetBanners(ctx)
308 if err != nil && err.Error() != apierror.BannerCheckMessage {
309 return err
310 }
311 label, err := r.LabelService.GetLabel(ctx, labelEdgeID)
312 if err != nil {
313 return fmt.Errorf("access denied: user org %s does not have access to requested organization", userOrg)
314 }
315 if label.BannerEdgeID == nil {
316 return nil
317 }
318 t, errs := r.LabelService.GetLabelTenant(ctx, labelEdgeID)
319 if errs != nil || userOrg != t.OrgName {
320 return fmt.Errorf("access denied: user org %s does not have access to requested organization", userOrg)
321 }
322 if !isUserAssignedToBanner(banners, *label.BannerEdgeID) {
323 return fmt.Errorf("access denied: user does not have access to the banner %s that the requested label resource %s exists in", *label.BannerEdgeID, labelEdgeID)
324 }
325 return nil
326 }
327
328 func (r *Resolver) checkUserTerminalAccess(ctx context.Context, terminalEdgeID, org string) error {
329 getLabel := false
330 terminal, err := r.TerminalService.GetTerminal(ctx, terminalEdgeID, &getLabel)
331 if err != nil {
332 return err
333 }
334 cluster, err := r.StoreClusterService.GetClusterByClusterEdgeID(ctx, terminal.ClusterEdgeID)
335 if err != nil {
336 return err
337 }
338
339 addIDsToContext(ctx, "", cluster.BannerEdgeID, terminal.ClusterEdgeID)
340
341 u := middleware.ForContext(ctx)
342 if u == nil {
343 return fmt.Errorf("access denied: user not authenticated")
344 }
345 switch {
346 case UserHasMatchingRole(ctx, []model.Role{model.RoleEdgeOrgAdmin}):
347 return nil
348 case UserHasMatchingRole(ctx, []model.Role{model.RoleEdgeBannerAdmin, model.RoleEdgeBannerOperator}):
349 userAssignedBanners, err := r.BannerService.GetUserAssignedBanners(ctx, fullUsername(u.Username, org))
350 if err != nil {
351 return err
352 }
353 for _, banner := range userAssignedBanners {
354 if cluster.BannerEdgeID == banner.BannerEdgeID {
355 return nil
356 }
357 }
358 fallthrough
359 default:
360 return fmt.Errorf("access denied: user does not have access to terminal %s", terminalEdgeID)
361 }
362 }
363
View as plain text