def setUp(self):
        super(WebhookControllerRBACTestCase, self).setUp()

        # Insert mock users, roles and assignments

        # Users
        user_1_db = UserDB(name='webhook_list')
        user_1_db = User.add_or_update(user_1_db)
        self.users['webhook_list'] = user_1_db

        user_2_db = UserDB(name='webhook_view')
        user_2_db = User.add_or_update(user_2_db)
        self.users['webhook_view'] = user_2_db

        # Roles
        # webhook_list
        grant_db = PermissionGrantDB(
            resource_uid=None,
            resource_type=ResourceType.WEBHOOK,
            permission_types=[PermissionType.WEBHOOK_LIST])
        grant_db = PermissionGrant.add_or_update(grant_db)
        permission_grants = [str(grant_db.id)]
        role_1_db = RoleDB(name='webhook_list',
                           permission_grants=permission_grants)
        role_1_db = Role.add_or_update(role_1_db)
        self.roles['webhook_list'] = role_1_db

        # webhook_view on webhook 1 (git)
        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_uid = webhook_db.get_uid()
        grant_db = PermissionGrantDB(
            resource_uid=webhook_uid,
            resource_type=ResourceType.WEBHOOK,
            permission_types=[PermissionType.WEBHOOK_VIEW])
        grant_db = PermissionGrant.add_or_update(grant_db)
        permission_grants = [str(grant_db.id)]
        role_1_db = RoleDB(name='webhook_view',
                           permission_grants=permission_grants)
        role_1_db = Role.add_or_update(role_1_db)
        self.roles['webhook_view'] = role_1_db

        # Role assignments
        role_assignment_db = UserRoleAssignmentDB(
            user=self.users['webhook_list'].name,
            role=self.roles['webhook_list'].name,
            source='assignments/%s.yaml' % self.users['webhook_list'].name)
        UserRoleAssignment.add_or_update(role_assignment_db)

        role_assignment_db = UserRoleAssignmentDB(
            user=self.users['webhook_view'].name,
            role=self.roles['webhook_view'].name,
            source='assignments/%s.yaml' % self.users['webhook_view'].name)
        UserRoleAssignment.add_or_update(role_assignment_db)
Example #2
0
    def setUp(self):
        super(WebhookControllerRBACTestCase, self).setUp()

        # Insert mock users, roles and assignments

        # Users
        user_1_db = UserDB(name='webhook_list')
        user_1_db = User.add_or_update(user_1_db)
        self.users['webhook_list'] = user_1_db

        user_2_db = UserDB(name='webhook_view')
        user_2_db = User.add_or_update(user_2_db)
        self.users['webhook_view'] = user_2_db

        # Roles
        # webhook_list
        grant_db = PermissionGrantDB(resource_uid=None,
                                     resource_type=ResourceType.WEBHOOK,
                                     permission_types=[PermissionType.WEBHOOK_LIST])
        grant_db = PermissionGrant.add_or_update(grant_db)
        permission_grants = [str(grant_db.id)]
        role_1_db = RoleDB(name='webhook_list', permission_grants=permission_grants)
        role_1_db = Role.add_or_update(role_1_db)
        self.roles['webhook_list'] = role_1_db

        # webhook_view on webhook 1 (git)
        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_uid = webhook_db.get_uid()
        grant_db = PermissionGrantDB(resource_uid=webhook_uid,
                                     resource_type=ResourceType.WEBHOOK,
                                     permission_types=[PermissionType.WEBHOOK_VIEW])
        grant_db = PermissionGrant.add_or_update(grant_db)
        permission_grants = [str(grant_db.id)]
        role_1_db = RoleDB(name='webhook_view', permission_grants=permission_grants)
        role_1_db = Role.add_or_update(role_1_db)
        self.roles['webhook_view'] = role_1_db

        # Role assignments
        role_assignment_db = UserRoleAssignmentDB(
            user=self.users['webhook_list'].name,
            role=self.roles['webhook_list'].name,
            source='assignments/%s.yaml' % self.users['webhook_list'].name)
        UserRoleAssignment.add_or_update(role_assignment_db)

        role_assignment_db = UserRoleAssignmentDB(
            user=self.users['webhook_view'].name,
            role=self.roles['webhook_view'].name,
            source='assignments/%s.yaml' % self.users['webhook_view'].name)
        UserRoleAssignment.add_or_update(role_assignment_db)
