Passed
Push — develop ( f534b1...a82689 )
by Plexxi
06:09 queued 03:13
created

PermissionsResolver._user_has_list_permission()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
"""
17
Module containing resolver classes which contain permission resolving logic for different resource
18
types.
19
"""
20
21
import sys
22
import logging as stdlib_logging
23
24
from st2common import log as logging
25
from st2common.models.db.pack import PackDB
26
from st2common.models.db.webhook import WebhookDB
27
from st2common.models.system.common import ResourceReference
28
from st2common.constants.triggers import WEBHOOK_TRIGGER_TYPE
29
from st2common.rbac.types import PermissionType
30
from st2common.rbac.types import ResourceType
31
from st2common.rbac.types import SystemRole
32
from st2common.rbac.types import GLOBAL_PACK_PERMISSION_TYPES
33
from st2common.services.rbac import get_roles_for_user
34
from st2common.services.rbac import get_all_permission_grants_for_user
35
36
LOG = logging.getLogger(__name__)
37
38
__all__ = [
39
    'RunnerPermissionsResolver',
40
    'PackPermissionsResolver',
41
    'SensorPermissionsResolver',
42
    'ActionPermissionsResolver',
43
    'ActionAliasPermissionsResolver',
44
    'RulePermissionsResolver',
45
    'RuleEnforcementPermissionsResolver',
46
    'KeyValuePermissionsResolver',
47
    'ExecutionPermissionsResolver',
48
    'WebhookPermissionsResolver',
49
50
    'get_resolver_for_resource_type',
51
    'get_resolver_for_permission_type'
52
]
53
54
# "Read" permission names which are granted to observer role by default
55
READ_PERMISSION_NAMES = [
56
    'view',
57
    'list'
58
]
59
60
61
class PermissionsResolver(object):
62
    """
63
    Base Permissions Resolver class.
64
65
    Permission resolver classes implement permission resolving / checking logic for a particular
66
    resource type.
67
    """
68
69
    resource_type = None  # Constant for the resource type this resolver refers to
70
71
    def user_has_permission(self, user_db, permission_type):
72
        """
73
        Method for checking user permissions which are not tied to a particular resource.
74
        """
75
        raise NotImplementedError()
76
77
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
78
        """
79
        Method for checking user permissions on a resource which is to be created (e.g.
80
        create operation).
81
        """
82
        raise NotImplementedError()
83
84
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
85
        """
86
        Method for checking user permissions on an existing resource (e.g. get one, edit, delete
87
        operations).
88
        """
89
        raise NotImplementedError()
90
91
    def _user_has_list_permission(self, user_db, permission_type):
92
        """
93
        Common method for checking if a user has specific "list" resource permission (e.g.
94
        rules_list, action_list, etc.).
95
        """
96
        assert PermissionType.get_permission_name(permission_type) == 'list'
97
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
98
99
    def _user_has_global_permission(self, user_db, permission_type):
100
        """
101
        Custom method for checking if user has a particular global permission which doesn't apply
102
        to a specific resource but it's system-wide aka global permission.
103
        """
104
        log_context = {
105
            'user_db': user_db,
106
            'permission_type': permission_type,
107
            'resolver': self.__class__.__name__
108
        }
109
        self._log('Checking user permissions', extra=log_context)
110
111
        # First check the system role permissions
112
        has_system_role_permission = self._user_has_system_role_permission(
113
            user_db=user_db, permission_type=permission_type)
114
115
        if has_system_role_permission:
116
            self._log('Found a matching grant via system role', extra=log_context)
117
            return True
118
119
        # Check custom roles
120
        permission_types = [permission_type]
121
122
        # Check direct grants
123
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
124
                                                               permission_types=permission_types)
125
        if len(permission_grants) >= 1:
126
            self._log('Found a direct grant', extra=log_context)
127
            return True
128
129
        self._log('No matching grants found', extra=log_context)
130
        return False
131
132
    def _user_has_system_role_permission(self, user_db, permission_type):
133
        """
134
        Check the user system roles and return True if user has the required permission.
135
136
        :rtype: ``bool``
137
        """
138
        permission_name = PermissionType.get_permission_name(permission_type)
139
140
        user_role_dbs = get_roles_for_user(user_db=user_db)
141
        user_role_names = [role_db.name for role_db in user_role_dbs]
142
143
        if SystemRole.SYSTEM_ADMIN in user_role_names:
