Esempio n. 1
0
    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)
Esempio n. 2
0
    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 = get_rbac_backend().get_utils_class()
        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, six.text_type(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)
Esempio n. 3
0
    def put(self, runner_type_api, name_or_id, requester_user):
        # Note: We only allow "enabled" attribute of the runner to be changed
        runner_type_db = self._get_by_name_or_id(name_or_id=name_or_id)

        permission_type = PermissionType.RUNNER_MODIFY
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=runner_type_db,
                                                          permission_type=permission_type)

        old_runner_type_db = runner_type_db
        LOG.debug('PUT /runnertypes/ lookup with id=%s found object: %s', name_or_id,
                  runner_type_db)

        try:
            if runner_type_api.id and runner_type_api.id != name_or_id:
                LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.',
                            runner_type_api.id, name_or_id)

            runner_type_db.enabled = runner_type_api.enabled
            runner_type_db = RunnerType.add_or_update(runner_type_db)
        except (ValidationError, ValueError) as e:
            LOG.exception('Validation failed for runner type data=%s', runner_type_api)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        extra = {'old_runner_type_db': old_runner_type_db, 'new_runner_type_db': runner_type_db}
        LOG.audit('Runner Type updated. RunnerType.id=%s.' % (runner_type_db.id), extra=extra)
        runner_type_api = RunnerTypeAPI.from_model(runner_type_db)
        return runner_type_api
Esempio n. 4
0
    def get_one(self, api_key_id_or_key, requester_user, show_secrets=None):
        """
            List api keys.

            Handle:
                GET /apikeys/1
        """
        api_key_db = None
        try:
            api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key)
        except ApiKeyNotFoundError:
            msg = ('ApiKey matching %s for reference and id not found.' % (api_key_id_or_key))
            LOG.exception(msg)
            abort(http_client.NOT_FOUND, msg)

        permission_type = PermissionType.API_KEY_VIEW
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=api_key_db,
                                                          permission_type=permission_type)

        try:
            mask_secrets = self._get_mask_secrets(show_secrets=show_secrets,
                                                  requester_user=requester_user)
            return ApiKeyAPI.from_model(api_key_db, mask_secrets=mask_secrets)
        except (ValidationError, ValueError) as e:
            LOG.exception('Failed to serialize API key.')
            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
Esempio n. 5
0
    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(six.text_type(e))
            abort(http_client.NOT_FOUND, six.text_type(e))
            return

        if permission_type:
            rbac_utils = get_rbac_backend().get_utils_class()
            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)
Esempio n. 6
0
    def post(self, action_alias, requester_user):
        """
            Create a new ActionAlias.

            Handles requests:
                POST /actionalias/
        """

        permission_type = PermissionType.ACTION_ALIAS_CREATE
        rbac_utils = get_rbac_backend().get_utils_class()
        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, six.text_type(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)
Esempio n. 7
0
    def get_one(self, group_id, requester_user):
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        coordinator = coordination.get_coordinator()

        if not isinstance(group_id, six.binary_type):
            group_id = group_id.encode('utf-8')

        try:
            member_ids = list(coordinator.get_members(group_id).get())
        except GroupNotCreated:
            msg = ('Group with ID "%s" not found.' % (group_id.decode('utf-8')))
            raise StackStormDBObjectNotFoundError(msg)

        result = {
            'members': []
        }

        for member_id in member_ids:
            capabilities = coordinator.get_member_capabilities(group_id, member_id).get()
            item = {
                'group_id': group_id.decode('utf-8'),
                'member_id': member_id.decode('utf-8'),
                'capabilities': capabilities
            }
            result['members'].append(item)

        return result
