コード例 #1
0
ファイル: inquiries.py プロジェクト: lyandut/st2
    def get_all(self, requester_user=None, limit=None, **raw_filters):
        """Retrieve multiple Inquiries

            Handles requests:
                GET /inquiries/
        """

        raw_inquiries = super(InquiriesController, self)._get_all(
            limit=limit,
            raw_filters={
                'status': action_constants.LIVEACTION_STATUS_PENDING,
                'runner': INQUIRY_RUNNER
            },
            requester_user=requester_user
        )

        # Since "model" is set to InquiryAPI (for good reasons), _get_all returns a list of
        # InquiryAPI instances, already converted to JSON. So in order to convert these to
        # InquiryResponseAPI instances, we first have to convert raw_inquiries.body back to
        # a list of dicts, and then individually convert these to InquiryResponseAPI instances
        inquiries = [InquiryResponseAPI.from_model(raw_inquiry, skip_db=True)
                     for raw_inquiry in json.loads(raw_inquiries.body)]

        # Repackage into Response with correct headers
        resp = Response(json=inquiries)
        resp.headers['X-Total-Count'] = raw_inquiries.headers['X-Total-Count']
        if limit:
            resp.headers['X-Limit'] = str(limit)
        return resp
コード例 #2
0
ファイル: auth.py プロジェクト: StackStorm/st2
    def get_all(self, requester_user, show_secrets=None, limit=None, offset=0):
        """
            List all keys.

            Handles requests:
                GET /apikeys/
        """
        mask_secrets = self._get_mask_secrets(show_secrets=show_secrets,
                                              requester_user=requester_user)

        limit = resource.validate_limit_query_param(limit, requester_user=requester_user)

        try:
            api_key_dbs = ApiKey.get_all(limit=limit, offset=offset)
            api_keys = [ApiKeyAPI.from_model(api_key_db, mask_secrets=mask_secrets)
                        for api_key_db in api_key_dbs]
        except OverflowError:
            msg = 'Offset "%s" specified is more than 32 bit int' % (offset)
            raise ValueError(msg)

        resp = Response(json=api_keys)
        resp.headers['X-Total-Count'] = str(api_key_dbs.count())

        if limit:
            resp.headers['X-Limit'] = str(limit)

        return resp
コード例 #3
0
ファイル: action_views.py プロジェクト: StackStorm/st2
    def get_one(self, ref_or_id, requester_user):
        """
            Outputs the file associated with action entry_point

            Handles requests:
                GET /actions/views/entry_point/1
        """
        LOG.info('GET /actions/views/entry_point with ref_or_id=%s', ref_or_id)
        action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        permission_type = PermissionType.ACTION_VIEW
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=action_db,
                                                          permission_type=permission_type)

        pack = getattr(action_db, 'pack', None)
        entry_point = getattr(action_db, 'entry_point', None)

        abs_path = utils.get_entry_point_abs_path(pack, entry_point)

        if not abs_path:
            raise StackStormDBObjectNotFoundError('Action ref_or_id=%s has no entry_point to output'
                                                  % ref_or_id)

        with codecs.open(abs_path, 'r') as fp:
            content = fp.read()

        # Ensure content is utf-8
        if isinstance(content, six.binary_type):
            content = content.decode('utf-8')

        try:
            content_type = mimetypes.guess_type(abs_path)[0]
        except Exception:
            content_type = None

        # Special case if /etc/mime.types doesn't contain entry for yaml, py
        if not content_type:
            _, extension = os.path.splitext(abs_path)
            if extension in ['.yaml', '.yml']:
                content_type = 'application/x-yaml'
            elif extension in ['.py']:
                content_type = 'application/x-python'
            else:
                content_type = 'text/plain'

        response = Response()
        response.headers['Content-Type'] = content_type
        response.text = content
        return response
コード例 #4
0
    def post(self, pack_install_request, requester_user=None):
        parameters = {
            "packs": pack_install_request.packs,
        }

        if pack_install_request.force:
            parameters["force"] = True

        if pack_install_request.skip_dependencies:
            parameters["skip_dependencies"] = True

        if not requester_user:
            requester_user = UserDB(name=cfg.CONF.system_user.user)

        new_liveaction_api = LiveActionCreateAPI(
            action="packs.install", parameters=parameters, user=requester_user.name
        )

        execution_resp = self._handle_schedule_execution(
            liveaction_api=new_liveaction_api, requester_user=requester_user
        )

        exec_id = PackAsyncAPI(execution_id=execution_resp.json["id"])

        return Response(json=exec_id, status=http_client.ACCEPTED)
コード例 #5
0
ファイル: packs.py プロジェクト: zsjohny/st2
    def post(self, pack_install_request, requester_user=None):
        parameters = {
            'packs': pack_install_request.packs,
            'python3': pack_install_request.python3
        }

        if pack_install_request.force:
            parameters['force'] = True

        if pack_install_request.skip_dependencies:
            parameters['skip_dependencies'] = True

        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        new_liveaction_api = LiveActionCreateAPI(action='packs.install',
                                                 parameters=parameters,
                                                 user=requester_user.name)

        execution_resp = self._handle_schedule_execution(
            liveaction_api=new_liveaction_api, requester_user=requester_user)

        exec_id = PackAsyncAPI(execution_id=execution_resp.json['id'])

        return Response(json=exec_id, status=http_client.ACCEPTED)
