Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

st2common/st2common/rbac/resolvers.py (1 issue)

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