Passed
Push — master ( 1043ed...a36ace )
by W
07:49
created

user_has_resource_db_permission()   B

Complexity

Conditions 3

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
dl 0
loc 34
rs 8.8571
c 1
b 0
f 0
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
"""
17
Module containing resolver classes which contain permission resolving logic for different resource
18
types.
19
"""
20
21
import sys
22
import logging as stdlib_logging
23
24
from st2common import log as logging
25
from st2common.models.db.pack import PackDB
26
from st2common.models.db.webhook import WebhookDB
27
from st2common.models.system.common import ResourceReference
28
from st2common.constants.triggers import WEBHOOK_TRIGGER_TYPE
29
from st2common.rbac.types import PermissionType
30
from st2common.rbac.types import ResourceType
31
from st2common.rbac.types import SystemRole
32
from st2common.rbac.types import GLOBAL_PACK_PERMISSION_TYPES
33
from st2common.services.rbac import get_roles_for_user
34
from st2common.services.rbac import get_all_permission_grants_for_user
35
36
LOG = logging.getLogger(__name__)
37
38
__all__ = [
39
    'RunnerPermissionsResolver',
40
    'PackPermissionsResolver',
41
    'SensorPermissionsResolver',
42
    'ActionPermissionsResolver',
43
    'ActionAliasPermissionsResolver',
44
    'RulePermissionsResolver',
45
    'RuleEnforcementPermissionsResolver',
46
    'KeyValuePermissionsResolver',
47
    'ExecutionPermissionsResolver',
48
    'WebhookPermissionsResolver',
49
    'TracePermissionsResolver',
50
    'TriggerPermissionsResolver',
51
52
    'get_resolver_for_resource_type',
53
    'get_resolver_for_permission_type'
54
]
55
56
# "Read" permission names which are granted to observer role by default
57
READ_PERMISSION_NAMES = [
58
    'view',
59
    'list'
60
]
61
62
63
class PermissionsResolver(object):
64
    """
65
    Base Permissions Resolver class.
66
67
    Permission resolver classes implement permission resolving / checking logic for a particular
68
    resource type.
69
    """
70
71
    resource_type = None  # Constant for the resource type this resolver refers to
72
73
    def user_has_permission(self, user_db, permission_type):
74
        """
75
        Method for checking user permissions which are not tied to a particular resource.
76
        """
77
        raise NotImplementedError()
78
79
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
80
        """
81
        Method for checking user permissions on a resource which is to be created (e.g.
82
        create operation).
83
        """
84
        raise NotImplementedError()
85
86
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
87
        """
88
        Method for checking user permissions on an existing resource (e.g. get one, edit, delete
89
        operations).
90
        """
91
        raise NotImplementedError()
92
93
    def _user_has_list_permission(self, user_db, permission_type):
94
        """
95
        Common method for checking if a user has specific "list" resource permission (e.g.
96
        rules_list, action_list, etc.).
97
        """
98
        assert PermissionType.get_permission_name(permission_type) == 'list'
99
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
100
101
    def _user_has_global_permission(self, user_db, permission_type):
102
        """
103
        Custom method for checking if user has a particular global permission which doesn't apply
104
        to a specific resource but it's system-wide aka global permission.
105
        """
106
        log_context = {
107
            'user_db': user_db,
108
            'permission_type': permission_type,
109
            'resolver': self.__class__.__name__
110
        }
111
        self._log('Checking user permissions', extra=log_context)
112
113
        # First check the system role permissions
114
        has_system_role_permission = self._user_has_system_role_permission(
115
            user_db=user_db, permission_type=permission_type)
116
117
        if has_system_role_permission:
118
            self._log('Found a matching grant via system role', extra=log_context)
119
            return True
120
121
        # Check custom roles
122
        permission_types = [permission_type]
123
124
        # Check direct grants
125
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
126
                                                               permission_types=permission_types)
127
        if len(permission_grants) >= 1:
128
            self._log('Found a direct grant', extra=log_context)
129
            return True
130
131
        self._log('No matching grants found', extra=log_context)
132
        return False
133
134
    def _user_has_system_role_permission(self, user_db, permission_type):