コード例 #6
0
ファイル: rules.py プロジェクト: versus/st2
    def post(self, rule, requester_user):
        """
            Create a new rule.

            Handles requests:
                POST /rules/
        """

        permission_type = PermissionType.RULE_CREATE
        rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user,
                                                           resource_api=rule,
                                                           permission_type=permission_type)

        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        # Validate that the authenticated user is admin if user query param is provided
        user = requester_user.name
        assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user,
                                                             user=user)

        if not hasattr(rule, 'context'):
            rule.context = dict()

        rule.context['user'] = user

        try:
            rule_db = RuleAPI.to_model(rule)
            LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db)

            # Check referenced trigger and action permissions
            # Note: This needs to happen after "to_model" call since to_model performs some
            # validation (trigger exists, etc.)
            assert_user_has_rule_trigger_and_action_permission(user_db=requester_user,
                                                               rule_api=rule)

            rule_db = Rule.add_or_update(rule_db)
            # After the rule has been added modify the ref_count. This way a failure to add
            # the rule due to violated constraints will have no impact on ref_count.
            increment_trigger_ref_count(rule_api=rule)
        except (ValidationError, ValueError) as e:
            LOG.exception('Validation failed for rule data=%s.', rule)
            abort(http_client.BAD_REQUEST, str(e))
            return
        except (ValueValidationException, jsonschema.ValidationError) as e:
            LOG.exception('Validation failed for rule data=%s.', rule)
            abort(http_client.BAD_REQUEST, str(e))
            return
        except TriggerDoesNotExistException as e:
            msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing '
                   'required "parameters" attribute' % (rule.trigger['type']))
            LOG.exception(msg)
            abort(http_client.BAD_REQUEST, msg)
            return

        extra = {'rule_db': rule_db}
        LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra)
        rule_api = RuleAPI.from_model(rule_db)

        return Response(json=rule_api, status=exc.HTTPCreated.code)
コード例 #7
0
    def _get_one(self, ref_or_id, requester_user, permission_type, exclude_fields=None,
                 from_model_kwargs=None):
        try:
            instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields)
        except Exception as e:
            LOG.exception(e.message)
            abort(http_client.NOT_FOUND, e.message)
            return

        if permission_type:
            rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                              resource_db=instance,
                                                              permission_type=permission_type)

        from_model_kwargs = from_model_kwargs or {}
        from_model_kwargs.update(self.from_model_kwargs)
        result = self.resource_model_filter(model=self.model, instance=instance,
                                            requester_user=requester_user,
                                            **from_model_kwargs)

        if not result:
            LOG.debug('Not returning the result because RBAC resource isolation is enabled and '
                      'current user doesn\'t match the resource user')
            raise ResourceAccessDeniedPermissionIsolationError(user_db=requester_user,
                                                               resource_api_or_db=instance,
                                                               permission_type=permission_type)

        if result and self.include_reference:
            pack = getattr(result, 'pack', None)
            name = getattr(result, 'name', None)
            result.ref = ResourceReference(pack=pack, name=name).ref

        return Response(json=result)
コード例 #8
0
    def delete(self, triggertype_ref_or_id):
        """
            Delete a triggertype.

            Handles requests:
                DELETE /triggertypes/1
                DELETE /triggertypes/pack.name
        """
        LOG.info('DELETE /triggertypes/ with ref_or_id=%s',
                 triggertype_ref_or_id)

        triggertype_db = self._get_by_ref_or_id(ref_or_id=triggertype_ref_or_id)
        triggertype_id = triggertype_db.id

        try:
            validate_not_part_of_system_pack(triggertype_db)
        except ValueValidationException as e:
            abort(http_client.BAD_REQUEST, str(e))

        try:
            TriggerType.delete(triggertype_db)
        except Exception as e:
            LOG.exception('Database delete encountered exception during delete of id="%s". ',
                          triggertype_id)
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
            return
        else:
            extra = {'triggertype': triggertype_db}
            LOG.audit('TriggerType deleted. TriggerType.id=%s' % (triggertype_db.id), extra=extra)
            if not triggertype_db.parameters_schema:
                TriggerTypeController._delete_shadow_trigger(triggertype_db)

        return Response(status=http_client.NO_CONTENT)
