Exemple #1
0
    def get_all(self,
                prefix=None,
                scope=FULL_SYSTEM_SCOPE,
                user=None,
                decrypt=False,
                **kwargs):
        """
            List all keys.

            Handles requests:
                GET /keys/
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

        scope = get_datastore_full_scope(scope)
        requester_user = get_requester()
        user = user or requester_user
        is_all_scope = (scope == ALL_SCOPE)
        is_admin = request_user_is_admin(request=pecan.request)

        if is_all_scope and not is_admin:
            msg = '"all" scope requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt,
                                               scope=scope,
                                               is_admin=is_admin)

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provided(
            request=pecan.request, user=user)

        from_model_kwargs = {'mask_secrets': not decrypt}
        kwargs['prefix'] = prefix

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            kwargs['scope'] = scope

        if scope == USER_SCOPE or scope == FULL_USER_SCOPE:
            # Make sure we only returned values scoped to current user
            if kwargs['prefix']:
                kwargs['prefix'] = get_key_reference(name=kwargs['prefix'],
                                                     scope=scope,
                                                     user=requester_user)
            else:
                kwargs['prefix'] = get_key_reference(name='',
                                                     scope=scope,
                                                     user=user)

        kvp_apis = super(KeyValuePairController,
                         self)._get_all(from_model_kwargs=from_model_kwargs,
                                        **kwargs)
        return kvp_apis
Exemple #2
0
    def put(self, kvp, name, scope=FULL_SYSTEM_SCOPE):
        """
        Create a new entry or update an existing one.
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE
        requester_user = get_requester()

        scope = getattr(kvp, 'scope', scope)
        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)

        user = getattr(kvp, 'user', requester_user) or requester_user

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provider(request=pecan.request,
                                                                     user=user)

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope)
        LOG.debug('PUT scope: %s, name: %s', scope, name)
        # TODO: Custom permission check since the key doesn't need to exist here

        # Note: We use lock to avoid a race
        with self._coordinator.get_lock(lock_name):
            try:
                existing_kvp_api = self._get_one_by_scope_and_name(
                    scope=scope,
                    name=key_ref
                )
            except StackStormDBObjectNotFoundError:
                existing_kvp_api = None

            kvp.name = key_ref
            kvp.scope = scope

            try:
                kvp_db = KeyValuePairAPI.to_model(kvp)

                if existing_kvp_api:
                    kvp_db.id = existing_kvp_api.id

                kvp_db = KeyValuePair.add_or_update(kvp_db)
            except (ValidationError, ValueError) as e:
                LOG.exception('Validation failed for key value data=%s', kvp)
                abort(http_client.BAD_REQUEST, str(e))
                return
            except CryptoKeyNotSetupException as e:
                LOG.exception(str(e))
                abort(http_client.BAD_REQUEST, str(e))
                return
            except InvalidScopeException as e:
                LOG.exception(str(e))
                abort(http_client.BAD_REQUEST, str(e))
                return
        extra = {'kvp_db': kvp_db}
        LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)

        kvp_api = KeyValuePairAPI.from_model(kvp_db)
        return kvp_api
Exemple #3
0
    def get_one(self, name, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False):
        """
            List key by name.

            Handle:
                GET /keys/key1
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)
        requester_user = get_requester()
        user = user or requester_user
        is_admin = request_user_is_admin(request=pecan.request)

        # User needs to be either admin or requesting item for itself
        self._validate_decrypt_query_parameter(decrypt=decrypt,
                                               scope=scope,
                                               is_admin=is_admin)

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provided(
            request=pecan.request, user=user)

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        from_model_kwargs = {'mask_secrets': not decrypt}
        kvp_api = self._get_one_by_scope_and_name(
            name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs)

        return kvp_api
Exemple #4
0
    def put(self, kvp, name, scope=FULL_SYSTEM_SCOPE):
        """
        Create a new entry or update an existing one.
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE
        requester_user = get_requester()

        scope = getattr(kvp, 'scope', scope)
        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)

        user = getattr(kvp, 'user', requester_user) or requester_user

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provided(
            request=pecan.request, user=user)

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope)
        LOG.debug('PUT scope: %s, name: %s', scope, name)
        # TODO: Custom permission check since the key doesn't need to exist here

        # Note: We use lock to avoid a race
        with self._coordinator.get_lock(lock_name):
            try:
                existing_kvp_api = self._get_one_by_scope_and_name(
                    scope=scope, name=key_ref)
            except StackStormDBObjectNotFoundError:
                existing_kvp_api = None

            kvp.name = key_ref
            kvp.scope = scope

            try:
                kvp_db = KeyValuePairAPI.to_model(kvp)

                if existing_kvp_api:
                    kvp_db.id = existing_kvp_api.id

                kvp_db = KeyValuePair.add_or_update(kvp_db)
            except (ValidationError, ValueError) as e:
                LOG.exception('Validation failed for key value data=%s', kvp)
                abort(http_client.BAD_REQUEST, str(e))
                return
            except CryptoKeyNotSetupException as e:
                LOG.exception(str(e))
                abort(http_client.BAD_REQUEST, str(e))
                return
            except InvalidScopeException as e:
                LOG.exception(str(e))
                abort(http_client.BAD_REQUEST, str(e))
                return
        extra = {'kvp_db': kvp_db}
        LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id),
                  extra=extra)

        kvp_api = KeyValuePairAPI.from_model(kvp_db)
        return kvp_api