135
        """
136
        Check the user system roles and return True if user has the required permission.
137
138
        :rtype: ``bool``
139
        """
140
        permission_name = PermissionType.get_permission_name(permission_type)
141
142
        user_role_dbs = get_roles_for_user(user_db=user_db)
143
        user_role_names = [role_db.name for role_db in user_role_dbs]
144
145
        if SystemRole.SYSTEM_ADMIN in user_role_names:
146
            # System admin has all the permissions
147
            return True
148
        elif SystemRole.ADMIN in user_role_names:
149
            # Admin has all the permissions
150
            return True
151
        elif SystemRole.OBSERVER in user_role_names and permission_name in READ_PERMISSION_NAMES:
152
            # Observer role has "view" permission on all the resources
153
            return True
154
155
        return False
156
157
    def _matches_permission_grant(self, resource_db, permission_grant, permission_type,
158
                                  all_permission_type):
159
        """
160
        :rtype: ``bool``
161
        """
162
        if permission_type in permission_grant.permission_types:
163
            # Direct permission grant
164
            return True
165
        elif all_permission_type in permission_grant.permission_types:
166
            # "ALL" permission grant
167
            return True
168
169
        return False
170
171
    def _get_all_permission_type_for_resource(self, resource_db):
172
        """
173
        Retrieve "ALL" permission type for the provided resource.
174
        """
175
        resource_type = resource_db.get_resource_type()
176
        permission_type = PermissionType.get_permission_type(resource_type=resource_type,
177
                                                             permission_name='all')
178
        return permission_type
179
180
    def _log(self, message, extra, level=stdlib_logging.DEBUG, **kwargs):
181
        """
182
        Custom logger method which prefix message with the class and caller method name.
183
        """
184
        class_name = self.__class__.__name__
185
        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...
186
        message_prefix = '%s.%s: ' % (class_name, method_name)
187
        message = message_prefix + message
188
189
        LOG.log(level, message, extra=extra, **kwargs)
190
191
192
class ContentPackResourcePermissionsResolver(PermissionsResolver):
193
    """
194
    Base permissions resolver class which contains common functionality for resources which belong
195
    to a pack (sensors, actions, action aliases, rules, ...).
196
    """
197
198
    resource_type = None
199
200
    # A list of resource-specific permission types which grant / imply "view" permission type
201
    view_grant_permission_types = []
202
203
    def _user_has_resource_permission(self, user_db, pack_uid, resource_uid, permission_type):
204
        log_context = {
205
            'user_db': user_db,
206
            'pack_uid': pack_uid,
207
            'resource_uid': resource_uid,
208
            'resource_type': self.resource_type,
209
            'permission_type': permission_type,
210
            'resolver': self.__class__.__name__
211
        }
212
        self._log('Checking user resource permissions', extra=log_context)
213
214
        # First check the system role permissions
215
        has_system_role_permission = self._user_has_system_role_permission(
216
            user_db=user_db, permission_type=permission_type)
217
218
        if has_system_role_permission:
219
            self._log('Found a matching grant via system role', extra=log_context)
220
            return True
221
222
        # Check custom roles
223
        view_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
224
                                                                  permission_name='view')
225
        all_permission_type = PermissionType.get_permission_type(resource_type=self.resource_type,
226
                                                                 permission_name='all')
227
228
        if permission_type == view_permission_type:
229
            # Note: Some permissions such as "create", "modify", "delete" and "execute" also
230
            # grant / imply "view" permission
231
            permission_types = self.view_grant_permission_types[:] + [permission_type]
232
        elif permission_type not in all_permission_type:
233
            permission_types = [all_permission_type, permission_type]
234
        else:
235
            permission_types = [permission_type]
236
237
        # Check direct grants on the specified resource
238
        resource_types = [self.resource_type]
239
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
240
                                                               resource_uid=resource_uid,
241
                                                               resource_types=resource_types,
242
                                                               permission_types=permission_types)
243
        if len(permission_grants) >= 1:
244
            self._log('Found a direct grant on the action', extra=log_context)
245
            return True
246
247
        # Check grants on the parent pack
248
        resource_types = [ResourceType.PACK]
249
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
250
                                                               resource_uid=pack_uid,