コード例 #9
0
    def _get_one(self,
                 ref_or_id,
                 requester_user,
                 permission_type,
                 exclude_fields=None,
                 from_model_kwargs=None):
        try:
            instance = self._get_by_ref_or_id(ref_or_id=ref_or_id,
                                              exclude_fields=exclude_fields)
        except Exception as e:
            LOG.exception(e.message)
            abort(http_client.NOT_FOUND, e.message)
            return

        if permission_type:
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=instance,
                permission_type=permission_type)

        from_model_kwargs = from_model_kwargs or {}
        from_model_kwargs.update(self.from_model_kwargs)
        result = self.model.from_model(instance, **from_model_kwargs)
        if result and self.include_reference:
            pack = getattr(result, 'pack', None)
            name = getattr(result, 'name', None)
            result.ref = ResourceReference(pack=pack, name=name).ref

        return Response(json=result)
コード例 #10
0
ファイル: actionexecutions.py プロジェクト: tools-env/st2
    def get(self, id, attribute, requester_user):
        """
        Retrieve a particular attribute for the provided action execution.

        Handles requests:

            GET /executions/<id>/attribute/<attribute name>

        :rtype: ``dict``
        """
        fields = [attribute, 'action__pack', 'action__uid']

        try:
            fields = self._validate_exclude_fields(fields)
        except ValueError:
            valid_attributes = ', '.join(ActionExecutionsControllerMixin.valid_exclude_attributes)
            msg = ('Invalid attribute "%s" specified. Valid attributes are: %s' %
                   (attribute, valid_attributes))
            raise ValueError(msg)

        action_exec_db = self.access.impl.model.objects.filter(id=id).only(*fields).get()

        permission_type = PermissionType.EXECUTION_VIEW
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=action_exec_db,
                                                          permission_type=permission_type)

        result = getattr(action_exec_db, attribute, None)
        return Response(json=result, status=http_client.OK)
コード例 #11
0
ファイル: policies.py プロジェクト: rahmiy/st2
    def post(self, instance, requester_user):
        """
            Create a new policy.
            Handles requests:
                POST /policies/
        """
        permission_type = PermissionType.POLICY_CREATE
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_api_permission(
            user_db=requester_user,
            resource_api=instance,
            permission_type=permission_type)

        op = 'POST /policies/'

        db_model = self.model.to_model(instance)
        LOG.debug('%s verified object: %s', op, db_model)

        db_model = self.access.add_or_update(db_model)

        LOG.debug('%s created object: %s', op, db_model)
        LOG.audit('Policy created. Policy.id=%s' % (db_model.id),
                  extra={'policy_db': db_model})

        exec_result = self.model.from_model(db_model)

        return Response(json=exec_result, status=http_client.CREATED)
コード例 #12
0
ファイル: resource.py プロジェクト: tzmvp/st2
    def _get_one(self, ref_or_id, requester_user, permission_type, exclude_fields=None,
                 include_fields=None, from_model_kwargs=None):
        try:
            instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields,
                                              include_fields=include_fields)
        except Exception as e:
            LOG.exception(str(e))
            abort(http_client.NOT_FOUND, str(e))
            return

        if permission_type:
            rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                              resource_db=instance,
                                                              permission_type=permission_type)

        # Perform resource isolation check (if supported)
        from_model_kwargs = from_model_kwargs or {}
        from_model_kwargs.update(self.from_model_kwargs)

        result = self.resource_model_filter(model=self.model, instance=instance,
                                            requester_user=requester_user,
                                            **from_model_kwargs)

        if not result:
            LOG.debug('Not returning the result because RBAC resource isolation is enabled and '
                      'current user doesn\'t match the resource user')
            raise ResourceAccessDeniedPermissionIsolationError(user_db=requester_user,
                                                               resource_api_or_db=instance,
                                                               permission_type=permission_type)

        return Response(json=result)
コード例 #13
0
ファイル: webhooks.py プロジェクト: rahmiy/st2
    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)
コード例 #14
0
    def delete(self, ref_or_id, requester_user):
        """
            Delete an action alias.

            Handles requests:
                DELETE /actionalias/1
        """
        action_alias_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)
        LOG.debug('DELETE /actionalias/ lookup with id=%s found object: %s',
                  ref_or_id, action_alias_db)

        permission_type = PermissionType.ACTION_ALIAS_DELETE
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=action_alias_db,
            permission_type=permission_type)

        try:
            ActionAlias.delete(action_alias_db)
        except Exception as e:
            LOG.exception(
                'Database delete encountered exception during delete of id="%s".',
                ref_or_id)
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
            return

        extra = {'action_alias_db': action_alias_db}
        LOG.audit('Action alias deleted. ActionAlias.id=%s.' %
                  (action_alias_db.id),
                  extra=extra)

        return Response(status=http_client.NO_CONTENT)
コード例 #15
0
    def post(self, triggertype):
        """
        Create a new triggertype.

        Handles requests:
            POST /triggertypes/
        """

        try:
            triggertype_db = TriggerTypeAPI.to_model(triggertype)
            triggertype_db = TriggerType.add_or_update(triggertype_db)
        except (ValidationError, ValueError) as e:
            LOG.exception("Validation failed for triggertype data=%s.", triggertype)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return
        else:
            extra = {"triggertype_db": triggertype_db}
            LOG.audit(
                "TriggerType created. TriggerType.id=%s" % (triggertype_db.id),
                extra=extra,
            )
            if not triggertype_db.parameters_schema:
                TriggerTypeController._create_shadow_trigger(triggertype_db)

        triggertype_api = TriggerTypeAPI.from_model(triggertype_db)

        return Response(json=triggertype_api, status=http_client.CREATED)