Esempio n. 8
0
    def _get_one_by_name_or_id(self, name_or_id, requester_user, permission_type,
                               exclude_fields=None, include_fields=None, from_model_kwargs=None):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        :param include_fields: A list of object fields to include.
        :type include_fields: ``list``
        """

        instance = self._get_by_name_or_id(name_or_id=name_or_id, exclude_fields=exclude_fields,
                                           include_fields=include_fields)

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

        if not instance:
            msg = 'Unable to identify resource with name_or_id "%s".' % (name_or_id)
            abort(http_client.NOT_FOUND, msg)

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

        return result
Esempio n. 9
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 = get_rbac_backend().get_utils_class()
        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, six.text_type(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)
Esempio n. 10
0
    def put(self, rule, rule_ref_or_id, requester_user):
        rule_db = self._get_by_ref_or_id(rule_ref_or_id)

        rbac_utils = get_rbac_backend().get_utils_class()
        permission_type = PermissionType.RULE_MODIFY
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=rule,
                                                          permission_type=permission_type)

        LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db)

        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
        rbac_utils.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:
            if rule.id is not None and rule.id != '' and rule.id != rule_ref_or_id:
                LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.',
                            rule.id, rule_ref_or_id)
            old_rule_db = rule_db

            try:
                rule_db = RuleAPI.to_model(rule)
            except TriggerDoesNotExistException as e:
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return

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

            rule_db.id = rule_ref_or_id
            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 (ValueValidationException, jsonschema.ValidationError, ValueError) as e:
            LOG.exception('Validation failed for rule data=%s', rule)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        # use old_rule_db for cleanup.
        cleanup_trigger_db_for_rule(old_rule_db)

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

        return rule_api
Esempio n. 11
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)
Esempio n. 12
0
    def _get_one(self, ref_or_id, requester_user):
        instance = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        permission_type = PermissionType.POLICY_TYPE_VIEW
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=instance,
                                                          permission_type=permission_type)

        result = self.model.from_model(instance)
        return result
Esempio n. 13
0
    def post(self, action, requester_user):
        """
        Create a new action.

        Handles requests:
            POST /actions/
        """

        permission_type = PermissionType.ACTION_CREATE
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_api_permission(
            user_db=requester_user,
            resource_api=action,
            permission_type=permission_type)

        try:
            # Perform validation
            validate_not_part_of_system_pack(action)
            action_validator.validate_action(action)
        except (
                ValidationError,
                ValueError,
                ValueValidationException,
                InvalidActionParameterException,
        ) as e:
            LOG.exception("Unable to create action data=%s", action)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        # Write pack data files to disk (if any are provided)
        data_files = getattr(action, "data_files", [])
        written_data_files = []
        if data_files:
            written_data_files = self._handle_data_files(pack_ref=action.pack,
                                                         data_files=data_files)

        action_model = ActionAPI.to_model(action)

        LOG.debug("/actions/ POST verified ActionAPI object=%s", action)
        action_db = Action.add_or_update(action_model)
        LOG.debug("/actions/ POST saved ActionDB object=%s", action_db)

        # Dispatch an internal trigger for each written data file. This way user
        # automate comitting this files to git using StackStorm rule
        if written_data_files:
            self._dispatch_trigger_for_written_data_files(
                action_db=action_db, written_data_files=written_data_files)

        extra = {"acion_db": action_db}
        LOG.audit("Action created. Action.id=%s" % (action_db.id), extra=extra)
        action_api = ActionAPI.from_model(action_db)

        return Response(json=action_api, status=http_client.CREATED)
Esempio n. 14
0
    def get_all(self, requester_user):
        """
            List all the available permission types.

            Handles requests:
                GET /rbac/permission_types
        """
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        result = get_resource_permission_types_with_descriptions()
        return result
Esempio n. 15
0
    def _validate_decrypt_query_parameter(self, decrypt, scope, requester_user):
        """
        Validate that the provider user is either admin or requesting to decrypt value for
        themselves.
        """
        rbac_utils = get_rbac_backend().get_utils_class()
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)
        is_user_scope = (scope == USER_SCOPE or scope == FULL_USER_SCOPE)

        if decrypt and (not is_user_scope and not is_admin):
            msg = 'Decrypt option requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)
Esempio n. 16
0
File: auth.py Progetto: zwunix/st2
    def get_roles(self, include_remote=True):
        """
        Retrieve roles assigned to that user.

        :param include_remote: True to also include remote role assignments.
        :type include_remote: ``bool``

        :rtype: ``list`` of :class:`RoleDB`
        """
        rbac_service = get_rbac_backend().get_service_class()
        result = rbac_service.get_roles_for_user(user_db=self, include_remote=include_remote)
        return result
Esempio n. 17
0
    def _schedule_execution(self, action_alias_db, params, notify, context,
                            requester_user, show_secrets):
        action_ref = action_alias_db.action_ref
        action_db = action_utils.get_action_by_ref(action_ref)

        if not action_db:
            raise StackStormDBObjectNotFoundError(
                'Action with ref "%s" not found ' % (action_ref))

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

        try:
            # prior to shipping off the params cast them to the right type.
            params = action_param_utils.cast_params(
                action_ref=action_alias_db.action_ref,
                params=params,
                cast_overrides=CAST_OVERRIDES,
            )
            if not context:
                context = {
                    "action_alias_ref":
                    reference.get_ref_from_model(action_alias_db),
                    "user":
                    get_system_username(),
                }
            liveaction = LiveActionDB(
                action=action_alias_db.action_ref,
                context=context,
                parameters=params,
                notify=notify,
            )
            _, action_execution_db = action_service.request(liveaction)
            mask_secrets = self._get_mask_secrets(requester_user,
                                                  show_secrets=show_secrets)
            return ActionExecutionAPI.from_model(action_execution_db,
                                                 mask_secrets=mask_secrets)
        except ValueError as e:
            LOG.exception("Unable to execute action.")
            abort(http_client.BAD_REQUEST, six.text_type(e))
        except jsonschema.ValidationError as e:
            LOG.exception(
                "Unable to execute action. Parameter validation failed.")
            abort(http_client.BAD_REQUEST, six.text_type(e))
        except Exception as e:
            LOG.exception(
                "Unable to execute action. Unexpected error encountered.")
            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
Esempio n. 18
0
    def validate(self, validate_role_exists=False):
        # Parent JSON schema validation
        cleaned = super(BaseRoleAssigmentAPI, self).validate()

        # Custom validation
        if validate_role_exists:
            # Validate that the referenced roles exist in the db
            rbac_service = get_rbac_backend().get_service_class()
            # pylint: disable=no-member
            rbac_service.validate_roles_exists(role_names=self.roles)
            # pylint: enable=no-member
        return cleaned
Esempio n. 19
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)
Esempio n. 20
0
    def _validate_all_scope(self, scope, requester_user):
        """
        Validate that "all" scope can only be provided by admins on RBAC installations.
        """
        scope = get_datastore_full_scope(scope)
        is_all_scope = (scope == ALL_SCOPE)
        rbac_utils = get_rbac_backend().get_utils_class()
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)

        if is_all_scope and not is_admin:
            msg = '"all" scope requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)
Esempio n. 21
0
    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
Esempio n. 22
0
    def _get_one(self, ref_or_id, requester_user):
        instance = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        permission_type = PermissionType.POLICY_TYPE_VIEW
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=instance,
            permission_type=permission_type)

        result = self.model.from_model(instance)
        return result
Esempio n. 23
0
    def _validate_all_scope(self, scope, requester_user):
        """
        Validate that "all" scope can only be provided by admins on RBAC installations.
        """
        scope = get_datastore_full_scope(scope)
        is_all_scope = (scope == ALL_SCOPE)
        rbac_utils = get_rbac_backend().get_utils_class()
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)

        if is_all_scope and not is_admin:
            msg = '"all" scope requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)
Esempio n. 24
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)
Esempio n. 25
0
    def get_all(self, requester_user):
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        coordinator = coordination.get_coordinator()

        group_ids = list(coordinator.get_groups().get())
        group_ids = [item.decode('utf-8') for item in group_ids]

        result = {
            'groups': group_ids
        }
        return result
Esempio n. 26
0
    def get_all(self, requester_user):
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        coordinator = coordination.get_coordinator()

        group_ids = list(coordinator.get_groups().get())
        group_ids = [item.decode('utf-8') for item in group_ids]

        result = {
            'groups': group_ids
        }
        return result
Esempio n. 27
0
    def _validate_decrypt_query_parameter(self, decrypt, scope,
                                          requester_user):
        """
        Validate that the provider user is either admin or requesting to decrypt value for
        themselves.
        """
        rbac_utils = get_rbac_backend().get_utils_class()
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)
        is_user_scope = (scope == USER_SCOPE or scope == FULL_USER_SCOPE)

        if decrypt and (not is_user_scope and not is_admin):
            msg = 'Decrypt option requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)
Esempio n. 28
0
    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(six.text_type(e))
            abort(http_client.NOT_FOUND, six.text_type(e))
            return

        if permission_type:
            rbac_utils = get_rbac_backend().get_utils_class()
            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)
Esempio n. 29
0
    def _handle_schedule_execution(self, liveaction_api, requester_user, context_string=None,
                                   show_secrets=False):
        """
        :param liveaction: LiveActionAPI object.
        :type liveaction: :class:`LiveActionAPI`
        """
        if not requester_user:
            requester_user = UserDB(cfg.CONF.system_user.user)

        # Assert action ref is valid
        action_ref = liveaction_api.action
        action_db = action_utils.get_action_by_ref(action_ref)

        if not action_db:
            message = 'Action "%s" cannot be found.' % (action_ref)
            LOG.warning(message)
            abort(http_client.BAD_REQUEST, message)

        # Assert the permissions
        permission_type = PermissionType.ACTION_EXECUTE
        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)

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

        try:
            return self._schedule_execution(liveaction=liveaction_api,
                                            requester_user=requester_user,
                                            user=user,
                                            context_string=context_string,
                                            show_secrets=show_secrets,
                                            action_db=action_db)
        except ValueError as e:
            LOG.exception('Unable to execute action.')
            abort(http_client.BAD_REQUEST, six.text_type(e))
        except jsonschema.ValidationError as e:
            LOG.exception('Unable to execute action. Parameter validation failed.')
            abort(http_client.BAD_REQUEST, re.sub("u'([^']*)'", r"'\1'",
                                                  getattr(e, 'message', six.text_type(e))))
        except trace_exc.TraceNotFoundException as e:
            abort(http_client.BAD_REQUEST, six.text_type(e))
        except validation_exc.ValueValidationException as e:
            raise e
        except Exception as e:
            LOG.exception('Unable to execute action. Unexpected error encountered.')
            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
Esempio n. 30
0
    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
Esempio n. 31
0
    def get(self, requester_user, auth_info):
        """
        Meta API endpoint wich returns information about the currently authenticated user.

            Handle:
                GET /v1/user
        """

        data = {}

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_service = get_rbac_backend().get_service_class()

        if cfg.CONF.rbac.enable and requester_user:
            role_dbs = rbac_service.get_roles_for_user(user_db=requester_user)
            roles = [role_db.name for role_db in role_dbs]
        else:
            roles = []

        data = {
            'username': requester_user.name,
            'authentication': {
                'method': auth_info['method'],
                'location': auth_info['location']
            },
            'rbac': {
                'enabled': cfg.CONF.rbac.enable,
                'roles': roles,
                'is_admin': rbac_utils.user_is_admin(user_db=requester_user)
            }
        }

        if auth_info.get('token_expire', None):
            token_expire = auth_info['token_expire'].strftime(
                '%Y-%m-%dT%H:%M:%SZ')
            data['authentication']['token_expire'] = token_expire

        return data
Esempio n. 32
0
    def get_all(self,
                requester_user,
                sort=None,
                offset=0,
                limit=None,
                **raw_filters):
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        return self._get_all(sort=sort,
                             offset=offset,
                             limit=limit,
                             raw_filters=raw_filters,
                             requester_user=requester_user)
Esempio n. 33
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)
Esempio n. 34
0
    def get_one(self, ref_or_id, requester_user):
        """
        Outputs the content of all the files inside the pack.

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

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=pack_db,
            permission_type=PermissionType.PACK_VIEW,
        )

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

        pack_ref = pack_db.ref
        pack_files = pack_db.files

        result = []
        for file_path in pack_files:
            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
                continue

            file_size = self._get_file_size(file_path=normalized_file_path)
            if file_size is not None and file_size > MAX_FILE_SIZE:
                LOG.debug(
                    'Skipping file "%s" which size exceeds max file size (%s bytes)'
                    % (normalized_file_path, MAX_FILE_SIZE)
                )
                continue

            content = self._get_file_content(file_path=normalized_file_path)

            include_file = self._include_file(file_path=file_path, content=content)
            if not include_file:
                LOG.debug('Skipping binary file "%s"' % (normalized_file_path))
                continue

            item = {"file_path": file_path, "content": content}
            result.append(item)
        return result
