Test Setup Failed
Push — master ( 561579...99c26c )
by
unknown
05:31
created

user_has_resource_db_permission()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
"""
17
Module containing resolver classes which contain permission resolving logic for different resource
18
types.
19
"""
20
21
import sys
22
import logging as stdlib_logging
23
24
from st2common import log as logging
25
from st2common.models.db.pack import PackDB
26
from st2common.models.db.webhook import WebhookDB
27
from st2common.models.system.common import ResourceReference
28
from st2common.constants.triggers import WEBHOOK_TRIGGER_TYPE
29
from st2common.rbac.types import PermissionType
30
from st2common.rbac.types import ResourceType
31
from st2common.rbac.types import SystemRole
32
from st2common.rbac.types import GLOBAL_PACK_PERMISSION_TYPES
33
from st2common.services.rbac import get_roles_for_user
34
from st2common.services.rbac import get_all_permission_grants_for_user
35
36
LOG = logging.getLogger(__name__)
37
38
__all__ = [
39
    'RunnerPermissionsResolver',
40
    'PackPermissionsResolver',
41
    'SensorPermissionsResolver',
42
    'ActionPermissionsResolver',
43
    'ActionAliasPermissionsResolver',
44
    'RulePermissionsResolver',
45
    'RuleEnforcementPermissionsResolver',
46
    'KeyValuePermissionsResolver',
47
    'ExecutionPermissionsResolver',
48
    'WebhookPermissionsResolver',
49
    '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
                                   PermissionType.EXECUTION_VIEWS_FILTERS_LIST]
651
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
652
653
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
654
        log_context = {
655
            'user_db': user_db,
656
            'resource_db': resource_db,
657
            'permission_type': permission_type,
658
            'resolver': self.__class__.__name__
659
        }
660
        self._log('Checking user resource permissions', extra=log_context)
661
662
        # First check the system role permissions
663
        has_system_role_permission = self._user_has_system_role_permission(
664
            user_db=user_db, permission_type=permission_type)
665
666
        if has_system_role_permission:
667
            self._log('Found a matching grant via system role', extra=log_context)
668
            return True
669
670
        # Check custom roles
671
        action = resource_db['action']
672
673
        # TODO: Add utility methods for constructing uids from parts
674
        pack_db = PackDB(ref=action['pack'])
675
676
        action_uid = action['uid']
677
        action_pack_uid = pack_db.get_uid()
678
679
        # Note: "action_execute" also grants / implies "execution_re_run" and "execution_stop"
680
        if permission_type == PermissionType.EXECUTION_VIEW:
681
            action_permission_type = PermissionType.ACTION_VIEW
682
        elif permission_type in [PermissionType.EXECUTION_RE_RUN,
683
                                 PermissionType.EXECUTION_STOP]:
684
            action_permission_type = PermissionType.ACTION_EXECUTE
685
        elif permission_type == PermissionType.EXECUTION_ALL:
686
            action_permission_type = PermissionType.ACTION_ALL
687
        elif permission_type == PermissionType.EXECUTION_VIEWS_FILTERS_LIST:
688
            action_permission_type = PermissionType.EXECUTION_VIEWS_FILTERS_LIST
689
        else:
690
            raise ValueError('Invalid permission type: %s' % (permission_type))
691
692
        # Check grants on the pack of the action to which execution belongs to
693
        resource_types = [ResourceType.PACK]
694
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
695
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
696
                                                               resource_uid=action_pack_uid,
697
                                                               resource_types=resource_types,
698
                                                               permission_types=permission_types)
699
700
        if len(permission_grants) >= 1:
701
            self._log('Found a grant on the execution action parent pack', extra=log_context)
702
            return True
703
704
        # Check grants on the action the execution belongs to
705
        resource_types = [ResourceType.ACTION]
706
        permission_types = [PermissionType.ACTION_ALL, action_permission_type]
707
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
708
                                                               resource_uid=action_uid,
709
                                                               resource_types=resource_types,
710
                                                               permission_types=permission_types)
711
712
        if len(permission_grants) >= 1:
713
            self._log('Found a grant on the execution action', extra=log_context)
714
            return True
715
716
        self._log('No matching grants found', extra=log_context)
717
        return False
718
719
720
class WebhookPermissionsResolver(PermissionsResolver):
721
722
    resource_type = ResourceType.WEBHOOK
723
724
    def user_has_permission(self, user_db, permission_type):
725
        assert permission_type in [PermissionType.WEBHOOK_LIST]