コード例 #16
0
    def test_install(self, _handle_schedule_execution):
        user_db = self.users['system_admin']
        self.use_user(user_db)

        _handle_schedule_execution.return_value = Response(json={'id': '123'})
        payload = {'packs': ['some']}

        resp = self.app.post_json('/v1/packs/install', payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {'execution_id': '123'})

        # Verify created execution correctly used the user which performed the API operation
        call_kwargs = _handle_schedule_execution.call_args[1]
        self.assertEqual(call_kwargs['requester_user'], user_db)
        self.assertEqual(call_kwargs['liveaction_api'].user, user_db.name)

        # Try with a different user
        user_db = self.users['admin']
        self.use_user(user_db)

        resp = self.app.post_json('/v1/packs/install', payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {'execution_id': '123'})

        # Verify created execution correctly used the user which performed the API operation
        call_kwargs = _handle_schedule_execution.call_args[1]
        self.assertEqual(call_kwargs['requester_user'], user_db)
        self.assertEqual(call_kwargs['liveaction_api'].user, user_db.name)
コード例 #17
0
    def delete(self, ref_or_id):
        """
            Delete a policy.
            Handles requests:
                POST /policies/1?_method=delete
                DELETE /policies/1
                DELETE /policies/mypack.mypolicy
        """
        op = 'DELETE /policies/%s/' % ref_or_id

        db_model = self._get_by_ref_or_id(ref_or_id=ref_or_id)
        LOG.debug('%s found object: %s', op, db_model)

        try:
            validate_not_part_of_system_pack(db_model)
        except ValueValidationException as e:
            LOG.exception('%s unable to delete object from system pack.', op)
            abort(http_client.BAD_REQUEST, str(e))

        try:
            self.access.delete(db_model)
        except Exception as e:
            LOG.exception('%s unable to delete object: %s', op, db_model)
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
            return

        LOG.debug('%s deleted object: %s', op, db_model)
        LOG.audit('Policy deleted. Policy.id=%s' % (db_model.id), extra={'policy_db': db_model})

        # return None
        return Response(status=http_client.NO_CONTENT)
コード例 #18
0
    def put(self, instance, ref_or_id):
        op = 'PUT /policies/%s/' % ref_or_id

        db_model = self._get_by_ref_or_id(ref_or_id=ref_or_id)
        LOG.debug('%s found object: %s', op, db_model)
        db_model_id = db_model.id

        try:
            validate_not_part_of_system_pack(db_model)
        except ValueValidationException as e:
            LOG.exception('%s unable to update object from system pack.', op)
            abort(http_client.BAD_REQUEST, str(e))

        if not getattr(instance, 'pack', None):
            instance.pack = db_model.pack

        try:
            db_model = self.model.to_model(instance)
            db_model.id = db_model_id
            db_model = self.access.add_or_update(db_model)
        except (ValidationError, ValueError) as e:
            LOG.exception('%s unable to update object: %s', op, db_model)
            abort(http_client.BAD_REQUEST, str(e))
            return

        LOG.debug('%s updated object: %s', op, db_model)
        LOG.audit('Policy updated. Policy.id=%s' % (db_model.id), extra={'policy_db': db_model})

        exec_result = self.model.from_model(db_model)

        return Response(json=exec_result, status=http_client.OK)
コード例 #19
0
    def post(self, action_alias, requester_user):
        """
            Create a new ActionAlias.

            Handles requests:
                POST /actionalias/
        """

        permission_type = PermissionType.ACTION_ALIAS_CREATE
        rbac_utils.assert_user_has_resource_api_permission(
            user_db=requester_user,
            resource_api=action_alias,
            permission_type=permission_type)

        try:
            action_alias_db = ActionAliasAPI.to_model(action_alias)
            LOG.debug(
                '/actionalias/ POST verified ActionAliasAPI and formulated ActionAliasDB=%s',
                action_alias_db)
            action_alias_db = ActionAlias.add_or_update(action_alias_db)
        except (ValidationError, ValueError, ValueValidationException) as e:
            LOG.exception('Validation failed for action alias data=%s.',
                          action_alias)
            abort(http_client.BAD_REQUEST, str(e))
            return

        extra = {'action_alias_db': action_alias_db}
        LOG.audit('Action alias created. ActionAlias.id=%s' %
                  (action_alias_db.id),
                  extra=extra)
        action_alias_api = ActionAliasAPI.from_model(action_alias_db)

        return Response(json=action_alias_api, status=http_client.CREATED)