Esempio n. 35
0
    def get(self, requester_user, auth_info):
        """
        Meta API endpoint wich returns information about the currently authenticated user.

            Handle:
                GET /v1/user
        """

        data = {}

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_service = get_rbac_backend().get_service_class()

        if cfg.CONF.rbac.enable and requester_user:
            role_dbs = rbac_service.get_roles_for_user(user_db=requester_user)
            roles = [role_db.name for role_db in role_dbs]
        else:
            roles = []

        data = {
            'username': requester_user.name,
            'authentication': {
                'method': auth_info['method'],
                'location': auth_info['location']
            },
            'rbac': {
                'enabled': cfg.CONF.rbac.enable,
                'roles': roles,
                'is_admin': rbac_utils.user_is_admin(user_db=requester_user)
            }
        }

        if auth_info.get('token_expire', None):
            token_expire = auth_info['token_expire'].strftime('%Y-%m-%dT%H:%M:%SZ')
            data['authentication']['token_expire'] = token_expire

        return data
Esempio n. 36
0
    def get(self, requester_user, auth_info):
        """
        Meta API endpoint wich returns information about the currently authenticated user.

            Handle:
                GET /v1/user
        """

        data = {}

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_service = get_rbac_backend().get_service_class()

        if cfg.CONF.rbac.enable and requester_user:
            role_dbs = rbac_service.get_roles_for_user(user_db=requester_user)
            roles = [role_db.name for role_db in role_dbs]
        else:
            roles = []

        data = {
            "username": requester_user.name,
            "authentication": {
                "method": auth_info["method"],
                "location": auth_info["location"],
            },
            "rbac": {
                "enabled": cfg.CONF.rbac.enable,
                "roles": roles,
                "is_admin": rbac_utils.user_is_admin(user_db=requester_user),
            },
        }

        if auth_info.get("token_expire", None):
            token_expire = auth_info["token_expire"].strftime("%Y-%m-%dT%H:%M:%SZ")
            data["authentication"]["token_expire"] = token_expire

        return data