Exemple #5
0
    def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None,
                decrypt=False, sort=None, offset=0, limit=None, **raw_filters):
        """
            List all keys.

            Handles requests:
                GET /keys/
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

        scope = get_datastore_full_scope(scope)
        is_all_scope = (scope == ALL_SCOPE)

        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)

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin,
                                               requester_user=requester_user)

        user = user or requester_user.name

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

        from_model_kwargs = {'mask_secrets': not decrypt}

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            raw_filters['scope'] = scope

        if scope == USER_SCOPE or scope == FULL_USER_SCOPE:
            # Make sure we only returned values scoped to current user
            if prefix:
                prefix = get_key_reference(name=prefix, scope=scope, user=user)
            else:
                prefix = get_key_reference(name='', scope=scope, user=user)

        raw_filters['prefix'] = prefix

        kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs,
                                                                sort=sort,
                                                                offset=offset,
                                                                limit=limit,
                                                                raw_filters=raw_filters,
                                                                requester_user=requester_user)
        return kvp_apis
Exemple #6
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)
Exemple #7
0
    def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None,
                decrypt=False, sort=None, offset=0, limit=None, **raw_filters):
        """
            List all keys.

            Handles requests:
                GET /keys/
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

        scope = get_datastore_full_scope(scope)
        is_all_scope = (scope == ALL_SCOPE)

        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)

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin,
                                               requester_user=requester_user)

        user = user or requester_user.name

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

        from_model_kwargs = {'mask_secrets': not decrypt}

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            raw_filters['scope'] = scope

        if scope == USER_SCOPE or scope == FULL_USER_SCOPE:
            # Make sure we only returned values scoped to current user
            if prefix:
                prefix = get_key_reference(name=prefix, scope=scope, user=user)
            else:
                prefix = get_key_reference(name='', scope=scope, user=user)

        raw_filters['prefix'] = prefix

        kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs,
                                                                sort=sort,
                                                                offset=offset,
                                                                limit=limit,
                                                                raw_filters=raw_filters)
        return kvp_apis
Exemple #8
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)
        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)
Exemple #9
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)
Exemple #10
0
    def get_all(self, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, **kwargs):
        """
            List all keys.

            Handles requests:
                GET /keys/
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

        scope = get_datastore_full_scope(scope)
        requester_user = get_requester()
        user = user or requester_user
        is_all_scope = (scope == ALL_SCOPE)
        is_admin = request_user_is_admin(request=pecan.request)

        if is_all_scope and not is_admin:
            msg = '"all" scope requires administrator access'
            raise AccessDeniedError(message=msg, user_db=requester_user)

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin)

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provider(request=pecan.request,
                                                                     user=user)

        from_model_kwargs = {'mask_secrets': not decrypt}
        kwargs['prefix'] = prefix

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            kwargs['scope'] = scope

        if scope == USER_SCOPE or scope == FULL_USER_SCOPE:
            # Make sure we only returned values scoped to current user
            if kwargs['prefix']:
                kwargs['prefix'] = get_key_reference(name=kwargs['prefix'], scope=scope,
                                                     user=requester_user)
            else:
                kwargs['prefix'] = get_key_reference(name='', scope=scope,
                                                     user=user)

        kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs,
                                                                **kwargs)
        return kvp_apis
Exemple #11
0
    def delete(self, name, scope=FULL_SYSTEM_SCOPE, user=None):
        """
            Delete the key value pair.

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

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

        requester_user = get_requester()
        user = user or requester_user

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provided(
            request=pecan.request, user=user)

        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, str(e))
                return

        extra = {'kvp_db': kvp_db}
        LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id),
                  extra=extra)