726
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
727
728
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
729
        log_context = {
730
            'user_db': user_db,
731
            'resource_db': resource_db,
732
            'permission_type': permission_type,
733
            'resolver': self.__class__.__name__
734
        }
735
        self._log('Checking user resource permissions', extra=log_context)
736
737
        # First check the system role permissions
738
        has_system_role_permission = self._user_has_system_role_permission(
739
            user_db=user_db, permission_type=permission_type)
740
741
        if has_system_role_permission:
742
            self._log('Found a matching grant via system role', extra=log_context)
743
            return True
744
745
        # Check custom roles
746
        webhook_uid = resource_db.get_uid()
747
748
        # Check direct grants on the webhook
749
        resource_types = [ResourceType.WEBHOOK]
750
        permission_types = [PermissionType.WEBHOOK_ALL, permission_type]
751
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
752
                                                               resource_uid=webhook_uid,
753
                                                               resource_types=resource_types,
754
                                                               permission_types=permission_types)
755
756
        if len(permission_grants) >= 1:
757
            self._log('Found a grant on the webhook', extra=log_context)
758
            return True
759
760
        self._log('No matching grants found', extra=log_context)
761
        return False
762
763
764
class TimerPermissionsResolver(PermissionsResolver):
765
    """
766
    Permission resolver for timers (timers are just a special type of triggers).
767
    """
768
769
    resource_type = ResourceType.TIMER
770
771
    def user_has_permission(self, user_db, permission_type):
772
        assert permission_type in [PermissionType.TIMER_LIST]
773
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
774
775
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
776
        log_context = {
777
            'user_db': user_db,
778
            'resource_db': resource_db,
779
            'permission_type': permission_type,
780
            'resolver': self.__class__.__name__
781
        }
782
        self._log('Checking user resource permissions', extra=log_context)
783
784
        # First check the system role permissions
785
        has_system_role_permission = self._user_has_system_role_permission(
786
            user_db=user_db, permission_type=permission_type)
787
788
        if has_system_role_permission:
789
            self._log('Found a matching grant via system role', extra=log_context)
790
            return True
791
792
        # Check custom roles
793
        timer_uid = resource_db.get_uid()
794
795
        # Check direct grants on the webhook
796
        resource_types = [ResourceType.TIMER]
797
        permission_types = [PermissionType.TIMER_ALL, permission_type]
798
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
799
                                                               resource_uid=timer_uid,
800
                                                               resource_types=resource_types,
801
                                                               permission_types=permission_types)
802
803
        if len(permission_grants) >= 1:
804
            self._log('Found a grant on the timer', extra=log_context)
805
            return True
806
807
        self._log('No matching grants found', extra=log_context)
808
        return False
809
810
811
class ApiKeyPermissionResolver(PermissionsResolver):
812
    """
813
    Permission resolver for "api key" resource type.
814
    """
815
816
    resource_type = ResourceType.API_KEY
817
818
    def user_has_permission(self, user_db, permission_type):
819
        assert permission_type in [PermissionType.API_KEY_LIST]
820
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
821
822
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
823
        assert permission_type in [PermissionType.API_KEY_CREATE]
824
        return self._user_has_global_permission(user_db=user_db, permission_type=permission_type)
825
826
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
827
        log_context = {
828
            'user_db': user_db,
829
            'resource_db': resource_db,
830
            'permission_type': permission_type,
831
            'resolver': self.__class__.__name__
832
        }
833
        self._log('Checking user resource permissions', extra=log_context)
834
835
        # First check the system role permissions
836
        has_system_role_permission = self._user_has_system_role_permission(
837
            user_db=user_db, permission_type=permission_type)
838
839
        if has_system_role_permission:
840
            self._log('Found a matching grant via system role', extra=log_context)
841
            return True
842
843
        # Check custom roles
844
        api_key_uid = resource_db.get_uid()
845
846
        # Check direct grants on the webhook
847
        resource_types = [ResourceType.API_KEY]
848
        permission_types = [PermissionType.API_KEY_ALL, permission_type]
849
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
850
                                                               resource_uid=api_key_uid,
851
                                                               resource_types=resource_types,
852
                                                               permission_types=permission_types)
853
854
        if len(permission_grants) >= 1:
855
            self._log('Found a grant on the api key', extra=log_context)
856
            return True
857
858
        self._log('No matching grants found', extra=log_context)
859
        return False
860
861
862
class TracePermissionsResolver(PermissionsResolver):
863
    """
864
    Permission resolver for "trace" resource type.
865
    """