Esempio n. 37
0
    def put(self, action, ref_or_id, requester_user):
        action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        # Assert permissions
        permission_type = PermissionType.ACTION_MODIFY
        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,
        )

        action_id = action_db.id

        if not getattr(action, "pack", None):
            action.pack = action_db.pack

        # Perform validation
        validate_not_part_of_system_pack(action)
        action_validator.validate_action(action)

        # Write pack data files to disk (if any are provided)
        data_files = getattr(action, "data_files", [])
        written_data_files = []
        if data_files:
            written_data_files = self._handle_data_files(pack_ref=action.pack,
                                                         data_files=data_files)

        try:
            action_db = ActionAPI.to_model(action)
            LOG.debug("/actions/ PUT incoming action: %s", action_db)
            action_db.id = action_id
            action_db = Action.add_or_update(action_db)
            LOG.debug("/actions/ PUT after add_or_update: %s", action_db)
        except (ValidationError, ValueError) as e:
            LOG.exception("Unable to update action data=%s", action)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        # Dispatch an internal trigger for each written data file. This way user
        # automate committing this files to git using StackStorm rule
        if written_data_files:
            self._dispatch_trigger_for_written_data_files(
                action_db=action_db, written_data_files=written_data_files)

        action_api = ActionAPI.from_model(action_db)
        LOG.debug("PUT /actions/ client_result=%s", action_api)

        return action_api