Exemple #12
0
    def delete(self, name, scope=FULL_SYSTEM_SCOPE, user=None):
        """
            Delete the key value pair.

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

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

        requester_user = get_requester()
        user = user or requester_user

        # Validate that the authenticated user is admin if user query param is provided
        assert_request_user_is_admin_if_user_query_param_is_provider(request=pecan.request,
                                                                     user=user)

        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, str(e))
                return

        extra = {'kvp_db': kvp_db}
        LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
Exemple #13
0
    def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False):
        """
            List key by name.

            Handle:
                GET /keys/key1
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

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

        is_admin = rbac_utils.user_is_admin(user_db=requester_user)

        # User needs to be either admin or requesting item for itself
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin,
                                               requester_user=requester_user)

        user = user or requester_user.name

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

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        from_model_kwargs = {'mask_secrets': not decrypt}
        kvp_api = self._get_one_by_scope_and_name(
            name=key_ref,
            scope=scope,
            from_model_kwargs=from_model_kwargs
        )

        return kvp_api
Exemple #14
0
    def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None,
                decrypt=False, sort=None, offset=0, limit=None, **raw_filters):
        """
            List all keys.

            Handles requests:
                GET /keys/
        """
        if not scope:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

        scope = get_datastore_full_scope(scope)

        # "all" scope can only be used by the admins (on RBAC installations)
        self._validate_all_scope(scope=scope, requester_user=requester_user)

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope,
                                               requester_user=requester_user)

        user_query_param_filter = bool(user)

        current_user = requester_user.name
        user = user or requester_user.name

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

        from_model_kwargs = {'mask_secrets': not decrypt}

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            raw_filters['scope'] = scope

        # Set prefix which will be used for user-scoped items.
        # NOTE: It's very important raw_filters['prefix'] is set when requesting user scoped items
        # to avoid information leakage (aka user1 retrieves items for user2)
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)

        if is_admin and user_query_param_filter:
            # Retrieve values scoped to the provided user
            user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE, user=user)
        else:
            # RBAC not enabled or user is not an admin, retrieve user scoped values for the
            # current user
            user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE,
                                                  user=current_user)

        if scope == ALL_SCOPE:
            # Special case for ALL_SCOPE
            # 1. Retrieve system scoped values
            raw_filters['scope'] = FULL_SYSTEM_SCOPE
            raw_filters['prefix'] = prefix

            assert 'scope' in raw_filters
            kvp_apis_system = super(KeyValuePairController, self)._get_all(
                from_model_kwargs=from_model_kwargs,
                sort=sort,
                offset=offset,
                limit=limit,
                raw_filters=raw_filters,
                requester_user=requester_user)

            # 2. Retrieve user scoped items for current user or for all the users (depending if the
            # authenticated user is admin and if ?user is provided)
            raw_filters['scope'] = FULL_USER_SCOPE

            if cfg.CONF.rbac.enable and is_admin and not user_query_param_filter:
                # Admin user retrieving user-scoped items for all the users
                raw_filters['prefix'] = prefix or ''
            else:
                raw_filters['prefix'] = user_scope_prefix

            assert 'scope' in raw_filters
            assert 'prefix' in raw_filters
            kvp_apis_user = super(KeyValuePairController, self)._get_all(
                from_model_kwargs=from_model_kwargs,
                sort=sort,
                offset=offset,
                limit=limit,
                raw_filters=raw_filters,
                requester_user=requester_user)

            # Combine the result
            kvp_apis = []
            kvp_apis.extend(kvp_apis_system.json or [])
            kvp_apis.extend(kvp_apis_user.json or [])
        elif scope in [USER_SCOPE, FULL_USER_SCOPE]:
            # Make sure we only returned values scoped to current user
            prefix = get_key_reference(name=prefix or '', scope=scope, user=user)
            raw_filters['prefix'] = user_scope_prefix

            assert 'scope' in raw_filters
            assert 'prefix' in raw_filters
            kvp_apis = super(KeyValuePairController, self)._get_all(
                from_model_kwargs=from_model_kwargs,
                sort=sort,
                offset=offset,
                limit=limit,
                raw_filters=raw_filters,
                requester_user=requester_user)
        elif scope in [SYSTEM_SCOPE, FULL_SYSTEM_SCOPE]:
            raw_filters['prefix'] = prefix

            assert 'scope' in raw_filters
            kvp_apis = super(KeyValuePairController, self)._get_all(
                from_model_kwargs=from_model_kwargs,
                sort=sort,
                offset=offset,
                limit=limit,
                raw_filters=raw_filters,
                requester_user=requester_user)
        else:
            raise ValueError('Invalid scope: %s' % (scope))

        return kvp_apis