251
                                                               resource_types=resource_types,
252
                                                               permission_types=permission_types)
253
254
        if len(permission_grants) >= 1:
255
            self._log('Found a grant on the action parent pack', extra=log_context)
256
            return True
257
258
        self._log('No matching grants found', extra=log_context)
259
        return False
260
261
262
class RunnerPermissionsResolver(PermissionsResolver):
263
    """
264
    Permission resolver for "runner_type" resource type.
265
    """
266
    resource_type = ResourceType.RUNNER
267
268
    def user_has_permission(self, user_db, permission_type):
269
        assert permission_type in [PermissionType.RUNNER_LIST]
270
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
271
272
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
273
        log_context = {
274
            'user_db': user_db,
275
            'resource_db': resource_db,
276
            'permission_type': permission_type,
277
            'resolver': self.__class__.__name__
278
        }
279
        self._log('Checking user resource permissions', extra=log_context)
280
281
        # First check the system role permissions
282
        has_system_role_permission = self._user_has_system_role_permission(
283
            user_db=user_db, permission_type=permission_type)
284
285
        if has_system_role_permission:
286
            self._log('Found a matching grant via system role', extra=log_context)
287
            return True
288
289
        # Check custom roles
290
        resource_uid = resource_db.get_uid()
291
        resource_types = [ResourceType.RUNNER]
292
        permission_types = [permission_type]
293
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
294
                                                               resource_uid=resource_uid,
295
                                                               resource_types=resource_types,
296
                                                               permission_types=permission_types)
297
298
        if len(permission_grants) >= 1:
299
            self._log('Found a direct grant on the runner type', extra=log_context)
300
            return True
301
302
        self._log('No matching grants found', extra=log_context)
303
        return False
304
305
306
class PackPermissionsResolver(PermissionsResolver):
307
    """
308
    Permission resolver for "pack" resource type.
309
    """
310
311
    resource_type = ResourceType.PACK
312
313
    def user_has_permission(self, user_db, permission_type):
314
        assert permission_type in GLOBAL_PACK_PERMISSION_TYPES
315
316
        if permission_type == PermissionType.PACK_LIST:
317
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
318
        else:
319
            return self._user_has_global_permission(user_db=user_db,
320
                                                    permission_type=permission_type)
321
322
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
323
        log_context = {
324
            'user_db': user_db,
325
            'resource_db': resource_db,
326
            'permission_type': permission_type,
327
            'resolver': self.__class__.__name__
328
        }
329
        self._log('Checking user resource permissions', extra=log_context)
330
331
        # First check the system role permissions
332
        has_system_role_permission = self._user_has_system_role_permission(
333
            user_db=user_db, permission_type=permission_type)
334
335
        if has_system_role_permission:
336
            self._log('Found a matching grant via system role', extra=log_context)
337
            return True
338
339
        # Check custom roles
340
        resource_uid = resource_db.get_uid()
341
        resource_types = [ResourceType.PACK]
342
        permission_types = [permission_type]
343
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
344
                                                               resource_uid=resource_uid,
345
                                                               resource_types=resource_types,
346
                                                               permission_types=permission_types)
347
348
        if len(permission_grants) >= 1:
349
            self._log('Found a direct grant on the pack', extra=log_context)
350
            return True
351
352
        self._log('No matching grants found', extra=log_context)
353
        return False
354
355
356
class SensorPermissionsResolver(ContentPackResourcePermissionsResolver):
357
    """
358
    Permission resolver for "sensor" resource type.
359
    """
360
361
    resource_type = ResourceType.SENSOR
362
    view_grant_permission_types = [
363
        PermissionType.SENSOR_ALL,
364
        PermissionType.SENSOR_MODIFY
365
    ]
366
367
    def user_has_permission(self, user_db, permission_type):
368
        assert permission_type in [PermissionType.SENSOR_LIST]
369
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
370
371
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
372
        sensor_uid = resource_db.get_uid()
373
        pack_uid = resource_db.get_pack_uid()
374
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
375
                                                  resource_uid=sensor_uid,
376
                                                  permission_type=permission_type)
377
378
379
class ActionPermissionsResolver(ContentPackResourcePermissionsResolver):
380
    """
381
    Permission resolver for "action" resource type.
382
    """
