Passed
Push — master ( ac1734...fff02f )
by
unknown
03:05
created

user_has_resource_db_permission()   C

Complexity

Conditions 7

Size

Total Lines 94

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
c 0
b 0
f 0
dl 0
loc 94
rs 5.325

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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.persistence.execution import ActionExecution
30
from st2common.rbac.types import PermissionType
31
from st2common.rbac.types import ResourceType
32
from st2common.rbac.types import SystemRole
33
from st2common.rbac.types import GLOBAL_PACK_PERMISSION_TYPES
34
from st2common.services.rbac import get_roles_for_user
35
from st2common.services.rbac import get_all_permission_grants_for_user
36
37
LOG = logging.getLogger(__name__)
38
39
__all__ = [
40
    'RunnerPermissionsResolver',
41
    'PackPermissionsResolver',
42
    'SensorPermissionsResolver',
43
    'ActionPermissionsResolver',
44
    'ActionAliasPermissionsResolver',
45
    'RulePermissionsResolver',
46
    'RuleEnforcementPermissionsResolver',
47
    'KeyValuePermissionsResolver',
48
    'ExecutionPermissionsResolver',
49
    'WebhookPermissionsResolver',
50
    'TracePermissionsResolver',
51
    'TriggerPermissionsResolver',
52
    'StreamPermissionsResolver',
53
    'InquiryPermissionsResolver',
54
55
    'get_resolver_for_resource_type',
56
    'get_resolver_for_permission_type'
57
]
58
59
# "Read" permission names which are granted to observer role by default
60
READ_PERMISSION_NAMES = [
61
    'view',
62
    'list'
63
]
64
65
66
class PermissionsResolver(object):
67
    """
68
    Base Permissions Resolver class.
69
70
    Permission resolver classes implement permission resolving / checking logic for a particular
71
    resource type.
72
    """
73
74
    resource_type = None  # Constant for the resource type this resolver refers to
75
76
    def user_has_permission(self, user_db, permission_type):
77
        """
78
        Method for checking user permissions which are not tied to a particular resource.
79
        """
80
        raise NotImplementedError()
81
82
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
83
        """
84
        Method for checking user permissions on a resource which is to be created (e.g.
85
        create operation).
86
        """
87
        raise NotImplementedError()
88
89
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
90
        """
91
        Method for checking user permissions on an existing resource (e.g. get one, edit, delete
92
        operations).
93
        """
94
        raise NotImplementedError()
95
96
    def _user_has_list_permission(self, user_db, permission_type):
97
        """
98
        Common method for checking if a user has specific "list" resource permission (e.g.
99
        rules_list, action_list, etc.).
100
        """
101
        assert PermissionType.get_permission_name(permission_type) == 'list'
102
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
103
104
    def _user_has_global_permission(self, user_db, permission_type):
105
        """
106
        Custom method for checking if user has a particular global permission which doesn't apply
107
        to a specific resource but it's system-wide aka global permission.
108
        """
109
        log_context = {
110
            'user_db': user_db,
111
            'permission_type': permission_type,
112
            'resolver': self.__class__.__name__
113
        }
114
        self._log('Checking user permissions', extra=log_context)
115
116
        # First check the system role permissions
117
        has_system_role_permission = self._user_has_system_role_permission(
118
            user_db=user_db, permission_type=permission_type)
119
120
        if has_system_role_permission:
121
            self._log('Found a matching grant via system role', extra=log_context)
122
            return True
123
124
        # Check custom roles
125
        permission_types = [permission_type]
126
127
        # Check direct grants
128
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
129
                                                               permission_types=permission_types)
130
        if len(permission_grants) >= 1:
131
            self._log('Found a direct grant', extra=log_context)
132
            return True
133
134
        self._log('No matching grants found', extra=log_context)
135
        return False
136
137
    def _user_has_system_role_permission(self, user_db, permission_type):
138
        """
139
        Check the user system roles and return True if user has the required permission.
140
141
        :rtype: ``bool``
142
        """
143
        permission_name = PermissionType.get_permission_name(permission_type)
144
145
        user_role_dbs = get_roles_for_user(user_db=user_db)
146
        user_role_names = [role_db.name for role_db in user_role_dbs]
147
148
        if SystemRole.SYSTEM_ADMIN in user_role_names:
149
            # System admin has all the permissions
150
            return True
151
        elif SystemRole.ADMIN in user_role_names:
152
            # Admin has all the permissions
153
            return True
154
        elif SystemRole.OBSERVER in user_role_names and permission_name in READ_PERMISSION_NAMES:
155
            # Observer role has "view" permission on all the resources
156
            return True
157
158
        return False
159
160
    def _matches_permission_grant(self, resource_db, permission_grant, permission_type,
161
                                  all_permission_type):
162
        """
163
        :rtype: ``bool``
164
        """
165
        if permission_type in permission_grant.permission_types:
166
            # Direct permission grant
167
            return True
168
        elif all_permission_type in permission_grant.permission_types:
169
            # "ALL" permission grant
170
            return True
171
172
        return False
173
174
    def _get_all_permission_type_for_resource(self, resource_db):
175
        """
176
        Retrieve "ALL" permission type for the provided resource.
177
        """
178
        resource_type = resource_db.get_resource_type()
179
        permission_type = PermissionType.get_permission_type(resource_type=resource_type,
180
                                                             permission_name='all')
181
        return permission_type
