Passed
Push — develop ( 58fbab...4c05e2 )
by Plexxi
07:20 queued 03:32
created

RunnerPermissionsResolver   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 42
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
rs 10
wmc 5

2 Methods

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