866
867
    resource_type = ResourceType.TRACE
868
869
    def user_has_permission(self, user_db, permission_type):
870
        assert permission_type in [PermissionType.TRACE_LIST]
871
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
872
873
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
874
        log_context = {
875
            'user_db': user_db,
876
            'resource_db': resource_db,
877
            'permission_type': permission_type,
878
            'resolver': self.__class__.__name__
879
        }
880
        self._log('Checking user resource permissions', extra=log_context)
881
882
        # First check the system role permissions
883
        has_system_role_permission = self._user_has_system_role_permission(
884
            user_db=user_db, permission_type=permission_type)
885
886
        if has_system_role_permission:
887
            self._log('Found a matching grant via system role', extra=log_context)
888
            return True
889
890
        # Check custom roles
891
        trace_uid = resource_db.get_uid()
892
893
        # Check direct grants on the webhook
894
        resource_types = [ResourceType.TRACE]
895
        permission_types = [PermissionType.TRACE_ALL, permission_type]
896
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
897
                                                               resource_uid=trace_uid,
898
                                                               resource_types=resource_types,
899
                                                               permission_types=permission_types)
900
901
        if len(permission_grants) >= 1:
902
            self._log('Found a grant on the trace', extra=log_context)
903
            return True
904
905
        self._log('No matching grants found', extra=log_context)
906
        return False
907
908
909
class TriggerPermissionsResolver(PermissionsResolver):
910
    """
911
    Permission resolver for trigger and timers (timers are just a special type of triggers).
912
    """
913
914
    resource_type = ResourceType.TRIGGER
915
916
    def user_has_permission(self, user_db, permission_type):
917
        assert permission_type in [PermissionType.TRIGGER_LIST]
918
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
919
920
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
921
        log_context = {
922
            'user_db': user_db,
923
            'resource_db': resource_db,
924
            'permission_type': permission_type,
925
            'resolver': self.__class__.__name__
926
        }
927
        self._log('Checking user resource permissions', extra=log_context)
928
929
        # First check the system role permissions
930
        has_system_role_permission = self._user_has_system_role_permission(
931
            user_db=user_db, permission_type=permission_type)
932
933
        if has_system_role_permission:
934
            self._log('Found a matching grant via system role', extra=log_context)
935
            return True
936
937
        # Check custom roles
938
        timer_uid = resource_db.get_uid()
939
940
        # Check direct grants on the webhook
941
        resource_types = [ResourceType.TRIGGER]
942
        permission_types = [PermissionType.TRIGGER_ALL, permission_type]
943
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
944
                                                               resource_uid=timer_uid,
945
                                                               resource_types=resource_types,
946
                                                               permission_types=permission_types)
947
948
        if len(permission_grants) >= 1:
949
            self._log('Found a grant on the timer', extra=log_context)
950
            return True
951
952
        self._log('No matching grants found', extra=log_context)
953
        return False
954
955
956
class PolicyTypePermissionsResolver(PermissionsResolver):
957
    """
958
    Permission resolver for "policy type" resource.
959
    """
960
961
    resource_type = ResourceType.POLICY_TYPE
962
963
    def user_has_permission(self, user_db, permission_type):
964
        assert permission_type in [PermissionType.POLICY_TYPE_LIST]
965
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
966
967
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
968
        log_context = {
969
            'user_db': user_db,
970
            'resource_db': resource_db,
971
            'permission_type': permission_type,
972
            'resolver': self.__class__.__name__
973
        }
974
        self._log('Checking user resource permissions', extra=log_context)
975
976
        # First check the system role permissions
977
        has_system_role_permission = self._user_has_system_role_permission(
978
            user_db=user_db, permission_type=permission_type)
979
980
        if has_system_role_permission:
981
            self._log('Found a matching grant via system role', extra=log_context)
982
            return True
983
984
        # Check custom roles
985
        policy_type_uid = resource_db.get_uid()
986
987
        # Check direct grants on the webhook
988
        resource_types = [ResourceType.POLICY_TYPE]
989
        permission_types = [PermissionType.POLICY_TYPE_ALL, permission_type]
990
        permission_grants = get_all_permission_grants_for_user(user_db=user_db,
991
                                                               resource_uid=policy_type_uid,
992
                                                               resource_types=resource_types,
993
                                                               permission_types=permission_types)
994
995
        if len(permission_grants) >= 1:
996
            self._log('Found a grant on the policy type', extra=log_context)