182
183
    def _log(self, message, extra, level=stdlib_logging.DEBUG, **kwargs):
184
        """
185
        Custom logger method which prefix message with the class and caller method name.
186
        """
187
        class_name = self.__class__.__name__
188
        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...
189
        message_prefix = '%s.%s: ' % (class_name, method_name)
190
        message = message_prefix + message
191
192
        LOG.log(level, message, extra=extra, **kwargs)
193
194
195
class ContentPackResourcePermissionsResolver(PermissionsResolver):
196
    """
197
    Base permissions resolver class which contains common functionality for resources which belong
198
    to a pack (sensors, actions, action aliases, rules, ...).
199
    """
200
201
    resource_type = None
202
203
    # A list of resource-specific permission types which grant / imply "view" permission type
204
    view_grant_permission_types = []
205
206
    def _user_has_resource_permission(self, user_db, pack_uid, resource_uid, permission_type):
207
        log_context = {
208
            'user_db': user_db,
209
            'pack_uid': pack_uid,
210
            'resource_uid': resource_uid,
211
            'resource_type': self.resource_type,
212
            'permission_type': permission_type,
213
            'resolver': self.__class__.__name__
214
        }
215
        self._log('Checking user resource permissions', extra=log_context)
216
217
        # First check the system role permissions
218
        self._log('Checking grants via system role permissions', extra=log_context)
219
        has_system_role_permission = self._user_has_system_role_permission(
220
            user_db=user_db, permission_type=permission_type)
221
222
        if has_system_role_permission:
223
            self._log('Found a matching grant via system role', extra=log_context)
224
            return True
225
226
        # Check custom roles
227
        view_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
228
                                                                  permission_name='view')
229
        all_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
230
                                                                 permission_name='all')
231
232
        if permission_type == view_permission_type:
233
            # Note: Some permissions such as "create", "modify", "delete" and "execute" also
234
            # grant / imply "view" permission
235
            permission_types = self.view_grant_permission_types[:] + [permission_type]
236
        elif permission_type not in all_permission_type:
237
            permission_types = [all_permission_type, permission_type]
238
        else:
239
            permission_types = [permission_type]
240
241
        # Check direct grants on the specified resource
242
        self._log('Checking direct grans on the specified resource', extra=log_context)
243
        resource_types = [self.resource_type]
244
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
245
                                                               resource_uid=resource_uid,
246
                                                               resource_types=resource_types,
247
                                                               permission_types=permission_types)
248
        if len(permission_grants) >= 1:
249
            self._log('Found a direct grant on the action', extra=log_context)
250
            return True
251
252
        # Check grants on the parent pack
253
        self._log('Checking grants on the parent resource', extra=log_context)
254
        resource_types = [ResourceType.PACK]
255
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
256
                                                               resource_uid=pack_uid,
257
                                                               resource_types=resource_types,
258
                                                               permission_types=permission_types)
259
260
        if len(permission_grants) >= 1:
261
            self._log('Found a grant on the action parent pack', extra=log_context)
262
            return True
263
264
        self._log('No matching grants found', extra=log_context)
265
        return False
266
267
268
class RunnerPermissionsResolver(PermissionsResolver):
269
    """
270
    Permission resolver for "runner_type" resource type.
271
    """
272
    resource_type = ResourceType.RUNNER
273
274
    def user_has_permission(self, user_db, permission_type):
275
        assert permission_type in [PermissionType.RUNNER_LIST]
276
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
277
278
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
279
        log_context = {
280
            'user_db': user_db,
281
            'resource_db': resource_db,
282
            'permission_type': permission_type,
283
            'resolver': self.__class__.__name__
284
        }
285
        self._log('Checking user resource permissions', extra=log_context)
286
287
        # First check the system role permissions
288
        has_system_role_permission = self._user_has_system_role_permission(
289
            user_db=user_db, permission_type=permission_type)
290
291
        if has_system_role_permission:
292
            self._log('Found a matching grant via system role', extra=log_context)
293
            return True
294
295
        # Check custom roles
296
        resource_uid = resource_db.get_uid()
297
        resource_types = [ResourceType.RUNNER]
298
        permission_types = [permission_type]
299
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
300
                                                               resource_uid=resource_uid,
301
                                                               resource_types=resource_types,
302
                                                               permission_types=permission_types)
303
304
        if len(permission_grants) >= 1:
305
            self._log('Found a direct grant on the runner type', extra=log_context)
306
            return True
307
308
        self._log('No matching grants found', extra=log_context)
309
        return False
310
311
312
class PackPermissionsResolver(PermissionsResolver):
313
    """
314
    Permission resolver for "pack" resource type.
315
    """
316
317
    resource_type = ResourceType.PACK
318
319
    def user_has_permission(self, user_db, permission_type):
320
        assert permission_type in GLOBAL_PACK_PERMISSION_TYPES
321
322
        if permission_type == PermissionType.PACK_LIST:
323
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
324
        else:
325
            return self._user_has_global_permission(user_db=user_db,
326
                                                    permission_type=permission_type)
327
328
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
329
        log_context = {
330
            'user_db': user_db,
331
            'resource_db': resource_db,
332
            'permission_type': permission_type,
333
            'resolver': self.__class__.__name__
334
        }
335
        self._log('Checking user resource permissions', extra=log_context)
336
337
        # First check the system role permissions
338
        has_system_role_permission = self._user_has_system_role_permission(
339
            user_db=user_db, permission_type=permission_type)
