Passed
Pull Request — master (#3640)
by Lakshmi
06:19
created

StreamPermissionsResolver   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 7
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 10
wmc 2

1 Method

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