Exemple #15
0
    def get_all(
        self,
        requester_user,
        prefix=None,
        scope=None,
        user=None,
        decrypt=False,
        sort=None,
        offset=0,
        limit=None,
        **raw_filters,
    ):
        """
        List all keys.

        Handles requests:
            GET /keys/
        """
        if not scope:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

        scope = get_datastore_full_scope(scope)

        if scope not in [ALL_SCOPE] + ALLOWED_SCOPES:
            raise ValueError("Invalid scope: %s" % (scope))

        # User needs to be either admin or requesting items for themselves
        self._validate_decrypt_query_parameter(decrypt=decrypt,
                                               scope=scope,
                                               requester_user=requester_user)

        current_user = requester_user.name
        user = user or requester_user.name

        rbac_utils = get_rbac_backend().get_utils_class()

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

        from_model_kwargs = {"mask_secrets": not decrypt}

        if scope and scope not in ALL_SCOPE:
            self._validate_scope(scope=scope)
            raw_filters["scope"] = scope

        # Check if user is granted one of the system roles.
        has_system_role = rbac_utils.user_has_system_role(
            user_db=requester_user)

        # Check that an admin user has permission to all system scoped items.
        if has_system_role and scope in [
                ALL_SCOPE, SYSTEM_SCOPE, FULL_SYSTEM_SCOPE
        ]:
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=KeyValuePairDB(scope=FULL_SYSTEM_SCOPE),
                permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
            )

        # Check that user has permission to user scoped items for provided user or current user.
        if user and scope in [ALL_SCOPE, USER_SCOPE, FULL_USER_SCOPE]:
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=KeyValuePairDB(scope="%s:%s" %
                                           (FULL_USER_SCOPE, user)),
                permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
            )

        # Set user scope prefix for the provided user (or current user if user not provided)
        # NOTE: It's very important raw_filters['prefix'] is set when requesting user scoped
        # items to avoid information leakage (aka user1 retrieves items for user2)
        user_scope_prefix = get_key_reference(name=prefix or "",
                                              scope=FULL_USER_SCOPE,
                                              user=user)

        # Special cases for ALL_SCOPE
        # 1. If user is an admin, then retrieves all system scoped items else only
        #    specific system scoped items that the user is granted permission to.
        # 2. Retrieves all the user scoped items that the current user owns.
        kvp_apis_system = []
        kvp_apis_user = []

        if scope in [ALL_SCOPE, SYSTEM_SCOPE, FULL_SYSTEM_SCOPE]:
            decrypted_keys = []
            # If user has system role, then retrieve all system scoped items
            if has_system_role:
                raw_filters["scope"] = FULL_SYSTEM_SCOPE
                raw_filters["prefix"] = prefix

                items = self._get_all(
                    from_model_kwargs=from_model_kwargs,
                    sort=sort,
                    offset=offset,
                    limit=limit,
                    raw_filters=raw_filters,
                    requester_user=requester_user,
                )

                kvp_apis_system.extend(items.json or [])
                if decrypt and items.json:
                    decrypted_keys.extend(kv_api["name"]
                                          for kv_api in items.json
                                          if kv_api["secret"])
            else:
                # Otherwise if user is not an admin, then get the list of
                # system scoped items that user is granted permission to.
                for key in get_all_system_kvp_names_for_user(current_user):
                    try:
                        item = self._get_one_by_scope_and_name(
                            from_model_kwargs=from_model_kwargs,
                            scope=FULL_SYSTEM_SCOPE,
                            name=key,
                        )

                        kvp_apis_system.append(item)
                    except Exception as e:
                        LOG.error("Unable to get key %s: %s", key, str(e))
                        continue
                    if decrypt and item.secret:
                        decrypted_keys.append(key)
            if decrypted_keys:
                LOG.audit(
                    "User %s decrypted the values %s ",
                    user,
                    decrypted_keys,
                    extra={
                        "User": user,
                        "scope": FULL_SYSTEM_SCOPE,
                        "key_name": decrypted_keys,
                        "operation": "decrypt",
                    },
                )

        if scope in [ALL_SCOPE, USER_SCOPE, FULL_USER_SCOPE]:
            # Retrieves all the user scoped items that the current user owns.
            raw_filters["scope"] = FULL_USER_SCOPE
            raw_filters["prefix"] = user_scope_prefix

            items = self._get_all(
                from_model_kwargs=from_model_kwargs,
                sort=sort,
                offset=offset,
                limit=limit,
                raw_filters=raw_filters,
                requester_user=requester_user,
            )

            kvp_apis_user.extend(items.json)
            if decrypt and items.json:
                decrypted_keys = [
                    kvp_api["name"] for kvp_api in items.json
                    if kvp_api["secret"]
                ]
                if decrypted_keys:
                    LOG.audit(
                        "User %s decrypted the values %s ",
                        user,
                        decrypted_keys,
                        extra={
                            "User": user,
                            "scope": FULL_USER_SCOPE,
                            "key_name": decrypted_keys,
                            "operation": "decrypt",
                        },
                    )

        return kvp_apis_system + kvp_apis_user