340
341
        if has_system_role_permission:
342
            self._log('Found a matching grant via system role', extra=log_context)
343
            return True
344
345
        # Check custom roles
346
        resource_uid = resource_db.get_uid()
347
        resource_types = [ResourceType.PACK]
348
        permission_types = [permission_type]
349
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
350
                                                               resource_uid=resource_uid,
351
                                                               resource_types=resource_types,
352
                                                               permission_types=permission_types)
353
354
        if len(permission_grants) >= 1:
355
            self._log('Found a direct grant on the pack', extra=log_context)
356
            return True
357
358
        self._log('No matching grants found', extra=log_context)
359
        return False
360
361
362
class SensorPermissionsResolver(ContentPackResourcePermissionsResolver):
363
    """
364
    Permission resolver for "sensor" resource type.
365
    """
366
367
    resource_type = ResourceType.SENSOR
368
    view_grant_permission_types = [
369
        PermissionType.SENSOR_ALL,
370
        PermissionType.SENSOR_MODIFY
371
    ]
372
373
    def user_has_permission(self, user_db, permission_type):
374
        assert permission_type in [PermissionType.SENSOR_LIST]
375
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
376
377
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
378
        sensor_uid = resource_db.get_uid()
379
        pack_uid = resource_db.get_pack_uid()
380
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
381
                                                  resource_uid=sensor_uid,
382
                                                  permission_type=permission_type)
383
384
385
class ActionPermissionsResolver(ContentPackResourcePermissionsResolver):
386
    """
387
    Permission resolver for "action" resource type.
388
    """
389
390
    resource_type = ResourceType.ACTION
391
    view_grant_permission_types = [
392
        PermissionType.ACTION_ALL,
393
        PermissionType.ACTION_CREATE,
394
        PermissionType.ACTION_MODIFY,
395
        PermissionType.ACTION_DELETE,
396
        PermissionType.ACTION_EXECUTE,
397
    ]
398
399
    def user_has_permission(self, user_db, permission_type):
400
        assert permission_type in [PermissionType.ACTION_LIST]
401
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
402
403
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
404
        assert permission_type in [PermissionType.ACTION_CREATE]
405
406
        action_uid = resource_api.get_uid()
407
        pack_uid = resource_api.get_pack_uid()
408
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
409
                                                  resource_uid=action_uid,
410
                                                  permission_type=permission_type)
411
412
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
413
        action_uid = resource_db.get_uid()
414
        pack_uid = resource_db.get_pack_uid()
415
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
416
                                                  resource_uid=action_uid,
417
                                                  permission_type=permission_type)
418
419
420
class ActionAliasPermissionsResolver(ContentPackResourcePermissionsResolver):
421
    """
422
    Permission resolver for "action_alias" resource type.
423
    """
424
425
    resource_type = ResourceType.ACTION_ALIAS
426
    view_grant_permission_types = [
427
        PermissionType.ACTION_ALIAS_ALL,
428
        PermissionType.ACTION_ALIAS_CREATE,
429
        PermissionType.ACTION_ALIAS_MODIFY,
430
        PermissionType.ACTION_ALIAS_DELETE
431
    ]
432
433
    def user_has_permission(self, user_db, permission_type):
434
        assert permission_type in [PermissionType.ACTION_ALIAS_LIST,
435
                                   PermissionType.ACTION_ALIAS_MATCH,
436
                                   PermissionType.ACTION_ALIAS_HELP]
437
438
        if permission_type == PermissionType.ACTION_ALIAS_LIST:
439
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
440
        elif permission_type in [PermissionType.ACTION_ALIAS_MATCH,
441
                                 PermissionType.ACTION_ALIAS_HELP]:
442
            return self._user_has_global_permission(user_db=user_db,
443
                                                    permission_type=permission_type)
444
        else:
445
            raise ValueError('Unsupported permission type: %s' % (permission_type))
446
447
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
448
        assert permission_type in [PermissionType.ACTION_ALIAS_CREATE]
449
450
        action_alias_uid = resource_api.get_uid()
451
        pack_uid = resource_api.get_pack_uid()
452
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
453
                                                  resource_uid=action_alias_uid,
454
                                                  permission_type=permission_type)
455
456
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
457
        action_alias_uid = resource_db.get_uid()
458
        pack_uid = resource_db.get_pack_uid()
459
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
460
                                                  resource_uid=action_alias_uid,
461
                                                  permission_type=permission_type)
462
463
464
class RulePermissionsResolver(ContentPackResourcePermissionsResolver):
465
    """
466
    Permission resolver for "rule" resource type.
467
    """
468
469
    resource_type = ResourceType.RULE
470
    view_grant_permission_types = [
471
        PermissionType.RULE_ALL,
472
        PermissionType.RULE_CREATE,
473
        PermissionType.RULE_MODIFY,
474
        PermissionType.RULE_DELETE
475
    ]
476
477
    def user_has_trigger_permission(self, user_db, trigger):
478
        """
479
        Check if the user has access to the provided trigger.
480
481
        This method is to be used during rule create and update where we check if the user has the
482
        necessary trigger permissions.
483
484
        Note: Right now we only support webhook triggers.
485
486
        :param trigger: "trigger" attribute of the RuleAPI object.
487
        :type trigger: ``dict``
488
        """
489
        log_context = {
490
            'user_db': user_db,
491
            'trigger': trigger,
492
            'resolver': self.__class__.__name__
493
        }