コード例 #20
0
ファイル: rules.py プロジェクト: scutojr/st2
    def delete(self, rule_ref_or_id, requester_user):
        """
            Delete a rule.

            Handles requests:
                DELETE /rules/1
        """
        rule_db = self._get_by_ref_or_id(ref_or_id=rule_ref_or_id)

        permission_type = PermissionType.RULE_DELETE
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=rule_db,
            permission_type=permission_type)

        LOG.debug('DELETE /rules/ lookup with id=%s found object: %s',
                  rule_ref_or_id, rule_db)
        try:
            Rule.delete(rule_db)
        except Exception as e:
            LOG.exception(
                'Database delete encountered exception during delete of id="%s".',
                rule_ref_or_id)
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
            return

        # use old_rule_db for cleanup.
        cleanup_trigger_db_for_rule(rule_db)

        extra = {'rule_db': rule_db}
        LOG.audit('Rule deleted. Rule.id=%s.' % (rule_db.id), extra=extra)

        return Response(status=http_client.NO_CONTENT)
コード例 #21
0
    def test_uninstall(self, _handle_schedule_execution):
        _handle_schedule_execution.return_value = Response(json={"id": "123"})
        payload = {"packs": ["some"]}

        resp = self.app.post_json("/v1/packs/uninstall", payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {"execution_id": "123"})
コード例 #22
0
 def make_response():
     listener = get_listener()
     app_iter = format(
         listener.generator(events=events,
                            action_refs=action_refs,
                            execution_ids=execution_ids))
     res = Response(content_type='text/event-stream', app_iter=app_iter)
     return res
コード例 #23
0
ファイル: test_packs.py プロジェクト: ulakarapu/st2
    def test_install_with_force_parameter(self, _handle_schedule_execution):
        _handle_schedule_execution.return_value = Response(json={'id': '123'})
        payload = {'packs': ['some'], 'force': True}

        resp = self.app.post_json('/v1/packs/install', payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {'execution_id': '123'})
コード例 #24
0
    def test_install_with_force_parameter(self, _handle_schedule_execution):
        _handle_schedule_execution.return_value = Response(json={"id": "123"})
        payload = {"packs": ["some"], "force": True}

        resp = self.app.post_json("/v1/packs/install", payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {"execution_id": "123"})
コード例 #25
0
    def post(self, def_yaml):
        result = self.validator.validate(def_yaml)

        for error in result:
            if not error.get('path', None):
                error['path'] = ''

        return Response(json=result)
コード例 #26
0
ファイル: test_packs.py プロジェクト: ulakarapu/st2
    def test_uninstall(self, _handle_schedule_execution):
        _handle_schedule_execution.return_value = Response(json={'id': '123'})
        payload = {'packs': ['some']}

        resp = self.app.post_json('/v1/packs/uninstall', payload)

        self.assertEqual(resp.status_int, 202)
        self.assertEqual(resp.json, {'execution_id': '123'})
コード例 #27
0
    def _schedule_execution(self,
                            liveaction,
                            user=None,
                            context_string=None,
                            show_secrets=False):
        # Initialize execution context if it does not exist.
        if not hasattr(liveaction, 'context'):
            liveaction.context = dict()

        liveaction.context['user'] = user
        LOG.debug('User is: %s' % liveaction.context['user'])

        # Retrieve other st2 context from request header.
        if context_string:
            context = try_loads(context_string)
            if not isinstance(context, dict):
                raise ValueError(
                    'Unable to convert st2-context from the headers into JSON.'
                )
            liveaction.context.update(context)

        # Schedule the action execution.
        liveaction_db = LiveActionAPI.to_model(liveaction)
        liveaction_db, actionexecution_db = action_service.create_request(
            liveaction_db)

        action_db = action_utils.get_action_by_ref(liveaction_db.action)
        runnertype_db = action_utils.get_runnertype_by_name(
            action_db.runner_type['name'])

        try:
            liveaction_db.parameters = param_utils.render_live_params(
                runnertype_db.runner_parameters, action_db.parameters,
                liveaction_db.parameters, liveaction_db.context)
        except ParamException:
            # By this point the execution is already in the DB therefore need to mark it failed.
            _, e, tb = sys.exc_info()
            action_service.update_status(liveaction=liveaction_db,
                                         new_status=LIVEACTION_STATUS_FAILED,
                                         result={
                                             'error':
                                             str(e),
                                             'traceback':
                                             ''.join(
                                                 traceback.format_tb(tb, 20))
                                         })
            # Might be a good idea to return the actual ActionExecution rather than bubble up
            # the execption.
            raise ValueValidationException(str(e))

        liveaction_db = LiveAction.add_or_update(liveaction_db, publish=False)

        _, actionexecution_db = action_service.publish_request(
            liveaction_db, actionexecution_db)
        execution_api = ActionExecutionAPI.from_model(
            actionexecution_db, mask_secrets=(not show_secrets))

        return Response(json=execution_api, status=http_client.CREATED)