144
            # System admin has all the permissions
145
            return True
146
        elif SystemRole.ADMIN in user_role_names:
147
            # Admin has all the permissions
148
            return True
149
        elif SystemRole.OBSERVER in user_role_names and permission_name in READ_PERMISSION_NAMES:
150
            # Observer role has "view" permission on all the resources
151
            return True
152
153
        return False
154
155
    def _matches_permission_grant(self, resource_db, permission_grant, permission_type,
156
                                  all_permission_type):
157
        """
158
        :rtype: ``bool``
159
        """
160
        if permission_type in permission_grant.permission_types:
161
            # Direct permission grant
162
            return True
163
        elif all_permission_type in permission_grant.permission_types:
164
            # "ALL" permission grant
165
            return True
166
167
        return False
168
169
    def _get_all_permission_type_for_resource(self, resource_db):
170
        """
171
        Retrieve "ALL" permission type for the provided resource.
172
        """
173
        resource_type = resource_db.get_resource_type()
174
        permission_type = PermissionType.get_permission_type(resource_type=resource_type,
175
                                                             permission_name='all')
176
        return permission_type
177
178
    def _log(self, message, extra, level=stdlib_logging.DEBUG, **kwargs):
179
        """
180
        Custom logger method which prefix message with the class and caller method name.
181
        """
182
        class_name = self.__class__.__name__
183
        method_name = sys._getframe().f_back.f_code.co_name
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _getframe was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
184
        message_prefix = '%s.%s: ' % (class_name, method_name)
185
        message = message_prefix + message
186
187
        LOG.log(level, message, extra=extra, **kwargs)
188
189
190
class ContentPackResourcePermissionsResolver(PermissionsResolver):
191
    """
192
    Base permissions resolver class which contains common functionality for resources which belong
193
    to a pack (sensors, actions, action aliases, rules, ...).
194
    """
195
196
    resource_type = None
197
198
    # A list of resource-specific permission types which grant / imply "view" permission type
199
    view_grant_permission_types = []
200
201
    def _user_has_resource_permission(self, user_db, pack_uid, resource_uid, permission_type):
202
        log_context = {
203
            'user_db': user_db,
204
            'pack_uid': pack_uid,
205
            'resource_uid': resource_uid,
206
            'resource_type': self.resource_type,
207
            'permission_type': permission_type,
208
            'resolver': self.__class__.__name__
209
        }
210
        self._log('Checking user resource permissions', extra=log_context)
211
212
        # First check the system role permissions
213
        has_system_role_permission = self._user_has_system_role_permission(
214
            user_db=user_db, permission_type=permission_type)
215
216
        if has_system_role_permission:
217
            self._log('Found a matching grant via system role', extra=log_context)
218
            return True
219
220
        # Check custom roles
221
        view_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
222
                                                                  permission_name='view')
223
        all_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
224
                                                                 permission_name='all')
225
226
        if permission_type == view_permission_type:
227
            # Note: Some permissions such as "create", "modify", "delete" and "execute" also
228
            # grant / imply "view" permission
229
            permission_types = self.view_grant_permission_types[:] + [permission_type]
230
        elif permission_type not in all_permission_type:
231
            permission_types = [all_permission_type, permission_type]
232
        else:
233
            permission_types = [permission_type]
234
235
        # Check direct grants on the specified resource
236
        resource_types = [self.resource_type]
237
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
238
                                                               resource_uid=resource_uid,
239
                                                               resource_types=resource_types,
240
                                                               permission_types=permission_types)
241
        if len(permission_grants) >= 1:
242
            self._log('Found a direct grant on the action', extra=log_context)
243
            return True
244
245
        # Check grants on the parent pack
246
        resource_types = [ResourceType.PACK]
247
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
248
                                                               resource_uid=pack_uid,
249
                                                               resource_types=resource_types,
250
                                                               permission_types=permission_types)
251
252
        if len(permission_grants) >= 1:
253
            self._log('Found a grant on the action parent pack', extra=log_context)
254
            return True
255
256
        self._log('No matching grants found', extra=log_context)
257
        return False
258
259
260
class RunnerPermissionsResolver(PermissionsResolver):
261
    """
262
    Permission resolver for "runner_type" resource type.
263
    """
264
    resource_type = ResourceType.RUNNER
265
266
    def user_has_permission(self, user_db, permission_type):
267
        assert permission_type in [PermissionType.RUNNER_LIST]