494
495
        trigger_type = trigger['type']
496
        trigger_parameters = trigger.get('parameters', {})
497
498
        if trigger_type != WEBHOOK_TRIGGER_TYPE:
499
            self._log('Not a webhook trigger type, ignoring trigger permission checking',
500
                      extra=log_context)
501
            return True
502
503
        resolver = get_resolver_for_resource_type(ResourceType.WEBHOOK)
504
        webhook_db = WebhookDB(name=trigger_parameters['url'])
505
        permission_type = PermissionType.WEBHOOK_CREATE
506
        result = resolver.user_has_resource_db_permission(user_db=user_db,
507
                                                          resource_db=webhook_db,
508
                                                          permission_type=permission_type)
509
510
        if result is True:
511
            self._log('Found a matching trigger grant', extra=log_context)
512
            return True
513
514
        self._log('No matching trigger grants found', extra=log_context)
515
        return False
516
517
    def user_has_action_permission(self, user_db, action_ref):
518
        """
519
        Check if the user has "execute" permission on the provided action.
520
        """
521
        pass
522
523
    def user_has_permission(self, user_db, permission_type):
524
        assert permission_type in [PermissionType.RULE_LIST]
525
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
526
527
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
528
        assert permission_type in [PermissionType.RULE_CREATE]
529
530
        rule_uid = resource_api.get_uid()
531
        pack_uid = resource_api.get_pack_uid()
532
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
533
                                                  resource_uid=rule_uid,
534
                                                  permission_type=permission_type)
535
536
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
537
        rule_uid = resource_db.get_uid()
538
        pack_uid = resource_db.get_pack_uid()
539
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
540
                                                  resource_uid=rule_uid,
541
                                                  permission_type=permission_type)
542
543
544
class RuleEnforcementPermissionsResolver(PermissionsResolver):
545
    """
546
    Permission resolver for "rule enforcement" resource type.
547
    """
548
    resource_type = ResourceType.RULE_ENFORCEMENT
549
550
    def user_has_permission(self, user_db, permission_type):
551
        assert permission_type in [PermissionType.RULE_ENFORCEMENT_LIST]
552
        permission_type = PermissionType.RULE_LIST
553
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
554
555
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
556
        log_context = {
557
            'user_db': user_db,
558
            'resource_db': resource_db,
559
            'permission_type': permission_type,
560
            'resolver': self.__class__.__name__
561
        }
562
        self._log('Checking user resource permissions', extra=log_context)
563
564
        # First check the system role permissions
565
        has_system_role_permission = self._user_has_system_role_permission(
566
            user_db=user_db, permission_type=permission_type)
567
568
        if has_system_role_permission:
569
            self._log('Found a matching grant via system role', extra=log_context)
570
            return True
571
572
        # Check custom roles
573
        rule_spec = getattr(resource_db, 'rule', None)
574
        rule_uid = rule_spec.uid
575
        rule_id = rule_spec.id
576
        rule_pack = ResourceReference.get_pack(rule_spec.ref)
577
578
        if not rule_uid or not rule_id or not rule_pack:
579
            LOG.error('Rule UID or ID or PACK not present in enforcement object. ' +
580
                      ('UID = %s, ID = %s, PACK = %s' % (rule_uid, rule_id, rule_pack)) +
581
                      'Cannot assess access permissions without it. Defaulting to DENY.')
582
            return False
583
584
        # TODO: Add utility methods for constructing uids from parts
585
        pack_db = PackDB(ref=rule_pack)
586
        rule_pack_uid = pack_db.get_uid()
587
588
        rule_permission_type = None
589
        if permission_type == PermissionType.RULE_ENFORCEMENT_VIEW:
590
            rule_permission_type = PermissionType.RULE_VIEW
591
        elif permission_type == PermissionType.RULE_ENFORCEMENT_LIST:
592
            rule_permission_type = PermissionType.RULE_LIST
593
        else:
594
            raise ValueError('Invalid permission type: %s' % (permission_type))
595
596
        permission_types = [PermissionType.RULE_ALL, rule_permission_type]
597
598
        view_permission_type = PermissionType.get_permission_type(resource_type=ResourceType.RULE,
599
                                                                  permission_name='view')
600
601
        if rule_permission_type == view_permission_type:
602
            permission_types = (RulePermissionsResolver.view_grant_permission_types[:] +
603
                                [rule_permission_type])
604
605
        # Check grants on the pack of the rule to which enforcement belongs to
606
        resource_types = [ResourceType.PACK]
607
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
608
                                                               resource_uid=rule_pack_uid,
609
                                                               resource_types=resource_types,
610
                                                               permission_types=permission_types)
611
612
        if len(permission_grants) >= 1:
613
            self._log('Found a grant on the enforcement rule parent pack', extra=log_context)
614
            return True
615
616
        # Check grants on the rule the enforcement belongs to
617
        resource_types = [ResourceType.RULE]
618
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
619
                                                               resource_uid=rule_uid,
620
                                                               resource_types=resource_types,
621
                                                               permission_types=permission_types)
622
623
        if len(permission_grants) >= 1:
624
            self._log('Found a grant on the enforcement\'s rule.', extra=log_context)
625
            return True
626
627
        self._log('No matching grants found', extra=log_context)
628
        return False
629
630
631
class KeyValuePermissionsResolver(PermissionsResolver):
632
    """
633
    Permission resolver for "key value pair" resource type.
634
    """