コード例 #28
0
    def post(self, api_key_api, requester_user):
        """
        Create a new entry.
        """

        permission_type = PermissionType.API_KEY_CREATE
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_api_permission(
            user_db=requester_user,
            resource_api=api_key_api,
            permission_type=permission_type,
        )

        api_key_db = None
        api_key = None
        try:
            if not getattr(api_key_api, "user", None):
                if requester_user:
                    api_key_api.user = requester_user.name
                else:
                    api_key_api.user = cfg.CONF.system_user.user

            try:
                User.get_by_name(api_key_api.user)
            except StackStormDBObjectNotFoundError:
                user_db = UserDB(name=api_key_api.user)
                User.add_or_update(user_db)

                extra = {"username": api_key_api.user, "user": user_db}
                LOG.audit('Registered new user "%s".' % (api_key_api.user),
                          extra=extra)

            # If key_hash is provided use that and do not create a new key. The assumption
            # is user already has the original api-key
            if not getattr(api_key_api, "key_hash", None):
                api_key, api_key_hash = auth_util.generate_api_key_and_hash()
                # store key_hash in DB
                api_key_api.key_hash = api_key_hash
            api_key_db = ApiKey.add_or_update(ApiKeyAPI.to_model(api_key_api))
        except (ValidationError, ValueError) as e:
            LOG.exception("Validation failed for api_key data=%s.",
                          api_key_api)
            abort(http_client.BAD_REQUEST, six.text_type(e))

        extra = {"api_key_db": api_key_db}
        LOG.audit("ApiKey created. ApiKey.id=%s" % (api_key_db.id),
                  extra=extra)

        api_key_create_response_api = ApiKeyCreateResponseAPI.from_model(
            api_key_db)
        # Return real api_key back to user. A one-way hash of the api_key is stored in the DB
        # only the real value only returned at create time. Also, no masking of key here since
        # the user needs to see this value atleast once.
        api_key_create_response_api.key = api_key

        return Response(json=api_key_create_response_api,
                        status=http_client.CREATED)
コード例 #29
0
 def make_response():
     app_iter = itertools.chain(existing_output_iter(),
                                new_output_iter())
     res = Response(headerlist=[("X-Accel-Buffering", "no"),
                                ('Cache-Control', 'no-cache'),
                                ("Content-Type",
                                 "text/event-stream; charset=UTF-8")],
                    app_iter=app_iter)
     return res
コード例 #30
0
    def __call__(self, environ, start_response):
        request = Request(environ)

        def custom_start_response(status, headers, exc_info=None):
            headers = ResponseHeaders(headers)

            origin = request.headers.get('Origin')
            origins = set(cfg.CONF.api.allow_origin)

            # Build a list of the default allowed origins
            public_api_url = cfg.CONF.auth.api_url

            # Default gulp development server WebUI URL
            origins.add('http://127.0.0.1:3000')

            # By default WebUI simple http server listens on 8080
            origins.add('http://localhost:8080')
            origins.add('http://127.0.0.1:8080')

            if public_api_url:
                # Public API URL
                origins.add(public_api_url)

            if origin:
                if '*' in origins:
                    origin_allowed = '*'
                else:
                    # See http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
                    origin_allowed = origin if origin in origins else 'null'
            else:
                origin_allowed = list(origins)[0]

            methods_allowed = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
            request_headers_allowed = [
                'Content-Type', 'Authorization', HEADER_ATTRIBUTE_NAME,
                HEADER_API_KEY_ATTRIBUTE_NAME, REQUEST_ID_HEADER
            ]
            response_headers_allowed = [
                'Content-Type', 'X-Limit', 'X-Total-Count', REQUEST_ID_HEADER
            ]

            headers['Access-Control-Allow-Origin'] = origin_allowed
            headers['Access-Control-Allow-Methods'] = ','.join(methods_allowed)
            headers['Access-Control-Allow-Headers'] = ','.join(
                request_headers_allowed)
            headers['Access-Control-Expose-Headers'] = ','.join(
                response_headers_allowed)

            return start_response(status, headers._items, exc_info)

        try:
            return self.app(environ, custom_start_response)
        except NotFoundException:
            if request.method != 'options':
                raise

            return Response()(environ, custom_start_response)