Example #3
0
    def test_get_one_no_permissions(self):
        user_db = self.users['no_permissions']
        self.use_user(user_db)

        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_id = name
        webhook_uid = webhook_db.get_uid()

        resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True)
        expected_msg = ('User "no_permissions" doesn\'t have required permission "webhook_view"'
                        ' on resource "%s"' % (webhook_uid))
        self.assertEqual(resp.status_code, httplib.FORBIDDEN)
        self.assertEqual(resp.json['faultstring'], expected_msg)
Example #4
0
    def test_get_one_no_permissions(self):
        user_db = self.users['no_permissions']
        self.use_user(user_db)

        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_id = name
        webhook_uid = webhook_db.get_uid()

        resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True)
        expected_msg = ('User "no_permissions" doesn\'t have required permission "webhook_view"'
                        ' on resource "%s"' % (webhook_uid))
        self.assertEqual(resp.status_code, http_client.FORBIDDEN)
        self.assertEqual(resp.json['faultstring'], expected_msg)
Example #5
0
    def setUp(self):
        super(WebhookPermissionsResolverTestCase, self).setUp()

        # Create some mock users
        user_1_db = UserDB(name='custom_role_webhook_grant')
        user_1_db = User.add_or_update(user_1_db)
        self.users['custom_role_webhook_grant'] = user_1_db

        # Create some mock resources on which permissions can be granted
        webhook_1_db = WebhookDB(name='st2/')
        self.resources['webhook_1'] = webhook_1_db

        # Create some mock roles with associated permission grants
        # Custom role - "webhook_send" grant on webhook_1
        grant_db = PermissionGrantDB(
            resource_uid=self.resources['webhook_1'].get_uid(),
            resource_type=ResourceType.WEBHOOK,
            permission_types=[PermissionType.WEBHOOK_SEND])
        grant_db = PermissionGrant.add_or_update(grant_db)
        permission_grants = [str(grant_db.id)]
        role_db = RoleDB(name='custom_role_webhook_grant',
                         permission_grants=permission_grants)
        role_db = Role.add_or_update(role_db)
        self.roles['custom_role_webhook_grant'] = role_db

        # Create some mock role assignments
        user_db = self.users['custom_role_webhook_grant']
        role_assignment_db = UserRoleAssignmentDB(
            user=user_db.name,
            role=self.roles['custom_role_webhook_grant'].name)
        UserRoleAssignment.add_or_update(role_assignment_db)
Example #6
0
    def post(self, hook, webhook_body_api, headers, requester_user):
        body = webhook_body_api.data

        permission_type = PermissionType.WEBHOOK_SEND
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=WebhookDB(name=hook),
            permission_type=permission_type)

        headers = self._get_headers_as_dict(headers)

        # If webhook contains a trace-tag use that else create create a unique trace-tag.
        trace_context = self._create_trace_context(trace_tag=headers.pop(
            TRACE_TAG_HEADER, None),
                                                   hook=hook)

        if hook == 'st2' or hook == 'st2/':
            # When using st2 or system webhook, body needs to always be a dict
            if not isinstance(body, dict):
                type_string = get_json_type_for_python_value(body)
                msg = ('Webhook body needs to be an object, got: %s' %
                       (type_string))
                raise ValueError(msg)

            trigger = body.get('trigger', None)
            payload = body.get('payload', None)

            if not trigger:
                msg = 'Trigger not specified.'
                return abort(http_client.BAD_REQUEST, msg)

            self._trigger_dispatcher_service.dispatch_with_context(
                trigger=trigger,
                payload=payload,
                trace_context=trace_context,
                throw_on_validation_error=True)
        else:
            if not self._is_valid_hook(hook):
                self._log_request('Invalid hook.', headers, body)
                msg = 'Webhook %s not registered with st2' % hook
                return abort(http_client.NOT_FOUND, msg)

            triggers = self._hooks.get_triggers_for_hook(hook)
            payload = {}

            payload['headers'] = headers
            payload['body'] = body

            # Dispatch trigger instance for each of the trigger found
            for trigger_dict in triggers:
                # TODO: Instead of dispatching the whole dict we should just
                # dispatch TriggerDB.ref or similar
                self._trigger_dispatcher_service.dispatch_with_context(
                    trigger=trigger_dict,
                    payload=payload,
                    trace_context=trace_context,
                    throw_on_validation_error=True)

        return Response(json=body, status=http_client.ACCEPTED)