635
636
    resource_type = ResourceType.KEY_VALUE_PAIR
637
638
    def user_has_permission(self, user_db, permission_type):
639
        # TODO: We don't support assigning permissions on key value pairs yet
640
        return True
641
642
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
643
        # TODO: We don't support assigning permissions on key value pairs yet
644
        return True
645
646
647
class ExecutionPermissionsResolver(PermissionsResolver):
648
    """
649
    Permission resolver for "execution" resource type.
650
    """
651
652
    resource_type = ResourceType.EXECUTION
653
654
    def user_has_permission(self, user_db, permission_type):
655
        assert permission_type in [PermissionType.EXECUTION_LIST,
656
                                   PermissionType.EXECUTION_VIEWS_FILTERS_LIST]
657
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
658
659
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
660
        log_context = {
661
            'user_db': user_db,
662
            'resource_db': resource_db,
663
            'permission_type': permission_type,
664
            'resolver': self.__class__.__name__
665
        }
666
        self._log('Checking user resource permissions', extra=log_context)
667
668
        # First check the system role permissions
669
        has_system_role_permission = self._user_has_system_role_permission(
670
            user_db=user_db, permission_type=permission_type)
671
672
        if has_system_role_permission:
673
            self._log('Found a matching grant via system role', extra=log_context)
674
            return True
675
676
        # Check custom roles
677
        action = resource_db['action']
678
679
        # TODO: Add utility methods for constructing uids from parts
680
        pack_db = PackDB(ref=action['pack'])
681
682
        action_uid = action['uid']
683
        action_pack_uid = pack_db.get_uid()
684
685
        # Note: "action_execute" also grants / implies "execution_re_run" and "execution_stop"
686
        if permission_type == PermissionType.EXECUTION_VIEW:
687
            action_permission_type = PermissionType.ACTION_VIEW
688
        elif permission_type in [PermissionType.EXECUTION_RE_RUN,
689
                                 PermissionType.EXECUTION_STOP]:
690
            action_permission_type = PermissionType.ACTION_EXECUTE
691
        elif permission_type == PermissionType.EXECUTION_ALL:
692
            action_permission_type = PermissionType.ACTION_ALL
693
        elif permission_type == PermissionType.EXECUTION_VIEWS_FILTERS_LIST:
694
            action_permission_type = PermissionType.EXECUTION_VIEWS_FILTERS_LIST
695
        else:
696
            raise ValueError('Invalid permission type: %s' % (permission_type))
697
698
        # Check grants on the pack of the action to which execution belongs to
699
        resource_types = [ResourceType.PACK]
700
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
701
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
702
                                                               resource_uid=action_pack_uid,
703
                                                               resource_types=resource_types,
704
                                                               permission_types=permission_types)
705
706
        if len(permission_grants) >= 1:
707
            self._log('Found a grant on the execution action parent pack', extra=log_context)
708
            return True
709
710
        # Check grants on the action the execution belongs to
711
        resource_types = [ResourceType.ACTION]
712
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
713
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
714
                                                               resource_uid=action_uid,
715
                                                               resource_types=resource_types,
716
                                                               permission_types=permission_types)
717
718
        if len(permission_grants) >= 1:
719
            self._log('Found a grant on the execution action', extra=log_context)
720
            return True
721
722
        self._log('No matching grants found', extra=log_context)
723
        return False
724
725
726
class WebhookPermissionsResolver(PermissionsResolver):
727
728
    resource_type = ResourceType.WEBHOOK
729
730
    def user_has_permission(self, user_db, permission_type):
731
        assert permission_type in [PermissionType.WEBHOOK_LIST]
732
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
733
734
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
735
        log_context = {
736
            'user_db': user_db,
737
            'resource_db': resource_db,
738
            'permission_type': permission_type,
739
            'resolver': self.__class__.__name__
740
        }
741
        self._log('Checking user resource permissions', extra=log_context)
742
743
        # First check the system role permissions
744
        has_system_role_permission = self._user_has_system_role_permission(
745
            user_db=user_db, permission_type=permission_type)
746
747
        if has_system_role_permission:
748
            self._log('Found a matching grant via system role', extra=log_context)
749
            return True
750
751
        # Check custom roles
752
        webhook_uid = resource_db.get_uid()
753
754
        # Check direct grants on the webhook
755
        resource_types = [ResourceType.WEBHOOK]
756
        permission_types = [PermissionType.WEBHOOK_ALL, permission_type]
757
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
758
                                                               resource_uid=webhook_uid,
759
                                                               resource_types=resource_types,
760
                                                               permission_types=permission_types)
761
762
        if len(permission_grants) >= 1:
763
            self._log('Found a grant on the webhook', extra=log_context)
764
            return True
765
766
        self._log('No matching grants found', extra=log_context)
767
        return False
768
769
770
class TimerPermissionsResolver(PermissionsResolver):
771
    """
772
    Permission resolver for timers (timers are just a special type of triggers).
773
    """
774
775
    resource_type = ResourceType.TIMER
776
777
    def user_has_permission(self, user_db, permission_type):
778
        assert permission_type in [PermissionType.TIMER_LIST]
779
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
780
781
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
782
        log_context = {
783
            'user_db': user_db,
784
            'resource_db': resource_db,
785
            'permission_type': permission_type,
786
            'resolver': self.__class__.__name__
787
        }