Exemple #16
0
    def put(self, kvp, name, requester_user, scope=None):
        """
        Create a new entry or update an existing one.
        """
        if not scope or scope == ALL_SCOPE:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

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

        scope = getattr(kvp, "scope", scope)
        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)

        user = getattr(kvp, "user", requester_user.name) or requester_user.name

        rbac_utils = get_rbac_backend().get_utils_class()

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

        # Set key reference for system or user scope
        key_ref = get_key_reference(scope=scope, name=name, user=user)
        extra = {
            "scope": scope,
            "name": name,
            "user": user,
            "key_ref": key_ref
        }
        LOG.debug("PUT /v1/keys/%s", name, extra=extra)

        # Setup a kvp database object used for verifying permission
        kvp_db = KeyValuePairDB(
            uid="%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR, scope, key_ref),
            scope=scope,
            name=key_ref,
        )

        # Check that user has permission to the key value pair.
        # If RBAC is enabled, this check will verify if user has system role with all access.
        # If RBAC is enabled, this check guards against a user accessing another user's kvp.
        # If RBAC is enabled, user needs to be explicitly granted permission to set a system kvp.
        # The check is sufficient to allow decryption of the system kvp.
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=kvp_db,
            permission_type=PermissionType.KEY_VALUE_PAIR_SET,
        )

        # Validate that the pre-encrypted option can only be used by admins
        encrypted = getattr(kvp, "encrypted", False)
        self._validate_encrypted_query_parameter(encrypted=encrypted,
                                                 scope=scope,
                                                 requester_user=requester_user)

        # Acquire a lock to avoid race condition between concurrent API calls
        with self._coordinator.get_lock(
                self._get_lock_name_for_key(name=key_ref, scope=scope)):
            try:
                existing_kvp_api = self._get_one_by_scope_and_name(
                    scope=scope, name=key_ref)
            except StackStormDBObjectNotFoundError:
                existing_kvp_api = None

            # st2client sends invalid id when initially setting a key so we ignore those
            id_ = kvp.__dict__.get("id", None)
            if not existing_kvp_api and id_ and not bson.ObjectId.is_valid(
                    id_):
                del kvp.__dict__["id"]

            kvp.name = key_ref
            kvp.scope = scope

            try:
                kvp_db = KeyValuePairAPI.to_model(kvp)

                if existing_kvp_api:
                    kvp_db.id = existing_kvp_api.id

                kvp_db = KeyValuePair.add_or_update(kvp_db)
            except (ValidationError, ValueError) as e:
                LOG.exception("Validation failed for key value data=%s", kvp)
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return
            except CryptoKeyNotSetupException as e:
                LOG.exception(six.text_type(e))
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return
            except InvalidScopeException as e:
                LOG.exception(six.text_type(e))
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return

        extra["kvp_db"] = kvp_db
        LOG.audit("PUT /v1/keys/%s succeeded id=%s",
                  name,
                  kvp_db.id,
                  extra=extra)

        return KeyValuePairAPI.from_model(kvp_db)