Esempio n. 38
0
    def get_one(self, url, requester_user):
        triggers = self._hooks.get_triggers_for_hook(url)

        if not triggers:
            abort(http_client.NOT_FOUND)
            return

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

        # For demonstration purpose return 1st
        return triggers[0]
Esempio n. 39
0
    def user_has_permission(user_db, permission_type):
        """
        Check that the provided user has specified permission.
        """
        if not cfg.CONF.rbac.enable:
            return True

        # TODO Verify permission type for the provided resource type
        rbac_backend = get_rbac_backend()

        resolver = rbac_backend.get_resolver_for_permission_type(
            permission_type=permission_type)
        result = resolver.user_has_permission(user_db=user_db,
                                              permission_type=permission_type)
        return result
Esempio n. 40
0
    def post(self, action, requester_user):
        """
            Create a new action.

            Handles requests:
                POST /actions/
        """

        permission_type = PermissionType.ACTION_CREATE
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user,
                                                           resource_api=action,
                                                           permission_type=permission_type)

        try:
            # Perform validation
            validate_not_part_of_system_pack(action)
            action_validator.validate_action(action)
        except (ValidationError, ValueError,
                ValueValidationException, InvalidActionParameterException) as e:
            LOG.exception('Unable to create action data=%s', action)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        # Write pack data files to disk (if any are provided)
        data_files = getattr(action, 'data_files', [])
        written_data_files = []
        if data_files:
            written_data_files = self._handle_data_files(pack_ref=action.pack,
                                                         data_files=data_files)

        action_model = ActionAPI.to_model(action)

        LOG.debug('/actions/ POST verified ActionAPI object=%s', action)
        action_db = Action.add_or_update(action_model)
        LOG.debug('/actions/ POST saved ActionDB object=%s', action_db)

        # Dispatch an internal trigger for each written data file. This way user
        # automate comitting this files to git using StackStorm rule
        if written_data_files:
            self._dispatch_trigger_for_written_data_files(action_db=action_db,
                                                          written_data_files=written_data_files)

        extra = {'acion_db': action_db}
        LOG.audit('Action created. Action.id=%s' % (action_db.id), extra=extra)
        action_api = ActionAPI.from_model(action_db)

        return Response(json=action_api, status=http_client.CREATED)
Esempio n. 41
0
    def _get_one_by_ref_or_id(self, ref_or_id, requester_user, exclude_fields=None):
        instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields)

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=instance,
                                                          permission_type=PermissionType.PACK_VIEW)

        if not instance:
            msg = 'Unable to identify resource with ref_or_id "%s".' % (ref_or_id)
            abort(http_client.NOT_FOUND, msg)
            return

        result = self.model.from_model(instance, **self.from_model_kwargs)

        return result
Esempio n. 42
0
    def _get_mask_secrets(self, requester_user, show_secrets=None):
        """
        Return a value for mask_secrets which can be used in masking secret properties
        to be retruned by any API. The default value is as per the config however admin
        users have the ability to override by passing in a special query parameter
        ?show_secrets=True.

        :rtype: ``bool``
        """
        mask_secrets = cfg.CONF.api.mask_secrets

        rbac_utils = get_rbac_backend().get_utils_class()
        if show_secrets and rbac_utils.user_is_admin(user_db=requester_user):
            mask_secrets = False

        return mask_secrets
Esempio n. 43
0
    def get_one(self, url, requester_user):
        triggers = self._hooks.get_triggers_for_hook(url)

        if not triggers:
            abort(http_client.NOT_FOUND)
            return

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

        # For demonstration purpose return 1st
        return triggers[0]