383
384
    resource_type = ResourceType.ACTION
385
    view_grant_permission_types = [
386
        PermissionType.ACTION_ALL,
387
        PermissionType.ACTION_CREATE,
388
        PermissionType.ACTION_MODIFY,
389
        PermissionType.ACTION_DELETE,
390
        PermissionType.ACTION_EXECUTE,
391
    ]
392
393
    def user_has_permission(self, user_db, permission_type):
394
        assert permission_type in [PermissionType.ACTION_LIST]
395
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
396
397
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
398
        assert permission_type in [PermissionType.ACTION_CREATE]
399
400
        action_uid = resource_api.get_uid()
401
        pack_uid = resource_api.get_pack_uid()
402
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
403
                                                  resource_uid=action_uid,
404
                                                  permission_type=permission_type)
405
406
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
407
        action_uid = resource_db.get_uid()
408
        pack_uid = resource_db.get_pack_uid()
409
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
410
                                                  resource_uid=action_uid,
411
                                                  permission_type=permission_type)
412
413
414
class ActionAliasPermissionsResolver(ContentPackResourcePermissionsResolver):
415
    """
416
    Permission resolver for "action_alias" resource type.
417
    """
418
419
    resource_type = ResourceType.ACTION_ALIAS
420
    view_grant_permission_types = [
421
        PermissionType.ACTION_ALIAS_ALL,
422
        PermissionType.ACTION_ALIAS_CREATE,
423
        PermissionType.ACTION_ALIAS_MODIFY,
424
        PermissionType.ACTION_ALIAS_DELETE
425
    ]
426
427
    def user_has_permission(self, user_db, permission_type):
428
        assert permission_type in [PermissionType.ACTION_ALIAS_LIST,
429
                                   PermissionType.ACTION_ALIAS_MATCH,
430
                                   PermissionType.ACTION_ALIAS_HELP]
431
432
        if permission_type == PermissionType.ACTION_ALIAS_LIST:
433
            return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
434
        elif permission_type in [PermissionType.ACTION_ALIAS_MATCH,
435
                                 PermissionType.ACTION_ALIAS_HELP]:
436
            return self._user_has_global_permission(user_db=user_db,
437
                                                    permission_type=permission_type)
438
        else:
439
            raise ValueError('Unsupported permission type: %s' % (permission_type))
440
441
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
442
        assert permission_type in [PermissionType.ACTION_ALIAS_CREATE]
443
444
        action_alias_uid = resource_api.get_uid()
445
        pack_uid = resource_api.get_pack_uid()
446
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
447
                                                  resource_uid=action_alias_uid,
448
                                                  permission_type=permission_type)
449
450
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
451
        action_alias_uid = resource_db.get_uid()
452
        pack_uid = resource_db.get_pack_uid()
453
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
454
                                                  resource_uid=action_alias_uid,
455
                                                  permission_type=permission_type)
456
457
458
class RulePermissionsResolver(ContentPackResourcePermissionsResolver):
459
    """
460
    Permission resolver for "rule" resource type.
461
    """
462
463
    resource_type = ResourceType.RULE
464
    view_grant_permission_types = [
465
        PermissionType.RULE_ALL,
466
        PermissionType.RULE_CREATE,
467
        PermissionType.RULE_MODIFY,
468
        PermissionType.RULE_DELETE
469
    ]
470
471
    def user_has_trigger_permission(self, user_db, trigger):
472
        """
473
        Check if the user has access to the provided trigger.
474
475
        This method is to be used during rule create and update where we check if the user has the
476
        necessary trigger permissions.
477
478
        Note: Right now we only support webhook triggers.
479
480
        :param trigger: "trigger" attribute of the RuleAPI object.
481
        :type trigger: ``dict``
482
        """
483
        log_context = {
484
            'user_db': user_db,
485
            'trigger': trigger,
486
            'resolver': self.__class__.__name__
487
        }
488
489
        trigger_type = trigger['type']
490
        trigger_parameters = trigger.get('parameters', {})
491
492
        if trigger_type != WEBHOOK_TRIGGER_TYPE:
493
            self._log('Not a webhook trigger type, ignoring trigger permission checking',
494
                      extra=log_context)
495
            return True
496
497
        resolver = get_resolver_for_resource_type(ResourceType.WEBHOOK)
498
        webhook_db = WebhookDB(name=trigger_parameters['url'])
499
        permission_type = PermissionType.WEBHOOK_CREATE
500
        result = resolver.user_has_resource_db_permission(user_db=user_db,
501
                                                          resource_db=webhook_db,
502
                                                          permission_type=permission_type)
503
504
        if result is True:
505
            self._log('Found a matching trigger grant', extra=log_context)
506
            return True
507
508
        self._log('No matching trigger grants found', extra=log_context)
509
        return False
510
511
    def user_has_action_permission(self, user_db, action_ref):
512
        """
513
        Check if the user has "execute" permission on the provided action.
514
        """
515
        pass
516
517
    def user_has_permission(self, user_db, permission_type):
518
        assert permission_type in [PermissionType.RULE_LIST]
519
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
520
521
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
522
        assert permission_type in [PermissionType.RULE_CREATE]
523
524
        rule_uid = resource_api.get_uid()
525
        pack_uid = resource_api.get_pack_uid()
526
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
527
                                                  resource_uid=rule_uid,
528
                                                  permission_type=permission_type)
529
530
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
531
        rule_uid = resource_db.get_uid()
532
        pack_uid = resource_db.get_pack_uid()
533
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
534
                                                  resource_uid=rule_uid,
535
                                                  permission_type=permission_type)
536
537
538
class RuleEnforcementPermissionsResolver(PermissionsResolver):
539
    """
540
    Permission resolver for "rule enforcement" resource type.
541
    """
542
    resource_type = ResourceType.RULE_ENFORCEMENT
543
544
    def user_has_permission(self, user_db, permission_type):
545
        assert permission_type in [PermissionType.RULE_ENFORCEMENT_LIST]
546
        permission_type = PermissionType.RULE_LIST
547
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
548
549
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
550
        log_context = {
551
            'user_db': user_db,
552
            'resource_db': resource_db,
553
            'permission_type': permission_type,
554
            'resolver': self.__class__.__name__
555
        }
556
        self._log('Checking user resource permissions', extra=log_context)
557
558
        # First check the system role permissions
559
        has_system_role_permission = self._user_has_system_role_permission(
560
            user_db=user_db, permission_type=permission_type)
561
562
        if has_system_role_permission:
563
            self._log('Found a matching grant via system role', extra=log_context)
564
            return True
565
566
        # Check custom roles
567
        rule_spec = getattr(resource_db, 'rule', None)
568
        rule_uid = rule_spec.uid
569
        rule_id = rule_spec.id
570
        rule_pack = ResourceReference.get_pack(rule_spec.ref)
571
572
        if not rule_uid or not rule_id or not rule_pack:
573
            LOG.error('Rule UID or ID or PACK not present in enforcement object. ' +
574
                      ('UID = %s, ID = %s, PACK = %s' % (rule_uid, rule_id, rule_pack)) +
575
                      'Cannot assess access permissions without it. Defaulting to DENY.')
576
            return False
577
578
        # TODO: Add utility methods for constructing uids from parts
579
        pack_db = PackDB(ref=rule_pack)
580
        rule_pack_uid = pack_db.get_uid()
581
582
        rule_permission_type = None
583
        if permission_type == PermissionType.RULE_ENFORCEMENT_VIEW:
584
            rule_permission_type = PermissionType.RULE_VIEW
585
        elif permission_type == PermissionType.RULE_ENFORCEMENT_LIST:
586
            rule_permission_type = PermissionType.RULE_LIST
587
        else:
588
            raise ValueError('Invalid permission type: %s' % (permission_type))
589
590
        permission_types = [PermissionType.RULE_ALL, rule_permission_type]
591
592
        view_permission_type = PermissionType.get_permission_type(resource_type=ResourceType.RULE,
593
                                                                  permission_name='view')
594
595
        if rule_permission_type == view_permission_type:
596
            permission_types = (RulePermissionsResolver.view_grant_permission_types[:] +
597
                                [rule_permission_type])