Exemple #17
0
 def test_get_datastore_full_scope_datastore_parent_scope(self):
     self.assertEqual(
         kv_utl.get_datastore_full_scope(DATASTORE_PARENT_SCOPE),
         DATASTORE_PARENT_SCOPE
     )
Exemple #18
0
    def delete(self, name, requester_user, scope=None, user=None):
        """
        Delete the key value pair.

        Handles requests:
            DELETE /keys/1
        """
        if not scope or scope == ALL_SCOPE:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

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

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

        user = user or requester_user.name

        rbac_utils = get_rbac_backend().get_utils_class()

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

        # Set key reference for system or user scope
        key_ref = get_key_reference(scope=scope, name=name, user=user)
        extra = {
            "scope": scope,
            "name": name,
            "user": user,
            "key_ref": key_ref
        }
        LOG.debug("DELETE /v1/keys/%s", name, extra=extra)

        # Setup a kvp database object used for verifying permission
        kvp_db = KeyValuePairDB(
            uid="%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR, scope, key_ref),
            scope=scope,
            name=key_ref,
        )

        # Check that user has permission to the key value pair.
        # If RBAC is enabled, this check will verify if user has system role with all access.
        # If RBAC is enabled, this check guards against a user accessing another user's kvp.
        # If RBAC is enabled, user needs to be explicitly granted permission to delete a system kvp.
        # The check is sufficient to allow decryption of the system kvp.
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=kvp_db,
            permission_type=PermissionType.KEY_VALUE_PAIR_DELETE,
        )

        # Acquire a lock to avoid race condition between concurrent API calls
        with self._coordinator.get_lock(
                self._get_lock_name_for_key(name=key_ref, scope=scope)):
            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)

            extra["kvp_db"] = kvp_db
            LOG.debug("DELETE /v1/keys/%s", name, extra=extra)

            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

        LOG.audit("DELETE /v1/keys/%s succeeded id=%s",
                  name,
                  kvp_db.id,
                  extra=extra)

        return Response(status=http_client.NO_CONTENT)
Exemple #19
0
 def test_get_datastore_full_scope_all_scope(self):
     self.assertEqual(
         kv_utl.get_datastore_full_scope(ALL_SCOPE),
         ALL_SCOPE
     )
Exemple #20
0
 def test_get_datastore_full_scope(self):
     self.assertEqual(
         kv_utl.get_datastore_full_scope(USER_SCOPE),
         DATASTORE_SCOPE_SEPARATOR.join([DATASTORE_PARENT_SCOPE, USER_SCOPE])
     )
Exemple #21
0
    def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False):
        """
            List key by name.

            Handle:
                GET /keys/key1
        """
        if not scope:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_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 needs to be either admin or requesting item for itself
        self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope,
                                               requester_user=requester_user)

        user_query_param_filter = bool(user)

        current_user = requester_user.name
        user = user or requester_user.name

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

        # Additional guard to ensure there is no information leakage across users
        is_admin = rbac_utils.user_is_admin(user_db=requester_user)

        if is_admin and user_query_param_filter:
            # Retrieve values scoped to the provided user
            user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE, user=user)
        else:
            # RBAC not enabled or user is not an admin, retrieve user scoped values for the
            # current user
            user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE,
                                                  user=current_user)

        if scope == FULL_USER_SCOPE:
            key_ref = user_scope_prefix
        elif scope == FULL_SYSTEM_SCOPE:
            key_ref = get_key_reference(scope=FULL_SYSTEM_SCOPE, name=name, user=user)
        else:
            raise ValueError('Invalid scope: %s' % (scope))

        from_model_kwargs = {'mask_secrets': not decrypt}
        kvp_api = self._get_one_by_scope_and_name(
            name=key_ref,
            scope=scope,
            from_model_kwargs=from_model_kwargs
        )

        return kvp_api