Esempio n. 44
0
File: base.py Progetto: rahmiy/st2
    def _get_mask_secrets(self, requester_user, show_secrets=None):
        """
        Return a value for mask_secrets which can be used in masking secret properties
        to be retruned by any API. The default value is as per the config however admin
        users have the ability to override by passing in a special query parameter
        ?show_secrets=True.

        :rtype: ``bool``
        """
        mask_secrets = cfg.CONF.api.mask_secrets

        rbac_utils = get_rbac_backend().get_utils_class()
        if show_secrets and rbac_utils.user_is_admin(user_db=requester_user):
            mask_secrets = False

        return mask_secrets
Esempio n. 45
0
    def get_all(self,
                requester_user,
                sort=None,
                offset=0,
                limit=None,
                **raw_filters):
        user = raw_filters.get('user', None)
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin_or_operating_on_own_resource(
            user_db=requester_user, user=user)

        return self._get_all(sort=sort,
                             offset=offset,
                             limit=limit,
                             raw_filters=raw_filters,
                             requester_user=requester_user)
Esempio n. 46
0
    def put(self, api_key_api, api_key_id_or_key, requester_user):
        api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key)

        permission_type = PermissionType.API_KEY_MODIFY
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=api_key_db,
            permission_type=permission_type,
        )

        old_api_key_db = api_key_db
        api_key_db = ApiKeyAPI.to_model(api_key_api)

        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)

        # Passing in key_hash as MASKED_ATTRIBUTE_VALUE is expected since we do not
        # leak it out therefore it is expected we get the same value back. Interpret
        # this special code and empty value as no-change
        if api_key_db.key_hash == MASKED_ATTRIBUTE_VALUE or not api_key_db.key_hash:
            api_key_db.key_hash = old_api_key_db.key_hash

        # Rather than silently ignore any update to key_hash it is better to explicitly
        # disallow and notify user.
        if old_api_key_db.key_hash != api_key_db.key_hash:
            raise ValueError("Update of key_hash is not allowed.")

        api_key_db.id = old_api_key_db.id
        api_key_db = ApiKey.add_or_update(api_key_db)

        extra = {
            "old_api_key_db": old_api_key_db,
            "new_api_key_db": api_key_db
        }
        LOG.audit("API Key updated. ApiKey.id=%s." % (api_key_db.id),
                  extra=extra)
        api_key_api = ApiKeyAPI.from_model(api_key_db)

        return api_key_api