Example #7
0
    def test_get_all_permission_success_get_one_no_permission_failure(self):
        user_db = self.users['webhook_list']
        self.use_user(user_db)

        # webhook_list permission, but no webhook_view permission
        resp = self.app.get('/v1/webhooks')
        self.assertEqual(resp.status_code, httplib.OK)
        self.assertEqual(len(resp.json), 1)

        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_id = name
        webhook_uid = webhook_db.get_uid()

        resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True)
        expected_msg = ('User "webhook_list" doesn\'t have required permission "webhook_view"'
                        ' on resource "%s"' % (webhook_uid))
        self.assertEqual(resp.status_code, httplib.FORBIDDEN)
        self.assertEqual(resp.json['faultstring'], expected_msg)
Example #8
0
    def test_get_all_permission_success_get_one_no_permission_failure(self):
        user_db = self.users['webhook_list']
        self.use_user(user_db)

        # webhook_list permission, but no webhook_view permission
        resp = self.app.get('/v1/webhooks')
        self.assertEqual(resp.status_code, http_client.OK)
        self.assertEqual(len(resp.json), 1)

        name = 'git'
        webhook_db = WebhookDB(name=name)
        webhook_id = name
        webhook_uid = webhook_db.get_uid()

        resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True)
        expected_msg = ('User "webhook_list" doesn\'t have required permission "webhook_view"'
                        ' on resource "%s"' % (webhook_uid))
        self.assertEqual(resp.status_code, http_client.FORBIDDEN)
        self.assertEqual(resp.json['faultstring'], expected_msg)
Example #9
0
        def func_wrapper(*args, **kwargs):
            hook = '/'.join(
                args[1:])  # TODO: There must be a better way to do this.
            webhook_db = WebhookDB(name=hook)

            resource_db = webhook_db
            utils.assert_request_user_has_resource_permission(
                request=pecan.request,
                resource_db=resource_db,
                permission_type=permission_type)
            return func(*args, **kwargs)
Example #10
0
    def get_one(self, name, requester_user):
        triggers = self._hooks.get_triggers_for_hook(name)

        if not triggers:
            abort(http_client.NOT_FOUND)
            return

        permission_type = PermissionType.WEBHOOK_VIEW
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=WebhookDB(name=name),
                                                          permission_type=permission_type)

        # For demonstration purpose return 1st
        return triggers[0]
Example #11
0
    def post(self, hook, body, headers, requester_user):
        body = vars(body)

        permission_type = PermissionType.WEBHOOK_SEND
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=WebhookDB(name=hook),
            permission_type=permission_type)

        headers = self._get_headers_as_dict(headers)
        # If webhook contains a trace-tag use that else create create a unique trace-tag.
        trace_context = self._create_trace_context(trace_tag=headers.pop(
            TRACE_TAG_HEADER, None),
                                                   hook=hook)

        if hook == 'st2' or hook == 'st2/':
            trigger = body.get('trigger', None)
            payload = body.get('payload', None)

            if not trigger:
                msg = 'Trigger not specified.'
                return abort(http_client.BAD_REQUEST, msg)

            self._trigger_dispatcher.dispatch(trigger,
                                              payload=payload,
                                              trace_context=trace_context)
        else:
            if not self._is_valid_hook(hook):
                self._log_request('Invalid hook.', headers, body)
                msg = 'Webhook %s not registered with st2' % hook
                return abort(http_client.NOT_FOUND, msg)

            triggers = self._hooks.get_triggers_for_hook(hook)
            payload = {}

            payload['headers'] = headers
            payload['body'] = body
            # Dispatch trigger instance for each of the trigger found
            for trigger in triggers:
                self._trigger_dispatcher.dispatch(trigger,
                                                  payload=payload,
                                                  trace_context=trace_context)

        return Response(json=body, status=http_client.ACCEPTED)