Exemple #22
0
    def get_one(self,
                name,
                requester_user,
                scope=None,
                user=None,
                decrypt=False):
        """
        List key by name.

        Handle:
            GET /keys/key1
        """
        if not scope or scope == ALL_SCOPE:
            # Default to system scope
            scope = FULL_SYSTEM_SCOPE

        if user:
            # Providing a user implies a user scope
            scope = FULL_USER_SCOPE

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

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

        user = user or requester_user.name

        rbac_utils = get_rbac_backend().get_utils_class()

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

        # Set key reference for system or user scope
        key_ref = get_key_reference(scope=scope, name=name, user=user)
        extra = {
            "scope": scope,
            "name": name,
            "user": user,
            "key_ref": key_ref
        }
        LOG.debug("GET /v1/keys/%s", name, extra=extra)

        # Setup a kvp database object used for verifying permission
        kvp_db = KeyValuePairDB(
            uid="%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR, scope, key_ref),
            scope=scope,
            name=key_ref,
        )

        # Check that user has permission to the key value pair.
        # If RBAC is enabled, this check will verify if user has system role with all access.
        # If RBAC is enabled, this check guards against a user accessing another user's kvp.
        # If RBAC is enabled, user needs to be explicitly granted permission to view a system kvp.
        # The check is sufficient to allow decryption of the system kvp.
        rbac_utils.assert_user_has_resource_db_permission(
            user_db=requester_user,
            resource_db=kvp_db,
            permission_type=PermissionType.KEY_VALUE_PAIR_VIEW,
        )

        from_model_kwargs = {"mask_secrets": not decrypt}
        kvp_api = self._get_one_by_scope_and_name(
            name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs)
        if decrypt and kvp_api.secret:
            LOG.audit(
                "User %s decrypted the value %s ",
                user,
                name,
                extra={
                    "user": user,
                    "scope": scope,
                    "key_name": name,
                    "operation": "decrypt",
                },
            )

        return kvp_api
Exemple #23
0
    def put(self, kvp, name, requester_user, scope=FULL_SYSTEM_SCOPE):
        """
        Create a new entry or update an existing one.
        """
        if not scope:
            scope = FULL_SYSTEM_SCOPE

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

        scope = getattr(kvp, 'scope', scope)
        scope = get_datastore_full_scope(scope)
        self._validate_scope(scope=scope)

        user = getattr(kvp, 'user', requester_user.name) or requester_user.name

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

        # Validate that encrypted option can only be used by admins
        encrypted = getattr(kvp, 'encrypted', False)
        self._validate_encrypted_query_parameter(encrypted=encrypted, scope=scope,
                                                 requester_user=requester_user)

        key_ref = get_key_reference(scope=scope, name=name, user=user)
        lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope)
        LOG.debug('PUT scope: %s, name: %s', scope, name)
        # TODO: Custom permission check since the key doesn't need to exist here

        # Note: We use lock to avoid a race
        with self._coordinator.get_lock(lock_name):
            try:
                existing_kvp_api = self._get_one_by_scope_and_name(
                    scope=scope,
                    name=key_ref
                )
            except StackStormDBObjectNotFoundError:
                existing_kvp_api = None

            # st2client sends invalid id when initially setting a key so we ignore those
            id_ = kvp.__dict__.get('id', None)
            if not existing_kvp_api and id_ and not bson.ObjectId.is_valid(id_):
                del kvp.__dict__['id']

            kvp.name = key_ref
            kvp.scope = scope

            try:
                kvp_db = KeyValuePairAPI.to_model(kvp)

                if existing_kvp_api:
                    kvp_db.id = existing_kvp_api.id

                kvp_db = KeyValuePair.add_or_update(kvp_db)
            except (ValidationError, ValueError) as e:
                LOG.exception('Validation failed for key value data=%s', kvp)
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return
            except CryptoKeyNotSetupException as e:
                LOG.exception(six.text_type(e))
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return
            except InvalidScopeException as e:
                LOG.exception(six.text_type(e))
                abort(http_client.BAD_REQUEST, six.text_type(e))
                return
        extra = {'kvp_db': kvp_db}
        LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)

        kvp_api = KeyValuePairAPI.from_model(kvp_db)
        return kvp_api