598
599
        # Check grants on the pack of the rule to which enforcement belongs to
600
        resource_types = [ResourceType.PACK]
601
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
602
                                                               resource_uid=rule_pack_uid,
603
                                                               resource_types=resource_types,
604
                                                               permission_types=permission_types)
605
606
        if len(permission_grants) >= 1:
607
            self._log('Found a grant on the enforcement rule parent pack', extra=log_context)
608
            return True
609
610
        # Check grants on the rule the enforcement belongs to
611
        resource_types = [ResourceType.RULE]
612
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
613
                                                               resource_uid=rule_uid,
614
                                                               resource_types=resource_types,
615
                                                               permission_types=permission_types)
616
617
        if len(permission_grants) >= 1:
618
            self._log('Found a grant on the enforcement\'s rule.', extra=log_context)
619
            return True
620
621
        self._log('No matching grants found', extra=log_context)
622
        return False
623
624
625
class KeyValuePermissionsResolver(PermissionsResolver):
626
    """
627
    Permission resolver for "key value pair" resource type.
628
    """
629
630
    resource_type = ResourceType.KEY_VALUE_PAIR
631
632
    def user_has_permission(self, user_db, permission_type):
633
        # TODO: We don't support assigning permissions on key value pairs yet
634
        return True
635
636
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
637
        # TODO: We don't support assigning permissions on key value pairs yet
638
        return True
639
640
641
class ExecutionPermissionsResolver(PermissionsResolver):
642
    """
643
    Permission resolver for "execution" resource type.
644
    """
645
646
    resource_type = ResourceType.EXECUTION
647
648
    def user_has_permission(self, user_db, permission_type):
649
        assert permission_type in [PermissionType.EXECUTION_LIST]
650
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
651
652
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
653
        log_context = {
654
            'user_db': user_db,
655
            'resource_db': resource_db,
656
            'permission_type': permission_type,
657
            'resolver': self.__class__.__name__
658
        }
659
        self._log('Checking user resource permissions', extra=log_context)
660
661
        # First check the system role permissions
662
        has_system_role_permission = self._user_has_system_role_permission(
663
            user_db=user_db, permission_type=permission_type)
664
665
        if has_system_role_permission:
666
            self._log('Found a matching grant via system role', extra=log_context)
667
            return True
668
669
        # Check custom roles
670
        action = resource_db['action']
671
672
        # TODO: Add utility methods for constructing uids from parts
673
        pack_db = PackDB(ref=action['pack'])
674
675
        action_uid = action['uid']
676
        action_pack_uid = pack_db.get_uid()
677
678
        # Note: "action_execute" also grants / implies "execution_re_run" and "execution_stop"
679
        if permission_type == PermissionType.EXECUTION_VIEW:
680
            action_permission_type = PermissionType.ACTION_VIEW
681
        elif permission_type in [PermissionType.EXECUTION_RE_RUN,
682
                                 PermissionType.EXECUTION_STOP]:
683
            action_permission_type = PermissionType.ACTION_EXECUTE
684
        elif permission_type == PermissionType.EXECUTION_ALL:
685
            action_permission_type = PermissionType.ACTION_ALL
686
        else:
687
            raise ValueError('Invalid permission type: %s' % (permission_type))
688
689
        # Check grants on the pack of the action to which execution belongs to
690
        resource_types = [ResourceType.PACK]
691
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
692
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
693
                                                               resource_uid=action_pack_uid,
694
                                                               resource_types=resource_types,
695
                                                               permission_types=permission_types)
696
697
        if len(permission_grants) >= 1:
698
            self._log('Found a grant on the execution action parent pack', extra=log_context)
699
            return True
700
701
        # Check grants on the action the execution belongs to
702
        resource_types = [ResourceType.ACTION]
703
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
704
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
705
                                                               resource_uid=action_uid,
706
                                                               resource_types=resource_types,
707
                                                               permission_types=permission_types)
708
709
        if len(permission_grants) >= 1:
710
            self._log('Found a grant on the execution action', extra=log_context)
711
            return True
712
713
        self._log('No matching grants found', extra=log_context)
714
        return False
715
716
717
class WebhookPermissionsResolver(PermissionsResolver):
718
719
    resource_type = ResourceType.WEBHOOK