268
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
269
270
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
271
        log_context = {
272
            'user_db': user_db,
273
            'resource_db': resource_db,
274
            'permission_type': permission_type,
275
            'resolver': self.__class__.__name__
276
        }
277
        self._log('Checking user resource permissions', extra=log_context)
278
279
        # First check the system role permissions
280
        has_system_role_permission = self._user_has_system_role_permission(
281
            user_db=user_db, permission_type=permission_type)
282
283
        if has_system_role_permission:
284
            self._log('Found a matching grant via system role', extra=log_context)
285
            return True
286
287
        # Check custom roles
288
        resource_uid = resource_db.get_uid()
289
        resource_types = [ResourceType.RUNNER]
290
        permission_types = [permission_type]
291
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
292
                                                               resource_uid=resource_uid,
293
                                                               resource_types=resource_types,
294
                                                               permission_types=permission_types)
295
296
        if len(permission_grants) >= 1:
297
            self._log('Found a direct grant on the runner type', extra=log_context)
298
            return True
299
300
        self._log('No matching grants found', extra=log_context)
301
        return False
302
303
304
class PackPermissionsResolver(PermissionsResolver):
305
    """
306
    Permission resolver for "pack" resource type.
307
    """
308
309
    resource_type = ResourceType.PACK
310
311
    def user_has_permission(self, user_db, permission_type):
312
        assert permission_type in GLOBAL_PACK_PERMISSION_TYPES
313
314
        if permission_type == PermissionType.PACK_LIST:
315
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
316
        else:
317
            return self._user_has_global_permission(user_db=user_db,
318
                                                    permission_type=permission_type)
319
320
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
321
        log_context = {
322
            'user_db': user_db,
323
            'resource_db': resource_db,
324
            'permission_type': permission_type,
325
            'resolver': self.__class__.__name__
326
        }
327
        self._log('Checking user resource permissions', extra=log_context)
328
329
        # First check the system role permissions
330
        has_system_role_permission = self._user_has_system_role_permission(
331
            user_db=user_db, permission_type=permission_type)
332
333
        if has_system_role_permission:
334
            self._log('Found a matching grant via system role', extra=log_context)
335
            return True
336
337
        # Check custom roles
338
        resource_uid = resource_db.get_uid()
339
        resource_types = [ResourceType.PACK]
340
        permission_types = [permission_type]
341
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
342
                                                               resource_uid=resource_uid,
343
                                                               resource_types=resource_types,
344
                                                               permission_types=permission_types)
345
346
        if len(permission_grants) >= 1:
347
            self._log('Found a direct grant on the pack', extra=log_context)
348
            return True
349
350
        self._log('No matching grants found', extra=log_context)
351
        return False
352
353
354
class SensorPermissionsResolver(ContentPackResourcePermissionsResolver):
355
    """
356
    Permission resolver for "sensor" resource type.
357
    """
358
359
    resource_type = ResourceType.SENSOR
360
    view_grant_permission_types = [
361
        PermissionType.SENSOR_ALL,
362
        PermissionType.SENSOR_MODIFY
363
    ]
364
365
    def user_has_permission(self, user_db, permission_type):
366
        assert permission_type in [PermissionType.SENSOR_LIST]
367
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
368
369
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
370
        sensor_uid = resource_db.get_uid()
371
        pack_uid = resource_db.get_pack_uid()
372
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
373
                                                  resource_uid=sensor_uid,
374
                                                  permission_type=permission_type)
375
376
377
class ActionPermissionsResolver(ContentPackResourcePermissionsResolver):
378
    """
379
    Permission resolver for "action" resource type.
380
    """
381
382
    resource_type = ResourceType.ACTION
383
    view_grant_permission_types = [
384
        PermissionType.ACTION_ALL,
385
        PermissionType.ACTION_CREATE,
386
        PermissionType.ACTION_MODIFY,
387
        PermissionType.ACTION_DELETE,
388
        PermissionType.ACTION_EXECUTE,
389
    ]
390
391
    def user_has_permission(self, user_db, permission_type):
392
        assert permission_type in [PermissionType.ACTION_LIST]
393
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
394
395
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
396
        assert permission_type in [PermissionType.ACTION_CREATE]
397
398
        action_uid = resource_api.get_uid()
399
        pack_uid = resource_api.get_pack_uid()
400
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
401
                                                  resource_uid=action_uid,