788
        self._log('Checking user resource permissions', extra=log_context)
789
790
        # First check the system role permissions
791
        has_system_role_permission = self._user_has_system_role_permission(
792
            user_db=user_db, permission_type=permission_type)
793
794
        if has_system_role_permission:
795
            self._log('Found a matching grant via system role', extra=log_context)
796
            return True
797
798
        # Check custom roles
799
        timer_uid = resource_db.get_uid()
800
801
        # Check direct grants on the webhook
802
        resource_types = [ResourceType.TIMER]
803
        permission_types = [PermissionType.TIMER_ALL, permission_type]
804
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
805
                                                               resource_uid=timer_uid,
806
                                                               resource_types=resource_types,
807
                                                               permission_types=permission_types)
808
809
        if len(permission_grants) >= 1:
810
            self._log('Found a grant on the timer', extra=log_context)
811
            return True
812
813
        self._log('No matching grants found', extra=log_context)
814
        return False
815
816
817
class ApiKeyPermissionResolver(PermissionsResolver):
818
    """
819
    Permission resolver for "api key" resource type.
820
    """
821
822
    resource_type = ResourceType.API_KEY
823
824
    def user_has_permission(self, user_db, permission_type):
825
        assert permission_type in [PermissionType.API_KEY_LIST]
826
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
827
828
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
829
        assert permission_type in [PermissionType.API_KEY_CREATE]
830
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
831
832
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
833
        log_context = {
834
            'user_db': user_db,
835
            'resource_db': resource_db,
836
            'permission_type': permission_type,
837
            'resolver': self.__class__.__name__
838
        }
839
        self._log('Checking user resource permissions', extra=log_context)
840
841
        # First check the system role permissions
842
        has_system_role_permission = self._user_has_system_role_permission(
843
            user_db=user_db, permission_type=permission_type)
844
845
        if has_system_role_permission:
846
            self._log('Found a matching grant via system role', extra=log_context)
847
            return True
848
849
        # Check custom roles
850
        api_key_uid = resource_db.get_uid()
851
852
        # Check direct grants on the webhook
853
        resource_types = [ResourceType.API_KEY]
854
        permission_types = [PermissionType.API_KEY_ALL, permission_type]
855
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
856
                                                               resource_uid=api_key_uid,
857
                                                               resource_types=resource_types,
858
                                                               permission_types=permission_types)
859
860
        if len(permission_grants) >= 1:
861
            self._log('Found a grant on the api key', extra=log_context)
862
            return True
863
864
        self._log('No matching grants found', extra=log_context)
865
        return False
866
867
868
class TracePermissionsResolver(PermissionsResolver):
869
    """
870
    Permission resolver for "trace" resource type.
871
    """
872
873
    resource_type = ResourceType.TRACE
874
875
    def user_has_permission(self, user_db, permission_type):
876
        assert permission_type in [PermissionType.TRACE_LIST]
877
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
878
879
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
880
        log_context = {
881
            'user_db': user_db,
882
            'resource_db': resource_db,
883
            'permission_type': permission_type,
884
            'resolver': self.__class__.__name__
885
        }
886
        self._log('Checking user resource permissions', extra=log_context)
887
888
        # First check the system role permissions
889
        has_system_role_permission = self._user_has_system_role_permission(
890
            user_db=user_db, permission_type=permission_type)
891
892
        if has_system_role_permission:
893
            self._log('Found a matching grant via system role', extra=log_context)
894
            return True
895
896
        # Check custom roles
897
        trace_uid = resource_db.get_uid()
898
899
        # Check direct grants on the webhook
900
        resource_types = [ResourceType.TRACE]
901
        permission_types = [PermissionType.TRACE_ALL, permission_type]
902
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
903
                                                               resource_uid=trace_uid,
904
                                                               resource_types=resource_types,
905
                                                               permission_types=permission_types)
906
907
        if len(permission_grants) >= 1:
908
            self._log('Found a grant on the trace', extra=log_context)
909
            return True
910
911
        self._log('No matching grants found', extra=log_context)
912
        return False
913
914
915
class TriggerPermissionsResolver(PermissionsResolver):
916
    """
917
    Permission resolver for trigger and timers (timers are just a special type of triggers).
918
    """
919
920
    resource_type = ResourceType.TRIGGER
921
922
    def user_has_permission(self, user_db, permission_type):
923
        assert permission_type in [PermissionType.TRIGGER_LIST]
924
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
925
926
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
927
        log_context = {
928
            'user_db': user_db,
929
            'resource_db': resource_db,
930
            'permission_type': permission_type,
931
            'resolver': self.__class__.__name__
932
        }
933
        self._log('Checking user resource permissions', extra=log_context)
934
935
        # First check the system role permissions
936
        has_system_role_permission = self._user_has_system_role_permission(
937
            user_db=user_db, permission_type=permission_type)
938
939
        if has_system_role_permission:
940
            self._log('Found a matching grant via system role', extra=log_context)
941
            return True
942
943
        # Check custom roles
944
        timer_uid = resource_db.get_uid()
945
946
        # Check direct grants on the webhook
947
        resource_types = [ResourceType.TRIGGER]
948
        permission_types = [PermissionType.TRIGGER_ALL, permission_type]
949
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
950
                                                               resource_uid=timer_uid,
951
                                                               resource_types=resource_types,
952
                                                               permission_types=permission_types)
953
954
        if len(permission_grants) >= 1:
955
            self._log('Found a grant on the timer', extra=log_context)
956
            return True
957
958
        self._log('No matching grants found', extra=log_context)
959
        return False
960
961
962
class PolicyTypePermissionsResolver(PermissionsResolver):
963
    """
964
    Permission resolver for "policy type" resource.
965
    """
966
967
    resource_type = ResourceType.POLICY_TYPE
968
969
    def user_has_permission(self, user_db, permission_type):
970
        assert permission_type in [PermissionType.POLICY_TYPE_LIST]
971
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
972
973
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
974
        log_context = {
975
            'user_db': user_db,
976
            'resource_db': resource_db,
977
            'permission_type': permission_type,
978
            'resolver': self.__class__.__name__
979
        }
980
        self._log('Checking user resource permissions', extra=log_context)
981
982
        # First check the system role permissions
983
        has_system_role_permission = self._user_has_system_role_permission(
984
            user_db=user_db, permission_type=permission_type)
985
986
        if has_system_role_permission:
987
            self._log('Found a matching grant via system role', extra=log_context)
988
            return True
989
990
        # Check custom roles
991
        policy_type_uid = resource_db.get_uid()
992
993
        # Check direct grants on the webhook
994
        resource_types = [ResourceType.POLICY_TYPE]
995
        permission_types = [PermissionType.POLICY_TYPE_ALL, permission_type]
996
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
997
                                                               resource_uid=policy_type_uid,
998
                                                               resource_types=resource_types,
999
                                                               permission_types=permission_types)
1000
1001
        if len(permission_grants) >= 1:
1002
            self._log('Found a grant on the policy type', extra=log_context)
1003
            return True
1004
1005
        self._log('No matching grants found', extra=log_context)
1006
        return False
1007
1008
1009
class PolicyPermissionsResolver(ContentPackResourcePermissionsResolver):
1010
    """
1011
    Permission resolver for "policy" resource type.
1012
    """
1013
1014
    resource_type = ResourceType.POLICY
1015
    view_grant_permission_types = [
1016
        PermissionType.POLICY_ALL,
1017
        PermissionType.POLICY_CREATE,
1018
        PermissionType.POLICY_MODIFY,
1019
        PermissionType.POLICY_DELETE
1020
    ]
1021
1022
    def user_has_permission(self, user_db, permission_type):
1023
        assert permission_type in [PermissionType.POLICY_LIST]
1024
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
1025
1026
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
1027
        assert permission_type in [PermissionType.POLICY_CREATE]
1028
1029
        policy_uid = resource_api.get_uid()
1030
        pack_uid = resource_api.get_pack_uid()
1031
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
1032
                                                  resource_uid=policy_uid,
1033
                                                  permission_type=permission_type)
1034
1035
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
1036
        policy_uid = resource_db.get_uid()
1037
        pack_uid = resource_db.get_pack_uid()
1038
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
1039
                                                  resource_uid=policy_uid,
1040
                                                  permission_type=permission_type)
1041
1042
1043
class StreamPermissionsResolver(PermissionsResolver):
1044
    resource_type = ResourceType.STREAM
1045
    view_grant_permission_types = []
1046
1047
    def user_has_permission(self, user_db, permission_type):
1048
        assert permission_type in [PermissionType.STREAM_VIEW]
1049
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
1050
1051
1052
class InquiryPermissionsResolver(PermissionsResolver):
1053
    resource_type = ResourceType.INQUIRY
1054
    view_grant_permission_types = [
1055
        PermissionType.INQUIRY_LIST,
1056
        PermissionType.INQUIRY_VIEW,
1057
        PermissionType.INQUIRY_RESPOND,
1058
        PermissionType.INQUIRY_ALL
1059
    ]
1060
1061
    def user_has_permission(self, user_db, permission_type):
1062
        assert permission_type in [PermissionType.INQUIRY_LIST, PermissionType.INQUIRY_ALL]
1063
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
1064
1065
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
1066
        """
1067
        Method for checking user permissions on an existing resource (e.g. get one, edit, delete
1068
        operations).
1069
1070
        NOTE:
1071
        Because we're borrowing the ActionExecutionDB model, the resource_db parameter is
1072
        effectively ignored. All other filters are passed to get_all_permission_grants_for_user.
1073
        Since all Inquiry permission types are global, this will still correctly return a list of
1074
        grants.
1075
        """
1076
1077
        permission_types = [
1078
            PermissionType.INQUIRY_VIEW,
1079
            PermissionType.INQUIRY_RESPOND,
1080
            PermissionType.INQUIRY_ALL
1081
        ]
1082
1083
        assert permission_type in permission_types
1084
1085
        log_context = {
1086
            'user_db': user_db,
1087
            'resource_db': resource_db,
1088
            'permission_type': permission_type,
1089
            'resolver': self.__class__.__name__
1090
        }
1091
        self._log('Checking user resource permissions', extra=log_context)
1092
1093
        # First check the system role permissions
1094
        has_system_role_permission = self._user_has_system_role_permission(
1095
            user_db=user_db, permission_type=permission_type)
1096
1097
        if has_system_role_permission:
1098
            self._log('Found a matching grant via system role', extra=log_context)
1099
            return True
1100
1101
        # Check for explicit Inquiry grants first
1102
        resource_types = [ResourceType.INQUIRY]
1103
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
1104
                                                               resource_types=resource_types,
1105
                                                               permission_types=permission_types)