720
721
    def user_has_permission(self, user_db, permission_type):
722
        assert permission_type in [PermissionType.WEBHOOK_LIST]
723
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
724
725
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
726
        log_context = {
727
            'user_db': user_db,
728
            'resource_db': resource_db,
729
            'permission_type': permission_type,
730
            'resolver': self.__class__.__name__
731
        }
732
        self._log('Checking user resource permissions', extra=log_context)
733
734
        # First check the system role permissions
735
        has_system_role_permission = self._user_has_system_role_permission(
736
            user_db=user_db, permission_type=permission_type)
737
738
        if has_system_role_permission:
739
            self._log('Found a matching grant via system role', extra=log_context)
740
            return True
741
742
        # Check custom roles
743
        webhook_uid = resource_db.get_uid()
744
745
        # Check direct grants on the webhook
746
        resource_types = [ResourceType.WEBHOOK]
747
        permission_types = [PermissionType.WEBHOOK_ALL, permission_type]
748
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
749
                                                               resource_uid=webhook_uid,
750
                                                               resource_types=resource_types,
751
                                                               permission_types=permission_types)
752
753
        if len(permission_grants) >= 1:
754
            self._log('Found a grant on the webhook', extra=log_context)
755
            return True
756
757
        self._log('No matching grants found', extra=log_context)
758
        return False
759
760
761
class ApiKeyPermissionResolver(PermissionsResolver):
762
    """
763
    Permission resolver for "api key" resource type.
764
    """
765
766
    resource_type = ResourceType.API_KEY
767
768
    def user_has_permission(self, user_db, permission_type):
769
        assert permission_type in [PermissionType.API_KEY_LIST]
770
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
771
772
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
773
        assert permission_type in [PermissionType.API_KEY_CREATE]
774
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
775
776
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
777
        log_context = {
778
            'user_db': user_db,
779
            'resource_db': resource_db,
780
            'permission_type': permission_type,
781
            'resolver': self.__class__.__name__
782
        }
783
        self._log('Checking user resource permissions', extra=log_context)
784
785
        # First check the system role permissions
786
        has_system_role_permission = self._user_has_system_role_permission(
787
            user_db=user_db, permission_type=permission_type)
788
789
        if has_system_role_permission:
790
            self._log('Found a matching grant via system role', extra=log_context)
791
            return True
792
793
        # Check custom roles
794
        api_key_uid = resource_db.get_uid()
795
796
        # Check direct grants on the webhook
797
        resource_types = [ResourceType.API_KEY]
798
        permission_types = [PermissionType.API_KEY_ALL, permission_type]
799
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
800
                                                               resource_uid=api_key_uid,
801
                                                               resource_types=resource_types,
802
                                                               permission_types=permission_types)
803
804
        if len(permission_grants) >= 1:
805
            self._log('Found a grant on the api key', extra=log_context)
806
            return True
807
808
        self._log('No matching grants found', extra=log_context)
809
        return False
810
811
812
class TracePermissionsResolver(PermissionsResolver):
813
    """
814
    Permission resolver for "trace" resource type.
815
    """
816
817
    resource_type = ResourceType.TRACE
818
819
    def user_has_permission(self, user_db, permission_type):
820
        assert permission_type in [PermissionType.TRACE_LIST]
821
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
822
823
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
824
        log_context = {
825
            'user_db': user_db,
826
            'resource_db': resource_db,
827
            'permission_type': permission_type,
828
            'resolver': self.__class__.__name__
829
        }
830
        self._log('Checking user resource permissions', extra=log_context)
831
832
        # First check the system role permissions
833
        has_system_role_permission = self._user_has_system_role_permission(
834
            user_db=user_db, permission_type=permission_type)
835
836
        if has_system_role_permission:
837
            self._log('Found a matching grant via system role', extra=log_context)
838
            return True
839
840
        # Check custom roles
841
        trace_uid = resource_db.get_uid()
842
843
        # Check direct grants on the webhook
844
        resource_types = [ResourceType.TRACE]
845
        permission_types = [PermissionType.TRACE_ALL, permission_type]
846
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
847
                                                               resource_uid=trace_uid,