Esempio n. 47
0
    def put(self, action_alias, ref_or_id, requester_user):
        """
            Update an action alias.

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

        permission_type = PermissionType.ACTION_ALIAS_MODIFY
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=action_alias_db,
            permission_type=permission_type)

        if not hasattr(action_alias, 'id'):
            action_alias.id = None

        try:
            if action_alias.id is not None and action_alias.id != '' and \
               action_alias.id != ref_or_id:
                LOG.warning(
                    'Discarding mismatched id=%s found in payload and using uri_id=%s.',
                    action_alias.id, ref_or_id)
            old_action_alias_db = action_alias_db
            action_alias_db = ActionAliasAPI.to_model(action_alias)
            action_alias_db.id = ref_or_id
            action_alias_db = ActionAlias.add_or_update(action_alias_db)
        except (ValidationError, ValueError) as e:
            LOG.exception('Validation failed for action alias data=%s',
                          action_alias)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

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

        return action_alias_api
Esempio n. 48
0
    def put(self, action, ref_or_id, requester_user):
        action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        # Assert permissions
        permission_type = PermissionType.ACTION_MODIFY
        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)

        action_id = action_db.id

        if not getattr(action, 'pack', None):
            action.pack = action_db.pack

        # Perform validation
        validate_not_part_of_system_pack(action)
        action_validator.validate_action(action)

        # Write pack data files to disk (if any are provided)
        data_files = getattr(action, 'data_files', [])
        written_data_files = []
        if data_files:
            written_data_files = self._handle_data_files(pack_ref=action.pack,
                                                         data_files=data_files)

        try:
            action_db = ActionAPI.to_model(action)
            LOG.debug('/actions/ PUT incoming action: %s', action_db)
            action_db.id = action_id
            action_db = Action.add_or_update(action_db)
            LOG.debug('/actions/ PUT after add_or_update: %s', action_db)
        except (ValidationError, ValueError) as e:
            LOG.exception('Unable to update action data=%s', action)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        # Dispatch an internal trigger for each written data file. This way user
        # automate committing this files to git using StackStorm rule
        if written_data_files:
            self._dispatch_trigger_for_written_data_files(action_db=action_db,
                                                          written_data_files=written_data_files)

        action_api = ActionAPI.from_model(action_db)
        LOG.debug('PUT /actions/ client_result=%s', action_api)

        return action_api
Esempio n. 49
0
    def _get_one_by_id(self,
                       id,
                       requester_user,
                       permission_type,
                       exclude_fields=None,
                       include_fields=None,
                       from_model_kwargs=None):
        """
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        :param include_fields: A list of object fields to include.
        :type include_fields: ``list``
        """

        instance = self._get_by_id(resource_id=id,
                                   exclude_fields=exclude_fields,
                                   include_fields=include_fields)

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

        if not instance:
            msg = 'Unable to identify resource with id "%s".' % id
            abort(http_client.NOT_FOUND, msg)

        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 result
Esempio n. 50
0
    def delete(self, ref_or_id, requester_user):
        """
        Delete an action.

        Handles requests:
            POST /actions/1?_method=delete
            DELETE /actions/1
            DELETE /actions/mypack.myaction
        """
        action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)
        action_id = action_db.id

        permission_type = PermissionType.ACTION_DELETE
        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,
        )

        try:
            validate_not_part_of_system_pack(action_db)
        except ValueValidationException as e:
            abort(http_client.BAD_REQUEST, six.text_type(e))

        LOG.debug(
            "DELETE /actions/ lookup with ref_or_id=%s found object: %s",
            ref_or_id,
            action_db,
        )

        try:
            Action.delete(action_db)
        except Exception as e:
            LOG.error(
                'Database delete encountered exception during delete of id="%s". '
                "Exception was %s",
                action_id,
                e,
            )
            abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))
            return

        extra = {"action_db": action_db}
        LOG.audit("Action deleted. Action.id=%s" % (action_db.id), extra=extra)
        return Response(status=http_client.NO_CONTENT)
Esempio n. 51
0
File: rbac.py Progetto: zwunix/st2
    def get_one(self, resource_type, requester_user):
        """
            List all the available permission types for a particular resource type.

            Handles requests:
                GET /rbac/permission_types/<resource type>
        """
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_is_admin(user_db=requester_user)

        all_permission_types = get_resource_permission_types_with_descriptions()
        permission_types = all_permission_types.get(resource_type, None)

        if permission_types is None:
            raise exc.HTTPNotFound('Invalid resource type: %s' % (resource_type))

        return permission_types
Esempio n. 52
0
    def _get_one_by_id(
        self,
        id,
        requester_user,
        permission_type,
        exclude_fields=None,
        from_model_kwargs=None,
    ):
        """Override ResourceController._get_one_by_id to contain scope of Inquiries UID hack
        :param exclude_fields: A list of object fields to exclude.
        :type exclude_fields: ``list``
        """
        LOG.debug('Retrieving action execution for inquiry "%s".' % id)

        execution_db = self._get_by_id(resource_id=id,
                                       exclude_fields=exclude_fields)

        if not execution_db:
            raise db_exceptions.StackStormDBObjectNotFoundError()

        # Inquiry currently does not have it's own database model and share with ActionExecution.
        # The object uid is in the format of "execution:<id>". To allow RBAC to resolve correctly
        # for inquiries, we're overriding the "get_uid" function so the object uid can be set to
        # "inquiry:<id>".
        #
        # TODO (mierdin): All of this should be removed once Inquiries get their own DB model.
        if (execution_db and getattr(execution_db, "runner", None) and
                execution_db.runner.get("runner_module") == INQUIRY_RUNNER):
            execution_db.get_uid = get_uid

        LOG.debug('Checking permission on inquiry "%s".' % id)

        if permission_type:
            rbac_utils = get_rbac_backend().get_utils_class()
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=execution_db,
                permission_type=permission_type,
            )

        from_model_kwargs = from_model_kwargs or {}
        from_model_kwargs.update(self.from_model_kwargs)
        result = self.model.from_model(execution_db, **from_model_kwargs)

        return result
Esempio n. 53
0
    def user_has_rule_trigger_permission(user_db, trigger):
        """
        Check that the currently logged-in has necessary permissions on the trigger used /
        referenced inside the rule.
        """
        if not cfg.CONF.rbac.enable:
            return True

        rbac_backend = get_rbac_backend()
        rules_resolver = rbac_backend.get_resolver_for_resource_type(
            ResourceType.RULE)
        has_trigger_permission = rules_resolver.user_has_trigger_permission(
            user_db=user_db, trigger=trigger)

        if has_trigger_permission:
            return True

        return False
Esempio n. 54
0
    def get_one(self, ref_or_id, requester_user):
        try:
            trigger_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)
        except Exception as e:
            LOG.exception(six.text_type(e))
            abort(http_client.NOT_FOUND, six.text_type(e))
            return

        permission_type = PermissionType.TIMER_VIEW
        resource_db = TimerDB(pack=trigger_db.pack, name=trigger_db.name)

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

        result = self.model.from_model(trigger_db)
        return result
Esempio n. 55
0
    def put(self, sensor_type, ref_or_id, requester_user):
        # Note: Right now this function only supports updating of "enabled"
        # attribute on the SensorType model.
        # The reason for that is that SensorTypeAPI.to_model right now only
        # knows how to work with sensor type definitions from YAML files.

        sensor_type_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        permission_type = PermissionType.SENSOR_MODIFY
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=sensor_type_db,
                                                          permission_type=permission_type)

        sensor_type_id = sensor_type_db.id

        try:
            validate_not_part_of_system_pack(sensor_type_db)
        except ValueValidationException as e:
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        if not getattr(sensor_type, 'pack', None):
            sensor_type.pack = sensor_type_db.pack
        try:
            old_sensor_type_db = sensor_type_db
            sensor_type_db.id = sensor_type_id
            sensor_type_db.enabled = getattr(sensor_type, 'enabled', False)
            sensor_type_db = SensorType.add_or_update(sensor_type_db)
        except (ValidationError, ValueError) as e:
            LOG.exception('Unable to update sensor_type data=%s', sensor_type)
            abort(http_client.BAD_REQUEST, six.text_type(e))
            return

        extra = {
            'old_sensor_type_db': old_sensor_type_db,
            'new_sensor_type_db': sensor_type_db
        }
        LOG.audit('Sensor updated. Sensor.id=%s.' % (sensor_type_db.id), extra=extra)
        sensor_type_api = SensorTypeAPI.from_model(sensor_type_db)

        return sensor_type_api
Esempio n. 56
0
    def _get_one(action_id, requester_user):
        """
            List merged action & runner parameters by action id.

            Handle:
                GET /actions/views/parameters/1
        """
        action_db = LookupUtils._get_action_by_id(action_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)

        runner_db = LookupUtils._get_runner_by_name(action_db.runner_type['name'])
        all_params = action_param_utils.get_params_view(
            action_db=action_db, runner_db=runner_db, merged_only=True)

        return {'parameters': all_params}
Esempio n. 57
0
    def delete(self, api_key_id_or_key, requester_user):
        """
            Delete the key value pair.

            Handles requests:
                DELETE /apikeys/1
        """
        api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key)

        permission_type = PermissionType.API_KEY_DELETE
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=api_key_db,
                                                          permission_type=permission_type)

        ApiKey.delete(api_key_db)

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

        return Response(status=http_client.NO_CONTENT)
Esempio n. 58
0
    def put(self, api_key_api, api_key_id_or_key, requester_user):
        api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key)

        permission_type = PermissionType.API_KEY_MODIFY
        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=api_key_db,
                                                          permission_type=permission_type)

        old_api_key_db = api_key_db
        api_key_db = ApiKeyAPI.to_model(api_key_api)

        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)

        # Passing in key_hash as MASKED_ATTRIBUTE_VALUE is expected since we do not
        # leak it out therefore it is expected we get the same value back. Interpret
        # this special code and empty value as no-change
        if api_key_db.key_hash == MASKED_ATTRIBUTE_VALUE or not api_key_db.key_hash:
            api_key_db.key_hash = old_api_key_db.key_hash

        # Rather than silently ignore any update to key_hash it is better to explicitly
        # disallow and notify user.
        if old_api_key_db.key_hash != api_key_db.key_hash:
            raise ValueError('Update of key_hash is not allowed.')

        api_key_db.id = old_api_key_db.id
        api_key_db = ApiKey.add_or_update(api_key_db)

        extra = {'old_api_key_db': old_api_key_db, 'new_api_key_db': api_key_db}
        LOG.audit('API Key updated. ApiKey.id=%s.' % (api_key_db.id), extra=extra)
        api_key_api = ApiKeyAPI.from_model(api_key_db)

        return api_key_api
Esempio n. 59
0
    def get_one(self, pack_ref, requester_user, show_secrets=False):
        """
        Retrieve config for a particular pack.

        Handles requests:
            GET /configs/<pack_ref>
        """
        from_model_kwargs = {
            'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets)
        }
        try:
            instance = packs_service.get_pack_by_ref(pack_ref=pack_ref)
        except StackStormDBObjectNotFoundError:
            msg = 'Unable to identify resource with pack_ref "%s".' % (pack_ref)
            abort(http_client.NOT_FOUND, msg)

        rbac_utils = get_rbac_backend().get_utils_class()
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                          resource_db=instance,
                                                          permission_type=PermissionType.PACK_VIEW)

        return self._get_one_by_pack_ref(pack_ref=pack_ref, from_model_kwargs=from_model_kwargs)