402
                                                  permission_type=permission_type)
403
404
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
405
        action_uid = resource_db.get_uid()
406
        pack_uid = resource_db.get_pack_uid()
407
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
408
                                                  resource_uid=action_uid,
409
                                                  permission_type=permission_type)
410
411
412
class ActionAliasPermissionsResolver(ContentPackResourcePermissionsResolver):
413
    """
414
    Permission resolver for "action_alias" resource type.
415
    """
416
417
    resource_type = ResourceType.ACTION_ALIAS
418
    view_grant_permission_types = [
419
        PermissionType.ACTION_ALIAS_ALL,
420
        PermissionType.ACTION_ALIAS_CREATE,
421
        PermissionType.ACTION_ALIAS_MODIFY,
422
        PermissionType.ACTION_ALIAS_DELETE
423
    ]
424
425
    def user_has_permission(self, user_db, permission_type):
426
        assert permission_type in [PermissionType.ACTION_ALIAS_LIST,
427
                                   PermissionType.ACTION_ALIAS_MATCH,
428
                                   PermissionType.ACTION_ALIAS_HELP]
429
430
        if permission_type == PermissionType.ACTION_ALIAS_LIST:
431
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
432
        elif permission_type in [PermissionType.ACTION_ALIAS_MATCH,
433
                                 PermissionType.ACTION_ALIAS_HELP]:
434
            return self._user_has_global_permission(user_db=user_db,
435
                                                    permission_type=permission_type)
436
        else:
437
            raise ValueError('Unsupported permission type: %s' % (permission_type))
438
439
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
440
        assert permission_type in [PermissionType.ACTION_ALIAS_CREATE]
441
442
        action_alias_uid = resource_api.get_uid()
443
        pack_uid = resource_api.get_pack_uid()
444
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
445
                                                  resource_uid=action_alias_uid,
446
                                                  permission_type=permission_type)
447
448
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
449
        action_alias_uid = resource_db.get_uid()
450
        pack_uid = resource_db.get_pack_uid()
451
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
452
                                                  resource_uid=action_alias_uid,
453
                                                  permission_type=permission_type)
454
455
456
class RulePermissionsResolver(ContentPackResourcePermissionsResolver):
457
    """
458
    Permission resolver for "rule" resource type.
459
    """
460
461
    resource_type = ResourceType.RULE
462
    view_grant_permission_types = [
463
        PermissionType.RULE_ALL,
464
        PermissionType.RULE_CREATE,
465
        PermissionType.RULE_MODIFY,
466
        PermissionType.RULE_DELETE
467
    ]
468
469
    def user_has_trigger_permission(self, user_db, trigger):
470
        """
471
        Check if the user has access to the provided trigger.
472
473
        This method is to be used during rule create and update where we check if the user has the
474
        necessary trigger permissions.
475
476
        Note: Right now we only support webhook triggers.
477
478
        :param trigger: "trigger" attribute of the RuleAPI object.
479
        :type trigger: ``dict``
480
        """
481
        log_context = {
482
            'user_db': user_db,
483
            'trigger': trigger,
484
            'resolver': self.__class__.__name__
485
        }
486
487
        trigger_type = trigger['type']
488
        trigger_parameters = trigger.get('parameters', {})
489
490
        if trigger_type != WEBHOOK_TRIGGER_TYPE:
491
            self._log('Not a webhook trigger type, ignoring trigger permission checking',
492
                      extra=log_context)
493
            return True
494
495
        resolver = get_resolver_for_resource_type(ResourceType.WEBHOOK)
496
        webhook_db = WebhookDB(name=trigger_parameters['url'])
497
        permission_type = PermissionType.WEBHOOK_CREATE
498
        result = resolver.user_has_resource_db_permission(user_db=user_db,
499
                                                          resource_db=webhook_db,
500
                                                          permission_type=permission_type)
501
502
        if result is True:
503
            self._log('Found a matching trigger grant', extra=log_context)
504
            return True
505
506
        self._log('No matching trigger grants found', extra=log_context)
507
        return False
508
509
    def user_has_action_permission(self, user_db, action_ref):
510
        """
511
        Check if the user has "execute" permission on the provided action.
512
        """
513
        pass
514
515
    def user_has_permission(self, user_db, permission_type):
516
        assert permission_type in [PermissionType.RULE_LIST]
517
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
518
519
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
520
        assert permission_type in [PermissionType.RULE_CREATE]