997
            return True
998
999
        self._log('No matching grants found', extra=log_context)
1000
        return False
1001
1002
1003
class PolicyPermissionsResolver(ContentPackResourcePermissionsResolver):
1004
    """
1005
    Permission resolver for "policy" resource type.
1006
    """
1007
1008
    resource_type = ResourceType.POLICY
1009
    view_grant_permission_types = [
1010
        PermissionType.POLICY_ALL,
1011
        PermissionType.POLICY_CREATE,
1012
        PermissionType.POLICY_MODIFY,
1013
        PermissionType.POLICY_DELETE
1014
    ]
1015
1016
    def user_has_permission(self, user_db, permission_type):
1017
        assert permission_type in [PermissionType.POLICY_LIST]
1018
        return self._user_has_list_permission(user_db=user_db, permission_type=permission_type)
1019
1020
    def user_has_resource_api_permission(self, user_db, resource_api, permission_type):
1021
        assert permission_type in [PermissionType.POLICY_CREATE]
1022
1023
        policy_uid = resource_api.get_uid()
1024
        pack_uid = resource_api.get_pack_uid()
1025
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
1026
                                                  resource_uid=policy_uid,
1027
                                                  permission_type=permission_type)
1028
1029
    def user_has_resource_db_permission(self, user_db, resource_db, permission_type):
1030
        policy_uid = resource_db.get_uid()
1031
        pack_uid = resource_db.get_pack_uid()
1032
        return self._user_has_resource_permission(user_db=user_db, pack_uid=pack_uid,
1033
                                                  resource_uid=policy_uid,
1034
                                                  permission_type=permission_type)
1035
1036
1037
def get_resolver_for_resource_type(resource_type):
1038
    """
1039
    Return resolver instance for the provided resource type.
1040
1041
    :rtype: Instance of :class:`PermissionsResolver`
1042
    """
1043
    if resource_type == ResourceType.RUNNER:
1044
        resolver_cls = RunnerPermissionsResolver
1045
    elif resource_type == ResourceType.PACK:
1046
        resolver_cls = PackPermissionsResolver
1047
    elif resource_type == ResourceType.SENSOR:
1048
        resolver_cls = SensorPermissionsResolver
1049
    elif resource_type == ResourceType.ACTION:
1050
        resolver_cls = ActionPermissionsResolver
1051
    elif resource_type == ResourceType.ACTION_ALIAS:
1052
        resolver_cls = ActionAliasPermissionsResolver
1053
    elif resource_type == ResourceType.RULE:
1054
        resolver_cls = RulePermissionsResolver
1055
    elif resource_type == ResourceType.EXECUTION:
1056
        resolver_cls = ExecutionPermissionsResolver
1057
    elif resource_type == ResourceType.KEY_VALUE_PAIR:
1058
        resolver_cls = KeyValuePermissionsResolver
1059
    elif resource_type == ResourceType.WEBHOOK:
1060
        resolver_cls = WebhookPermissionsResolver
1061
    elif resource_type == ResourceType.TIMER:
1062
        resolver_cls = TimerPermissionsResolver
1063
    elif resource_type == ResourceType.API_KEY:
1064
        resolver_cls = ApiKeyPermissionResolver
1065
    elif resource_type == ResourceType.RULE_ENFORCEMENT:
1066
        resolver_cls = RuleEnforcementPermissionsResolver
1067
    elif resource_type == ResourceType.TRACE:
1068
        resolver_cls = TracePermissionsResolver
1069
    elif resource_type == ResourceType.TRIGGER:
1070
        resolver_cls = TriggerPermissionsResolver
1071
    elif resource_type == ResourceType.POLICY_TYPE:
1072
        resolver_cls = PolicyTypePermissionsResolver
1073
    elif resource_type == ResourceType.POLICY:
1074
        resolver_cls = PolicyPermissionsResolver
1075
    else:
1076
        raise ValueError('Unsupported resource: %s' % (resource_type))
1077
1078
    resolver_instance = resolver_cls()
1079
    return resolver_instance
1080
1081
1082
def get_resolver_for_permission_type(permission_type):
1083
    """
1084
    Return resolver instance for the provided permission type.
1085
1086
    :rtype: Instance of :class:`PermissionsResolver`
1087
    """
1088
    resource_type = PermissionType.get_resource_type(permission_type=permission_type)
1089
    resolver_instance = get_resolver_for_resource_type(resource_type=resource_type)
1090
    return resolver_instance
1091