Example #12
0
    def user_has_trigger_permission(self, user_db, trigger):
        """
        Check if the user has access to the provided trigger.

        This method is to be used during rule create and update where we check if the user has the
        necessary trigger permissions.

        Note: Right now we only support webhook triggers.

        :param trigger: "trigger" attribute of the RuleAPI object.
        :type trigger: ``dict``
        """
        log_context = {
            'user_db': user_db,
            'trigger': trigger,
            'resolver': self.__class__.__name__
        }

        trigger_type = trigger['type']
        trigger_parameters = trigger.get('parameters', {})

        if trigger_type != WEBHOOK_TRIGGER_TYPE:
            self._log(
                'Not a webhook trigger type, ignoring trigger permission checking',
                extra=log_context)
            return True

        resolver = get_resolver_for_resource_type(ResourceType.WEBHOOK)
        webhook_db = WebhookDB(name=trigger_parameters['url'])
        permission_type = PermissionType.WEBHOOK_CREATE
        result = resolver.user_has_resource_db_permission(
            user_db=user_db,
            resource_db=webhook_db,
            permission_type=permission_type)

        if result is True:
            self._log('Found a matching trigger grant', extra=log_context)
            return True

        self._log('No matching trigger grants found', extra=log_context)
        return False
Example #13
0
    def post(self, hook, webhook_body_api, headers, requester_user):
        body = webhook_body_api.data

        permission_type = PermissionType.WEBHOOK_SEND
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=WebhookDB(name=hook),
            permission_type=permission_type,
        )

        headers = self._get_headers_as_dict(headers)
        headers = self._filter_authentication_headers(headers)

        # If webhook contains a trace-tag use that else create create a unique trace-tag.
        trace_context = self._create_trace_context(
            trace_tag=headers.pop(TRACE_TAG_HEADER, None), hook=hook
        )

        if hook == "st2" or hook == "st2/":
            # When using st2 or system webhook, body needs to always be a dict
            if not isinstance(body, dict):
                type_string = get_json_type_for_python_value(body)
                msg = "Webhook body needs to be an object, got: %s" % (type_string)
                raise ValueError(msg)

            trigger = body.get("trigger", None)
            payload = body.get("payload", None)

            if not trigger:
                msg = "Trigger not specified."
                return abort(http_client.BAD_REQUEST, msg)

            self._trigger_dispatcher_service.dispatch_with_context(
                trigger=trigger,
                payload=payload,
                trace_context=trace_context,
                throw_on_validation_error=True,
            )
        else:
            if not self._is_valid_hook(hook):
                self._log_request("Invalid hook.", headers, body)
                msg = "Webhook %s not registered with st2" % hook
                return abort(http_client.NOT_FOUND, msg)

            triggers = self._hooks.get_triggers_for_hook(hook)
            payload = {}

            payload["headers"] = headers
            payload["body"] = body

            # Dispatch trigger instance for each of the trigger found
            for trigger_dict in triggers:
                # TODO: Instead of dispatching the whole dict we should just
                # dispatch TriggerDB.ref or similar
                self._trigger_dispatcher_service.dispatch_with_context(
                    trigger=trigger_dict,
                    payload=payload,
                    trace_context=trace_context,
                    throw_on_validation_error=True,
                )

        # NOTE: For url encoded request bodies, values will be bytes instead of unicode and this
        # doesn't work with orjson so we first need to "cast" all the values from bytes to unicode

        return Response(json=body, status=http_client.ACCEPTED)