コード例 #31
0
    def delete(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None):
        """
        Delete the key value pair.

        Handles requests:
            DELETE /keys/1
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)

        user = user or requester_user.name

        # Validate that the authenticated user is admin if user query param is provided
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin_if_user_query_param_is_provided(
            user_db=requester_user, user=user, require_rbac=True)

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope)

        # Note: We use lock to avoid a race
        with self._coordinator.get_lock(lock_name):
            from_model_kwargs = {"mask_secrets": True}
            kvp_api = self._get_one_by_scope_and_name(
                name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs)

            kvp_db = KeyValuePairAPI.to_model(kvp_api)

            LOG.debug(
                "DELETE /keys/ lookup with scope=%s name=%s found object: %s",
                scope,
                name,
                kvp_db,
            )

            try:
                KeyValuePair.delete(kvp_db)
            except Exception as e:
                LOG.exception(
                    "Database delete encountered exception during "
                    'delete of name="%s". ',
                    name,
                )
                abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
                return

        extra = {"kvp_db": kvp_db}
        LOG.audit("KeyValuePair deleted. KeyValuePair.id=%s" % (kvp_db.id),
                  extra=extra)

        return Response(status=http_client.NO_CONTENT)
コード例 #32
0
    def __call__(self, environ, start_response):
        # The middleware sets a number of headers that helps prevent a range of attacks in browser
        # environment. It also handles OPTIONS requests used by browser as pre-flight check before
        # the potentially insecure request is made. An absence of this headers on the response will
        # prevent the error from ever reaching the JS layer of client-side code making it impossible
        # to process the response or provide a human-friendly error message. Order is not important
        # as long at this condition is met and headers not get overridden by another middleware
        # higher up the call stack.
        request = Request(environ)

        def custom_start_response(status, headers, exc_info=None):
            headers = ResponseHeaders(headers)

            origin = request.headers.get('Origin')
            origins = set(cfg.CONF.api.allow_origin)

            # Build a list of the default allowed origins
            public_api_url = cfg.CONF.auth.api_url

            # Default gulp development server WebUI URL
            origins.add('http://127.0.0.1:3000')

            # By default WebUI simple http server listens on 8080
            origins.add('http://localhost:8080')
            origins.add('http://127.0.0.1:8080')

            if public_api_url:
                # Public API URL
                origins.add(public_api_url)

            if origin:
                if '*' in origins:
                    origin_allowed = '*'
                else:
                    # See http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
                    origin_allowed = origin if origin in origins else 'null'
            else:
                origin_allowed = list(origins)[0]

            methods_allowed = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
            request_headers_allowed = ['Content-Type', 'Authorization', HEADER_ATTRIBUTE_NAME,
                                       HEADER_API_KEY_ATTRIBUTE_NAME, REQUEST_ID_HEADER]
            response_headers_allowed = ['Content-Type', 'X-Limit', 'X-Total-Count',
                                        REQUEST_ID_HEADER]

            headers['Access-Control-Allow-Origin'] = origin_allowed
            headers['Access-Control-Allow-Methods'] = ','.join(methods_allowed)
            headers['Access-Control-Allow-Headers'] = ','.join(request_headers_allowed)
            headers['Access-Control-Expose-Headers'] = ','.join(response_headers_allowed)

            return start_response(status, headers._items, exc_info)

        if request.method == 'OPTIONS':
            return Response()(environ, custom_start_response)
        else:
            return self.app(environ, custom_start_response)
コード例 #33
0
    def get_one(self, ref_or_id, file_path, requester_user, **kwargs):
        """
            Outputs the content of a specific file in a pack.

            Handles requests:
                GET /packs/views/file/<pack_ref_or_id>/<file path>
        """
        pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        if not pack_db:
            msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id)
            raise StackStormDBObjectNotFoundError(msg)

        if not file_path:
            raise ValueError('Missing file path')

        pack_ref = pack_db.ref

        # Note: Until list filtering is in place we don't require RBAC check for icon file
        permission_type = PermissionType.PACK_VIEW
        if file_path not in WHITELISTED_FILE_PATHS:
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=pack_db,
                permission_type=permission_type)

        normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref,
                                                      file_path=file_path)
        if not normalized_file_path or not os.path.isfile(
                normalized_file_path):
            # Ignore references to files which don't exist on disk
            raise StackStormDBObjectNotFoundError('File "%s" not found' %
                                                  (file_path))

        file_size, file_mtime = self._get_file_stats(
            file_path=normalized_file_path)

        response = Response()

        if not self._is_file_changed(file_mtime, **kwargs):
            response.status = http_client.NOT_MODIFIED
        else:
            if file_size is not None and file_size > MAX_FILE_SIZE:
                msg = ('File %s exceeds maximum allowed file size (%s bytes)' %
                       (file_path, MAX_FILE_SIZE))
                raise ValueError(msg)

            content_type = mimetypes.guess_type(normalized_file_path)[0] or \
                'application/octet-stream'

            response.headers['Content-Type'] = content_type
            response.body = self._get_file_content(
                file_path=normalized_file_path)

        response.headers['Last-Modified'] = format_date_time(file_mtime)
        response.headers['ETag'] = repr(file_mtime)

        return response
コード例 #34
0
ファイル: pack_views.py プロジェクト: lyandut/st2
    def get_one(self, ref_or_id, file_path, requester_user, if_none_match=None,
                if_modified_since=None):
        """
            Outputs the content of a specific file in a pack.

            Handles requests:
                GET /packs/views/file/<pack_ref_or_id>/<file path>
        """
        pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        if not pack_db:
            msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id)
            raise StackStormDBObjectNotFoundError(msg)

        if not file_path:
            raise ValueError('Missing file path')

        pack_ref = pack_db.ref

        # Note: Until list filtering is in place we don't require RBAC check for icon file
        permission_type = PermissionType.PACK_VIEW
        if file_path not in WHITELISTED_FILE_PATHS:
            rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                              resource_db=pack_db,
                                                              permission_type=permission_type)

        normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref, file_path=file_path)
        if not normalized_file_path or not os.path.isfile(normalized_file_path):
            # Ignore references to files which don't exist on disk
            raise StackStormDBObjectNotFoundError('File "%s" not found' % (file_path))

        file_size, file_mtime = self._get_file_stats(file_path=normalized_file_path)

        response = Response()

        if not self._is_file_changed(file_mtime,
                                     if_none_match=if_none_match,
                                     if_modified_since=if_modified_since):
            response.status = http_client.NOT_MODIFIED
        else:
            if file_size is not None and file_size > MAX_FILE_SIZE:
                msg = ('File %s exceeds maximum allowed file size (%s bytes)' %
                       (file_path, MAX_FILE_SIZE))
                raise ValueError(msg)

            content_type = mimetypes.guess_type(normalized_file_path)[0] or \
                'application/octet-stream'

            response.headers['Content-Type'] = content_type
            response.body = self._get_file_content(file_path=normalized_file_path)

        response.headers['Last-Modified'] = format_date_time(file_mtime)
        response.headers['ETag'] = repr(file_mtime)

        return response
コード例 #35
0
ファイル: resource.py プロジェクト: lyandut/st2
    def _get_all(self, exclude_fields=None, include_fields=None, advanced_filters=None,
                 sort=None, offset=0, limit=None, query_options=None,
                 from_model_kwargs=None, raw_filters=None, requester_user=None):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """
        raw_filters = copy.deepcopy(raw_filters) or {}

        exclude_fields = exclude_fields or []
        include_fields = include_fields or []
        query_options = query_options if query_options else self.query_options

        # TODO: Why do we use comma delimited string, user can just specify
        # multiple values using ?sort=foo&sort=bar and we get a list back
        sort = sort.split(',') if sort else []

        db_sort_values = []
        for sort_key in sort:
            if sort_key.startswith('-'):
                direction = '-'
                sort_key = sort_key[1:]
            elif sort_key.startswith('+'):
                direction = '+'
                sort_key = sort_key[1:]
            else:
                direction = ''

            if sort_key not in self.supported_filters:
                # Skip unsupported sort key
                continue

            sort_value = direction + self.supported_filters[sort_key]
            db_sort_values.append(sort_value)

        default_sort_values = copy.copy(query_options.get('sort'))
        raw_filters['sort'] = db_sort_values if db_sort_values else default_sort_values

        # TODO: To protect us from DoS, we need to make max_limit mandatory
        offset = int(offset)
        if offset >= 2**31:
            raise ValueError('Offset "%s" specified is more than 32-bit int' % (offset))

        limit = validate_limit_query_param(limit=limit, requester_user=requester_user)
        eop = offset + int(limit) if limit else None

        filters = {}
        for k, v in six.iteritems(self.supported_filters):
            filter_value = raw_filters.get(k, None)

            if not filter_value:
                continue

            value_transform_function = self.filter_transform_functions.get(k, None)
            value_transform_function = value_transform_function or (lambda value: value)
            filter_value = value_transform_function(value=filter_value)

            if k in ['id', 'name'] and isinstance(filter_value, list):
                filters[k + '__in'] = filter_value
            else:
                field_name_split = v.split('.')

                # Make sure filter value is a list when using "in" filter
                if field_name_split[-1] == 'in' and not isinstance(filter_value, (list, tuple)):
                    filter_value = [filter_value]

                filters['__'.join(field_name_split)] = filter_value

        if advanced_filters:
            for token in advanced_filters.split(' '):
                try:
                    [k, v] = token.split(':', 1)
                except ValueError:
                    raise ValueError('invalid format for filter "%s"' % token)
                path = k.split('.')
                try:
                    self.model.model._lookup_field(path)
                    filters['__'.join(path)] = v
                except LookUpError as e:
                    raise ValueError(str(e))

        if exclude_fields and include_fields:
            msg = ('exclude_fields and include_fields arguments are mutually exclusive. '
                   'You need to provide either one or another, but not both.')
            raise ValueError(msg)

        instances = self.access.query(exclude_fields=exclude_fields, only_fields=include_fields,
                                      **filters)
        if limit == 1:
            # Perform the filtering on the DB side
            instances = instances.limit(limit)

        from_model_kwargs = from_model_kwargs or {}
        from_model_kwargs.update(self.from_model_kwargs)

        result = self.resources_model_filter(model=self.model,
                                             instances=instances,
                                             offset=offset,
                                             eop=eop,
                                             requester_user=requester_user,
                                             **from_model_kwargs)

        resp = Response(json=result)
        resp.headers['X-Total-Count'] = str(instances.count())

        if limit:
            resp.headers['X-Limit'] = str(limit)

        return resp
コード例 #36
0
ファイル: auth.py プロジェクト: StackStorm/st2
def process_successful_response(token):
    resp = Response(json=token, status=http_client.CREATED)
    resp.headers['X-API-URL'] = cfg.CONF.auth.api_url
    return resp