521
522
        rule_uid = resource_api.get_uid()
523
        pack_uid = resource_api.get_pack_uid()
524
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
525
                                                  resource_uid=rule_uid,
526
                                                  permission_type=permission_type)
527
528
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
529
        rule_uid = resource_db.get_uid()
530
        pack_uid = resource_db.get_pack_uid()
531
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
532
                                                  resource_uid=rule_uid,
533
                                                  permission_type=permission_type)
534
535
536
class RuleEnforcementPermissionsResolver(PermissionsResolver):
537
    """
538
    Permission resolver for "rule enforcement" resource type.
539
    """
540
    resource_type = ResourceType.RULE_ENFORCEMENT
541
542
    def user_has_permission(self, user_db, permission_type):
543
        assert permission_type in [PermissionType.RULE_ENFORCEMENT_LIST]
544
        permission_type = PermissionType.RULE_LIST
545
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
546
547
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
548
        log_context = {
549
            'user_db': user_db,
550
            'resource_db': resource_db,
551
            'permission_type': permission_type,
552
            'resolver': self.__class__.__name__
553
        }
554
        self._log('Checking user resource permissions', extra=log_context)
555
556
        # First check the system role permissions
557
        has_system_role_permission = self._user_has_system_role_permission(
558
            user_db=user_db, permission_type=permission_type)
559
560
        if has_system_role_permission:
561
            self._log('Found a matching grant via system role', extra=log_context)
562
            return True
563
564
        # Check custom roles
565
        rule_spec = getattr(resource_db, 'rule', None)
566
        rule_uid = rule_spec.uid
567
        rule_id = rule_spec.id
568
        rule_pack = ResourceReference.get_pack(rule_spec.ref)
569
570
        if not rule_uid or not rule_id or not rule_pack:
571
            LOG.error('Rule UID or ID or PACK not present in enforcement object. ' +
572
                      ('UID = %s, ID = %s, PACK = %s' % (rule_uid, rule_id, rule_pack)) +
573
                      'Cannot assess access permissions without it. Defaulting to DENY.')
574
            return False
575
576
        # TODO: Add utility methods for constructing uids from parts
577
        pack_db = PackDB(ref=rule_pack)
578
        rule_pack_uid = pack_db.get_uid()
579
580
        rule_permission_type = None
581
        if permission_type == PermissionType.RULE_ENFORCEMENT_VIEW:
582
            rule_permission_type = PermissionType.RULE_VIEW
583
        elif permission_type == PermissionType.RULE_ENFORCEMENT_LIST:
584
            rule_permission_type = PermissionType.RULE_LIST
585
        else:
586
            raise ValueError('Invalid permission type: %s' % (permission_type))
587
588
        permission_types = [PermissionType.RULE_ALL, rule_permission_type]
589
590
        view_permission_type = PermissionType.get_permission_type(resource_type=ResourceType.RULE,
591
                                                                  permission_name='view')
592
593
        if rule_permission_type == view_permission_type:
594
            permission_types = (RulePermissionsResolver.view_grant_permission_types[:] +
595
                                [rule_permission_type])
596
597
        # Check grants on the pack of the rule to which enforcement belongs to
598
        resource_types = [ResourceType.PACK]
599
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
600
                                                               resource_uid=rule_pack_uid,
601
                                                               resource_types=resource_types,
602
                                                               permission_types=permission_types)
603
604
        if len(permission_grants) >= 1:
605
            self._log('Found a grant on the enforcement rule parent pack', extra=log_context)
606
            return True
607
608
        # Check grants on the rule the enforcement belongs to
609
        resource_types = [ResourceType.RULE]
610
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
611
                                                               resource_uid=rule_uid,
612
                                                               resource_types=resource_types,
613
                                                               permission_types=permission_types)
614
615
        if len(permission_grants) >= 1:
616
            self._log('Found a grant on the enforcement\'s rule.', extra=log_context)
617
            return True
618
619
        self._log('No matching grants found', extra=log_context)
620
        return False
621
622
623
class KeyValuePermissionsResolver(PermissionsResolver):
624
    """
625
    Permission resolver for "key value pair" resource type.
626
    """
627
628
    resource_type = ResourceType.KEY_VALUE_PAIR
629
630
    def user_has_permission(self, user_db, permission_type):
631
        # TODO: We don't support assigning permissions on key value pairs yet
632
        return True