848
                                                               resource_types=resource_types,
849
                                                               permission_types=permission_types)
850
851
        if len(permission_grants) >= 1:
852
            self._log('Found a grant on the trace', extra=log_context)
853
            return True
854
855
        self._log('No matching grants found', extra=log_context)
856
        return False
857
858
859
class TriggerPermissionsResolver(PermissionsResolver):
860
    """
861
    Permission resolver for trigger and timers (timers are just a special type of triggers).
862
    """
863
864
    resource_type = ResourceType.TRACE
865
866
    def user_has_permission(self, user_db, permission_type):
867
        assert permission_type in [PermissionType.TRIGGER_LIST]
868
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
869
870
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
871
        log_context = {
872
            'user_db': user_db,
873
            'resource_db': resource_db,
874
            'permission_type': permission_type,
875
            'resolver': self.__class__.__name__
876
        }
877
        self._log('Checking user resource permissions', extra=log_context)
878
879
        # First check the system role permissions
880
        has_system_role_permission = self._user_has_system_role_permission(
881
            user_db=user_db, permission_type=permission_type)
882
883
        if has_system_role_permission:
884
            self._log('Found a matching grant via system role', extra=log_context)
885
            return True
886
887
        # Check custom roles
888
        timer_uid = resource_db.get_uid()
889
890
        # Check direct grants on the webhook
891
        resource_types = [ResourceType.TRIGGER]
892
        permission_types = [PermissionType.TRIGGER_ALL, permission_type]
893
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
894
                                                               resource_uid=timer_uid,
895
                                                               resource_types=resource_types,
896
                                                               permission_types=permission_types)
897
898
        if len(permission_grants) >= 1:
899
            self._log('Found a grant on the timer', extra=log_context)
900
            return True
901
902
        self._log('No matching grants found', extra=log_context)
903
        return False
904
905
906
def get_resolver_for_resource_type(resource_type):
907
    """
908
    Return resolver instance for the provided resource type.
909
910
    :rtype: Instance of :class:`PermissionsResolver`
911
    """
912
    if resource_type == ResourceType.RUNNER:
913
        resolver_cls = RunnerPermissionsResolver
914
    elif resource_type == ResourceType.PACK:
915
        resolver_cls = PackPermissionsResolver
916
    elif resource_type == ResourceType.SENSOR:
917
        resolver_cls = SensorPermissionsResolver
918
    elif resource_type == ResourceType.ACTION:
919
        resolver_cls = ActionPermissionsResolver
920
    elif resource_type == ResourceType.ACTION_ALIAS:
921
        resolver_cls = ActionAliasPermissionsResolver
922
    elif resource_type == ResourceType.RULE:
923
        resolver_cls = RulePermissionsResolver
924
    elif resource_type == ResourceType.EXECUTION:
925
        resolver_cls = ExecutionPermissionsResolver
926
    elif resource_type == ResourceType.KEY_VALUE_PAIR:
927
        resolver_cls = KeyValuePermissionsResolver
928
    elif resource_type == ResourceType.WEBHOOK:
929
        resolver_cls = WebhookPermissionsResolver
930
    elif resource_type == ResourceType.API_KEY:
931
        resolver_cls = ApiKeyPermissionResolver
932
    elif resource_type == ResourceType.RULE_ENFORCEMENT:
933
        resolver_cls = RuleEnforcementPermissionsResolver
934
    elif resource_type == ResourceType.TRACE:
935
        resolver_cls = TracePermissionsResolver
936
    elif resource_type == ResourceType.TRIGGER:
937
        resolver_cls = TriggerPermissionsResolver
938
    else:
939
        raise ValueError('Unsupported resource: %s' % (resource_type))
940
941
    resolver_instance = resolver_cls()
942
    return resolver_instance
943
944
945
def get_resolver_for_permission_type(permission_type):
946
    """
947
    Return resolver instance for the provided permission type.
948
949
    :rtype: Instance of :class:`PermissionsResolver`
950
    """
951
    resource_type = PermissionType.get_resource_type(permission_type=permission_type)
952
    resolver_instance = get_resolver_for_resource_type(resource_type=resource_type)
953
    return resolver_instance
954