1106
1107
        if len(permission_grants) >= 1:
1108
            self._log('Found a grant on the inquiry', extra=log_context)
1109
            return True
1110
1111
        # If the inquiry has a parent (is in a workflow) we want to
1112
        # check permissions of the parent action and pack, and inherit
1113
        # if applicable
1114
        if resource_db.parent:
1115
1116
            # Retrieve objects for parent workflow action and pack
1117
            wf_exc = ActionExecution.get(id=resource_db.parent)
1118
            wf_action = wf_exc['action']
1119
            # TODO: Add utility methods for constructing uids from parts
1120
            wf_pack_db = PackDB(ref=wf_action['pack'])
1121
            wf_action_uid = wf_action['uid']
1122
            wf_action_pack_uid = wf_pack_db.get_uid()
1123
1124
            # Check grants on the pack of the workflow that the Inquiry was generated from
1125
            resource_types = [ResourceType.PACK]
1126
            permission_types = [PermissionType.ACTION_ALL, PermissionType.ACTION_EXECUTE]
1127
            permission_grants = get_all_permission_grants_for_user(
1128
                user_db=user_db,
1129
                resource_uid=wf_action_pack_uid,
1130
                resource_types=resource_types,
1131
                permission_types=permission_types
1132
            )
1133
1134
            if len(permission_grants) >= 1:
1135
                log_context['wf_action_pack_uid'] = wf_action_pack_uid
1136
                self._log(
1137
                    'Found a grant on the parent pack for an inquiry workflow',
1138
                    extra=log_context
1139
                )
1140
                return True
1141
1142
            # Check grants on the workflow that the Inquiry was generated from
1143
            resource_types = [ResourceType.ACTION]
1144
            permission_types = [PermissionType.ACTION_ALL, PermissionType.ACTION_EXECUTE]
1145
            permission_grants = get_all_permission_grants_for_user(
1146
                user_db=user_db,
1147
                resource_uid=wf_action_uid,
1148
                resource_types=resource_types,
1149
                permission_types=permission_types
1150
            )
1151
1152
            if len(permission_grants) >= 1:
1153
                log_context['wf_action_uid'] = wf_action_uid
1154
                self._log('Found a grant on the inquiry workflow', extra=log_context)
1155
                return True
1156
1157
        self._log('No matching grants found', extra=log_context)
1158
        return False
1159
1160
1161
def get_resolver_for_resource_type(resource_type):
1162
    """
1163
    Return resolver instance for the provided resource type.
1164
1165
    :rtype: Instance of :class:`PermissionsResolver`
1166
    """
1167
    if resource_type == ResourceType.RUNNER:
1168
        resolver_cls = RunnerPermissionsResolver
1169
    elif resource_type == ResourceType.PACK:
1170
        resolver_cls = PackPermissionsResolver
1171
    elif resource_type == ResourceType.SENSOR:
1172
        resolver_cls = SensorPermissionsResolver
1173
    elif resource_type == ResourceType.ACTION:
1174
        resolver_cls = ActionPermissionsResolver
1175
    elif resource_type == ResourceType.ACTION_ALIAS:
1176
        resolver_cls = ActionAliasPermissionsResolver
1177
    elif resource_type == ResourceType.RULE:
1178
        resolver_cls = RulePermissionsResolver
1179
    elif resource_type == ResourceType.EXECUTION:
1180
        resolver_cls = ExecutionPermissionsResolver
1181
    elif resource_type == ResourceType.KEY_VALUE_PAIR:
1182
        resolver_cls = KeyValuePermissionsResolver
1183
    elif resource_type == ResourceType.WEBHOOK:
1184
        resolver_cls = WebhookPermissionsResolver
1185
    elif resource_type == ResourceType.TIMER:
1186
        resolver_cls = TimerPermissionsResolver
1187
    elif resource_type == ResourceType.API_KEY:
1188
        resolver_cls = ApiKeyPermissionResolver
1189
    elif resource_type == ResourceType.RULE_ENFORCEMENT:
1190
        resolver_cls = RuleEnforcementPermissionsResolver
1191
    elif resource_type == ResourceType.TRACE:
1192
        resolver_cls = TracePermissionsResolver
1193
    elif resource_type == ResourceType.TRIGGER:
1194
        resolver_cls = TriggerPermissionsResolver
1195
    elif resource_type == ResourceType.POLICY_TYPE:
1196
        resolver_cls = PolicyTypePermissionsResolver
1197
    elif resource_type == ResourceType.POLICY:
1198
        resolver_cls = PolicyPermissionsResolver
1199
    elif resource_type == ResourceType.STREAM:
1200
        resolver_cls = StreamPermissionsResolver
1201
    elif resource_type == ResourceType.INQUIRY:
1202
        resolver_cls = InquiryPermissionsResolver
1203
    else:
1204
        raise ValueError('Unsupported resource: %s' % (resource_type))
1205
1206
    resolver_instance = resolver_cls()
1207
    return resolver_instance
1208
1209
1210
def get_resolver_for_permission_type(permission_type):
1211
    """
1212
    Return resolver instance for the provided permission type.
1213
1214
    :rtype: Instance of :class:`PermissionsResolver`
1215
    """
1216
    resource_type = PermissionType.get_resource_type(permission_type=permission_type)
1217
    resolver_instance = get_resolver_for_resource_type(resource_type=resource_type)
1218
    return resolver_instance
1219