633
634
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
635
        # TODO: We don't support assigning permissions on key value pairs yet
636
        return True
637
638
639
class ExecutionPermissionsResolver(PermissionsResolver):
640
    """
641
    Permission resolver for "execution" resource type.
642
    """
643
644
    resource_type = ResourceType.EXECUTION
645
646
    def user_has_permission(self, user_db, permission_type):
647
        assert permission_type in [PermissionType.EXECUTION_LIST]
648
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
649
650
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
651
        log_context = {
652
            'user_db': user_db,
653
            'resource_db': resource_db,
654
            'permission_type': permission_type,
655
            'resolver': self.__class__.__name__
656
        }
657
        self._log('Checking user resource permissions', extra=log_context)
658
659
        # First check the system role permissions
660
        has_system_role_permission = self._user_has_system_role_permission(
661
            user_db=user_db, permission_type=permission_type)
662
663
        if has_system_role_permission:
664
            self._log('Found a matching grant via system role', extra=log_context)
665
            return True
666
667
        # Check custom roles
668
        action = resource_db['action']
669
670
        # TODO: Add utility methods for constructing uids from parts
671
        pack_db = PackDB(ref=action['pack'])
672
673
        action_uid = action['uid']
674
        action_pack_uid = pack_db.get_uid()
675
676
        # Note: "action_execute" also grants / implies "execution_re_run" and "execution_stop"
677
        if permission_type == PermissionType.EXECUTION_VIEW:
678
            action_permission_type = PermissionType.ACTION_VIEW
679
        elif permission_type in [PermissionType.EXECUTION_RE_RUN,
680
                                 PermissionType.EXECUTION_STOP]:
681
            action_permission_type = PermissionType.ACTION_EXECUTE
682
        elif permission_type == PermissionType.EXECUTION_ALL:
683
            action_permission_type = PermissionType.ACTION_ALL
684
        else:
685
            raise ValueError('Invalid permission type: %s' % (permission_type))
686
687
        # Check grants on the pack of the action to which execution belongs to
688
        resource_types = [ResourceType.PACK]
689
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
690
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
691
                                                               resource_uid=action_pack_uid,
692
                                                               resource_types=resource_types,
693
                                                               permission_types=permission_types)
694
695
        if len(permission_grants) >= 1:
696
            self._log('Found a grant on the execution action parent pack', extra=log_context)
697
            return True
698
699
        # Check grants on the action the execution belongs to
700
        resource_types = [ResourceType.ACTION]
701
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
702
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
703
                                                               resource_uid=action_uid,
704
                                                               resource_types=resource_types,
705
                                                               permission_types=permission_types)
706
707
        if len(permission_grants) >= 1:
708
            self._log('Found a grant on the execution action', extra=log_context)
709
            return True
710
711
        self._log('No matching grants found', extra=log_context)
712
        return False
713
714
715
class WebhookPermissionsResolver(PermissionsResolver):
716
717
    resource_type = ResourceType.WEBHOOK
718
719
    def user_has_permission(self, user_db, permission_type):
720
        # TODO
721
        return True
722
723
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
724
        log_context = {
725
            'user_db': user_db,
726
            'resource_db': resource_db,
727
            'permission_type': permission_type,
728
            'resolver': self.__class__.__name__
729
        }
730
        self._log('Checking user resource permissions', extra=log_context)
731
732
        # First check the system role permissions
733
        has_system_role_permission = self._user_has_system_role_permission(
734
            user_db=user_db, permission_type=permission_type)
735
736
        if has_system_role_permission:
737
            self._log('Found a matching grant via system role', extra=log_context)
738
            return True
739
740
        # Check custom roles
741
        webhook_uid = resource_db.get_uid()
742
743
        # Check direct grants on the webhook
744
        resource_types = [ResourceType.WEBHOOK]
745
        permission_types = [PermissionType.WEBHOOK_ALL, permission_type]
746
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
747
                                                               resource_uid=webhook_uid,
748
                                                               resource_types=resource_types,
749
                                                               permission_types=permission_types)
750
751
        if len(permission_grants) >= 1:
752
            self._log('Found a grant on the webhook', extra=log_context)
753
            return True
754
755
        self._log('No matching grants found', extra=log_context)
756
        return False
757
758
759
class ApiKeyPermissionResolver(PermissionsResolver):
760
    """
761
    Permission resolver for "api key" resource type.
762
    """
763
764
    resource_type = ResourceType.API_KEY
765
766
    def user_has_permission(self, user_db, permission_type):
767
        assert permission_type in [PermissionType.API_KEY_LIST]
768
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
769
770
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
771
        assert permission_type in [PermissionType.API_KEY_CREATE]
772
        # TODO:
773
        return True
774
775
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
776
        # Desired impl is as under. Permission grants as of now are to
777
        # a role while for this to work right the permission grant would have to
778
        # be to a specific user. The user ofcourse has to belong to a role
779
        # that can create API keys however ALL permissions for specific API Key
780
        # should only be granted if the user created the key or has system role.
781
        # Until we introduce an ability to support per user this is disabled.
782
783
        # log_context = {
784
        #     'user_db': user_db,
785
        #     'resource_db': resource_db,
786
        #     'permission_type': permission_type,
787
        #     'resolver': self.__class__.__name__
788
        # }
789
        # self._log('Checking user resource permissions', extra=log_context)
790
791
        # # First check the system role permissions
792
        # has_system_role_permission = self._user_has_system_role_permission(
793
        #     user_db=user_db, permission_type=permission_type)
794
795
        # if has_system_role_permission:
796
        #     self._log('Found a matching grant via system role', extra=log_context)
797
        #     return True
798
799
        # # Check custom roles
800
        # api_key_uid = resource_db.get_uid()
801
802
        # if permission_type == PermissionType.API_KEY_VIEW:
803
        #     # Note: "create", and "delete" do not grant/imply "view" permission
804
        #     permission_types = [
805
        #         PermissionType.API_KEY_ALL,
806
        #         permission_type
807
        #     ]
808
        # else:
809
        #     permission_types = [PermissionType.ACTION_ALL, permission_type]
810
811
        # # Check direct grants on the specified resource
812
        # resource_types = [ResourceType.API_KEY]
813
        # permission_grants = get_all_permission_grants_for_user(user_db=user_db,
814
        #                                                        resource_uid=api_key_uid,
815
        #                                                        resource_types=resource_types,
816
        #                                                        permission_types=permission_types)
817
        # if len(permission_grants) >= 1:
818
        #     self._log('Found a direct grant on the api_key', extra=log_context)
819
        #     return True
820
821
        # self._log('No matching grants found', extra=log_context)
822
        # return False
823
        return True
824
825
826
def get_resolver_for_resource_type(resource_type):
827
    """
828
    Return resolver instance for the provided resource type.
829
830
    :rtype: Instance of :class:`PermissionsResolver`
831
    """
832
    if resource_type == ResourceType.RUNNER:
833
        resolver_cls = RunnerPermissionsResolver
834
    elif resource_type == ResourceType.PACK:
835
        resolver_cls = PackPermissionsResolver
836
    elif resource_type == ResourceType.SENSOR:
837
        resolver_cls = SensorPermissionsResolver
838
    elif resource_type == ResourceType.ACTION:
839
        resolver_cls = ActionPermissionsResolver
840
    elif resource_type == ResourceType.ACTION_ALIAS:
841
        resolver_cls = ActionAliasPermissionsResolver
842
    elif resource_type == ResourceType.RULE:
843
        resolver_cls = RulePermissionsResolver
844
    elif resource_type == ResourceType.EXECUTION:
845
        resolver_cls = ExecutionPermissionsResolver
846
    elif resource_type == ResourceType.KEY_VALUE_PAIR:
847
        resolver_cls = KeyValuePermissionsResolver
848
    elif resource_type == ResourceType.WEBHOOK:
849
        resolver_cls = WebhookPermissionsResolver
850
    elif resource_type == ResourceType.API_KEY:
851
        resolver_cls = ApiKeyPermissionResolver
852
    elif resource_type == ResourceType.RULE_ENFORCEMENT:
853
        resolver_cls = RuleEnforcementPermissionsResolver
854
    else:
855
        raise ValueError('Unsupported resource: %s' % (resource_type))
856
857
    resolver_instance = resolver_cls()
858
    return resolver_instance
859
860
861
def get_resolver_for_permission_type(permission_type):
862
    """
863
    Return resolver instance for the provided permission type.
864
865
    :rtype: Instance of :class:`PermissionsResolver`
866
    """
867
    resource_type = PermissionType.get_resource_type(permission_type=permission_type)
868
    resolver_instance = get_resolver_for_resource_type(resource_type=resource_type)
869
    return resolver_instance
870