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 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 #3
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 #4
0
    def setUp(self):
        super(KeyValuesControllerRBACTestCase, self).setUp()

        self.kvps = {}

        # Insert mock users
        user_1_db = UserDB(name='user1')
        user_1_db = User.add_or_update(user_1_db)
        self.users['user_1'] = user_1_db

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

        # Insert mock kvp objects
        kvp_api = KeyValuePairSetAPI(name='test_system_scope', value='value1',
                                     scope=FULL_SYSTEM_SCOPE)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_1'] = kvp_db

        kvp_api = KeyValuePairSetAPI(name='test_system_scope_secret', value='value_secret',
                                     scope=FULL_SYSTEM_SCOPE, secret=True)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_2'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_1', user='******')
        kvp_db = KeyValuePairDB(name=name, value='valueu12', scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_3'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_2', user='******')
        kvp_api = KeyValuePairSetAPI(name=name, value='user_secret', scope=FULL_USER_SCOPE,
                                     secret=True)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_4'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name='test_user_scope_3', user='******')
        kvp_db = KeyValuePairDB(name=name, value='valueu21', scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_5'] = kvp_db

        self.system_scoped_items_count = 2
        self.user_scoped_items_count = 3
        self.user_scoped_items_per_user_count = {
            'user1': 2,
            'user2': 1
        }
Exemple #5
0
    def setUp(self):
        super(KeyValuesControllerRBACTestCase, self).setUp()

        self.kvps = {}

        # Insert mock users
        user_1_db = UserDB(name="user1")
        user_1_db = User.add_or_update(user_1_db)
        self.users["user_1"] = user_1_db

        user_2_db = UserDB(name="user2")
        user_2_db = User.add_or_update(user_2_db)
        self.users["user_2"] = user_2_db

        # Insert mock kvp objects
        kvp_api = KeyValuePairSetAPI(name="test_system_scope", value="value1", scope=FULL_SYSTEM_SCOPE)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps["kvp_1"] = kvp_db

        kvp_api = KeyValuePairSetAPI(
            name="test_system_scope_secret", value="value_secret", scope=FULL_SYSTEM_SCOPE, secret=True
        )
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps["kvp_2"] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_1", user="******")
        kvp_db = KeyValuePairDB(name=name, value="valueu12", scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps["kvp_3"] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_2", user="******")
        kvp_api = KeyValuePairSetAPI(name=name, value="user_secret", scope=FULL_USER_SCOPE, secret=True)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps["kvp_4"] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE, name="test_user_scope_3", user="******")
        kvp_db = KeyValuePairDB(name=name, value="valueu21", scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps["kvp_5"] = kvp_db

        self.system_scoped_items_count = 2
        self.user_scoped_items_count = 3
        self.user_scoped_items_per_user_count = {"user1": 2, "user2": 1}
Exemple #6
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 #7
0
    def get_one(self, name, scope=SYSTEM_SCOPE, user=None, decrypt=False):
        """
            List key by name.

            Handle:
                GET /keys/key1
        """
        self._validate_scope(scope=scope)

        if user:
            # Providing a user implies a user scope
            scope = USER_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_provider(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 #8
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 #9
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 #10
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
 def test_get_key_reference_user_scope(self):
     ref = get_key_reference(scope=USER_SCOPE, name='foo', user='******')
     self.assertEqual(ref, 'stanley:foo')
     self.assertRaises(InvalidUserException,
                       get_key_reference,
                       scope=USER_SCOPE,
                       name='foo',
                       user='')
Exemple #12
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 #13
0
 def test_get_key_reference_user_scope(self):
     ref = get_key_reference(scope=USER_SCOPE, name="foo", user="******")
     self.assertEqual(ref, "stanley:foo")
     self.assertRaises(
         InvalidUserException,
         get_key_reference,
         scope=USER_SCOPE,
         name="foo",
         user="",
     )
Exemple #14
0
    def test_admin_permissions_for_user_scoped_kvps(self):
        resolver = KeyValuePermissionsResolver()

        admin_user_db = self.users["admin"]

        # Setup users. No explicit grant, role, and assignment records should be
        # required for user to access their KVPs
        user_1_db = UserDB(name="user105")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        # Insert user scoped key value pairs for user1.
        key_1_name = "mykey5"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval5",
        )
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        # Admin user should have general list permissions on user1's kvps.
        self.assertUserHasResourceDbPermission(
            resolver=resolver,
            user_db=admin_user_db,
            resource_db=KeyValuePairDB(scope="%s:%s" %
                                       (FULL_USER_SCOPE, user_1_db.name)),
            permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
        )

        # Admin user should have all permissions to another user1's kvp.
        self.assertUserHasResourceDbPermission(
            resolver=resolver,
            user_db=admin_user_db,
            resource_db=kvp_1_db,
            permission_type=PermissionType.KEY_VALUE_PAIR_ALL,
        )

        self.assertUserHasResourceDbPermissions(
            resolver=resolver,
            user_db=admin_user_db,
            resource_db=kvp_1_db,
            permission_types=self.read_permission_types,
        )

        self.assertUserHasResourceDbPermissions(
            resolver=resolver,
            user_db=admin_user_db,
            resource_db=kvp_1_db,
            permission_types=self.write_permission_types,
        )
Exemple #15
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 #16
0
def set_datastore_value_for_config_key(pack_name,
                                       key_name,
                                       value,
                                       secret=False,
                                       user=None):
    """
    Set config value in the datastore.

    This function takes care of correctly encoding the key name, serializing the
    value, etc.

    :param pack_name: Pack name.
    :type pack_name: ``str``

    :param key_name: Config key name.
    :type key_name: ``str``

    :param secret: True if this value is a secret.
    :type secret: ``bool``

    :param user: Optional username if working on a user-scoped config item.
    :type user: ``str``

    :rtype: :class:`KeyValuePairDB`
    """

    if user:
        scope = FULL_USER_SCOPE
    else:
        scope = FULL_SYSTEM_SCOPE

    name = get_key_reference(scope=scope, name=key_name, user=user)

    kvp_api = KeyValuePairAPI(name=name,
                              value=value,
                              scope=scope,
                              secret=secret)
    kvp_db = KeyValuePairAPI.to_model(kvp_api)

    # TODO: Obtain a lock
    try:
        existing_kvp_db = KeyValuePair.get_by_scope_and_name(scope=scope,
                                                             name=name)
    except StackStormDBObjectNotFoundError:
        existing_kvp_db = None

    if existing_kvp_db:
        kvp_db.id = existing_kvp_db.id

    kvp_db = KeyValuePair.add_or_update(kvp_db)
    return kvp_db
Exemple #17
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 #18
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 #19
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 #20
0
def set_datastore_value_for_config_key(pack_name, key_name, value, secret=False, user=None):
    """
    Set config value in the datastore.

    This function takes care of correctly encoding the key name, serializing the
    value, etc.

    :param pack_name: Pack name.
    :type pack_name: ``str``

    :param key_name: Config key name.
    :type key_name: ``str``

    :param secret: True if this value is a secret.
    :type secret: ``bool``

    :param user: Optional username if working on a user-scoped config item.
    :type user: ``str``

    :rtype: :class:`KeyValuePairDB`
    """

    if user:
        scope = USER_SCOPE
    else:
        scope = SYSTEM_SCOPE

    name = get_key_reference(scope=scope, name=key_name, user=user)

    kvp_api = KeyValuePairAPI(name=name, value=value, scope=scope, secret=secret)
    kvp_db = KeyValuePairAPI.to_model(kvp_api)

    # TODO: Obtain a lock
    existing_kvp_db = KeyValuePair.get_by_scope_and_name(scope=scope, name=name)
    if existing_kvp_db:
        kvp_db.id = existing_kvp_db.id

    kvp_db = KeyValuePair.add_or_update(kvp_db)
    return kvp_db
 def test_get_key_reference_system_scope(self):
     ref = get_key_reference(scope=SYSTEM_SCOPE, name='foo')
     self.assertEqual(ref, 'foo')
Exemple #22
0
    def setUp(self):
        super(KeyValuesControllerRBACTestCase, self).setUp()

        self.kvps = {}

        # Insert mock users
        user_1_db = UserDB(name='user1')
        user_1_db = User.add_or_update(user_1_db)
        self.users['user_1'] = user_1_db

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

        # Insert mock kvp objects
        kvp_api = KeyValuePairSetAPI(name='test_system_scope',
                                     value='value1',
                                     scope=FULL_SYSTEM_SCOPE)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_1'] = kvp_db

        kvp_api = KeyValuePairSetAPI(name='test_system_scope_secret',
                                     value='value_secret',
                                     scope=FULL_SYSTEM_SCOPE,
                                     secret=True)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_2'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE,
                                 name='test_user_scope_1',
                                 user='******')
        kvp_db = KeyValuePairDB(name=name,
                                value='valueu12',
                                scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_3'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE,
                                 name='test_user_scope_2',
                                 user='******')
        kvp_api = KeyValuePairSetAPI(name=name,
                                     value='user_secret',
                                     scope=FULL_USER_SCOPE,
                                     secret=True)
        kvp_db = KeyValuePairSetAPI.to_model(kvp_api)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_4'] = kvp_db

        name = get_key_reference(scope=FULL_USER_SCOPE,
                                 name='test_user_scope_3',
                                 user='******')
        kvp_db = KeyValuePairDB(name=name,
                                value='valueu21',
                                scope=FULL_USER_SCOPE)
        kvp_db = KeyValuePair.add_or_update(kvp_db)
        kvp_db = KeyValuePairAPI.from_model(kvp_db)
        self.kvps['kvp_5'] = kvp_db

        self.system_scoped_items_count = 2
        self.user_scoped_items_count = 3
        self.user_scoped_items_per_user_count = {'user1': 2, 'user2': 1}
Exemple #23
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
    def test_user_permissions_for_user_scope_kvps(self):
        # Insert user scoped key value pairs for user1.
        user_1_db = UserDB(name="user111")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        key_1_name = "mykey1"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_api = KeyValuePairSetAPI(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval1",
            secret=True,
        )
        kvp_1_db = KeyValuePairSetAPI.to_model(kvp_1_api)
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        # Insert user scoped key value pairs for user2.
        user_2_db = UserDB(name="user112")
        user_2_db = User.add_or_update(user_2_db)
        self.users[user_2_db.name] = user_2_db

        key_2_name = "mykey2"
        key_2_ref = get_key_reference(FULL_USER_SCOPE, key_2_name,
                                      user_2_db.name)
        kvp_2_api = KeyValuePairSetAPI(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_2_ref),
            scope=FULL_USER_SCOPE,
            name=key_2_ref,
            value="myval2",
            secret=True,
        )
        kvp_2_db = KeyValuePairSetAPI.to_model(kvp_2_api)
        kvp_2_db = KeyValuePair.add_or_update(kvp_2_db)
        self.resources[kvp_2_db.uid] = kvp_2_db

        # Set context to user
        self.use_user(self.users[user_1_db.name])

        # User should be able to list the system and user scoped kvps that user has permission to.
        resp = self.app.get(
            "/v1/keys?limit=-1")  # server defaults no scope to system scope
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get(
            "/v1/keys/")  # server defaults no scope to system scope
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get("/v1/keys?scope=all")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 1)
        self.assertTrue(
            all([item["scope"] == FULL_USER_SCOPE for item in resp.json]))

        resp = self.app.get("/v1/keys?scope=system")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get("/v1/keys?scope=user")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 1)
        self.assertTrue(
            all([item["scope"] == FULL_USER_SCOPE for item in resp.json]))

        # User should have read and write permissions to his/her own kvps.
        k, v = key_1_name, kvp_1_api.value
        resp = self.app.get("/v1/keys/%s?decrypt=True&scope=user" % k)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(resp.json["value"], v)

        d = {
            "name": key_1_ref,
            "value": "value for %s" % k,
            "scope": FULL_USER_SCOPE,
            "secret": True,
        }
        resp = self.app.put_json("/v1/keys/%s?scope=user" % k, d)
        self.assertEqual(resp.status_int, http_client.OK)

        resp = self.app.get("/v1/keys/%s?decrypt=True&scope=user" % k)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(resp.json["value"], "value for %s" % k)

        resp = self.app.delete("/v1/keys/%s?scope=user" % k)
        self.assertEqual(resp.status_code, http_client.NO_CONTENT)

        resp = self.app.get("/v1/keys/%s?scope=user" % k, expect_errors=True)
        self.assertEqual(resp.status_int, http_client.NOT_FOUND)
    def test_user_permissions_for_another_user_kvps(self):
        # Setup users.
        user_1_db = UserDB(name="user113")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        user_2_db = UserDB(name="user114")
        user_2_db = User.add_or_update(user_2_db)
        self.users[user_2_db.name] = user_2_db

        # Insert user scoped key value pairs for user1.
        key_1_name = "mykey3"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_api = KeyValuePairSetAPI(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval3",
            secret=True,
        )
        kvp_1_db = KeyValuePairSetAPI.to_model(kvp_1_api)
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        # Setup bad grant, role, and assignment records where administrator
        # accidentally or intentionally try to grant a user's kvps to another user.
        grant_db = PermissionGrantDB(
            resource_uid=kvp_1_db.get_uid(),
            resource_type=ResourceType.KEY_VALUE_PAIR,
            permission_types=[PermissionType.KEY_VALUE_PAIR_ALL],
        )
        grant_db = PermissionGrant.add_or_update(grant_db)

        role_db = RoleDB(
            name="custom_role_user_key3_all_grant",
            permission_grants=[str(grant_db.id)],
        )
        role_db = Role.add_or_update(role_db)
        self.roles[role_db.name] = role_db

        role_assignment_db = UserRoleAssignmentDB(
            user=user_2_db.name,
            role=role_db.name,
            source="assignments/%s.yaml" % user_2_db.name,
        )
        UserRoleAssignment.add_or_update(role_assignment_db)

        # Set context to user
        self.use_user(self.users[user_2_db.name])

        # User2 should not be able to list user1's kvp.
        resp = self.app.get(
            "/v1/keys?limit=-1")  # server defaults no scope to system scope
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get(
            "/v1/keys/")  # server defaults no scope to system scope
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get("/v1/keys?scope=all")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get("/v1/keys?scope=system")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        resp = self.app.get("/v1/keys?scope=user")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        # User2 should not have read and write permissions on user1's kvp.
        k = key_1_name
        url = "/v1/keys/%s?scope=user&user=%s" % (k, user_1_db.name)
        resp = self.app.get(url, expect_errors=True)
        self.assertEqual(resp.status_int, http_client.FORBIDDEN)

        d = {
            "name": key_1_ref,
            "value": "value for %s" % k,
            "scope": FULL_USER_SCOPE,
            "user": user_1_db.name,
        }
        resp = self.app.put_json(url, d, expect_errors=True)
        self.assertEqual(resp.status_int, http_client.FORBIDDEN)

        resp = self.app.delete(url, expect_errors=True)
        self.assertEqual(resp.status_code, http_client.FORBIDDEN)
Exemple #26
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)
    def setUp(self):
        super(KeyValueSystemScopeControllerRBACTestCase, self).setUp()

        # Insert system scoped key value pairs.
        kvp_1_api = KeyValuePairSetAPI(
            uid="%s:%s:key1" %
            (ResourceType.KEY_VALUE_PAIR, FULL_SYSTEM_SCOPE),
            scope=FULL_SYSTEM_SCOPE,
            name="key1",
            value="val1",
            secret=True,
        )
        kvp_1_db = KeyValuePairSetAPI.to_model(kvp_1_api)
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        kvp_2_api = KeyValuePairSetAPI(
            uid="%s:%s:key2" %
            (ResourceType.KEY_VALUE_PAIR, FULL_SYSTEM_SCOPE),
            scope=FULL_SYSTEM_SCOPE,
            name="key2",
            value="val2",
            secret=True,
        )
        kvp_2_db = KeyValuePairSetAPI.to_model(kvp_2_api)
        kvp_2_db = KeyValuePair.add_or_update(kvp_2_db)
        self.resources[kvp_2_db.uid] = kvp_2_db

        # Setup users for user scoped KVPs.
        user_1_db = UserDB(name="user101")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        user_2_db = UserDB(name="user102")
        user_2_db = User.add_or_update(user_2_db)
        self.users[user_2_db.name] = user_2_db

        # Insert user scoped key value pairs for user1.
        key_1_name = "mykey1"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval1",
        )
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        key_2_name = "mykey2"
        key_2_ref = get_key_reference(FULL_USER_SCOPE, key_2_name,
                                      user_1_db.name)
        kvp_2_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_2_ref),
            scope=FULL_USER_SCOPE,
            name=key_2_ref,
            value="myval2",
        )
        kvp_2_db = KeyValuePair.add_or_update(kvp_2_db)
        self.resources[kvp_2_db.uid] = kvp_2_db
Exemple #28
0
 def test_get_key_reference_user_scope(self):
     ref = get_key_reference(scope=USER_SCOPE, name='foo', user='******')
     self.assertEqual(ref, 'stanley:foo')
     self.assertRaises(InvalidUserException, get_key_reference,
                       scope=USER_SCOPE, name='foo', user='')
Exemple #29
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 #30
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 #31
0
    def test_user_permissions_for_another_user_kvps(self):
        resolver = KeyValuePermissionsResolver()

        # Setup users. No explicit grant, role, and assignment records should be
        # required for user to access their KVPs
        user_1_db = UserDB(name="user103")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        user_2_db = UserDB(name="user104")
        user_2_db = User.add_or_update(user_2_db)
        self.users[user_2_db.name] = user_2_db

        # Insert user scoped key value pairs for user1.
        key_1_name = "mykey3"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval3",
        )
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        # Setup bad grant, role, and assignment records where administrator
        # accidentally or intentionally try to grant a user's kvps to another user.
        grant_db = PermissionGrantDB(
            resource_uid=kvp_1_db.get_uid(),
            resource_type=ResourceType.KEY_VALUE_PAIR,
            permission_types=[PermissionType.KEY_VALUE_PAIR_ALL],
        )
        grant_db = PermissionGrant.add_or_update(grant_db)

        role_db = RoleDB(
            name="custom_role_user_key3_all_grant",
            permission_grants=[str(grant_db.id)],
        )
        role_db = Role.add_or_update(role_db)
        self.roles[role_db.name] = role_db

        role_assignment_db = UserRoleAssignmentDB(
            user=user_2_db.name,
            role=role_db.name,
            source="assignments/%s.yaml" % user_2_db.name,
        )
        UserRoleAssignment.add_or_update(role_assignment_db)

        # User2 should not have general list permissions on user1's kvps.
        self.assertUserDoesntHaveResourceDbPermission(
            resolver=resolver,
            user_db=user_2_db,
            resource_db=KeyValuePairDB(scope="%s:%s" %
                                       (FULL_USER_SCOPE, user_1_db.name)),
            permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
        )

        # User2 should not have any permissions on another user1's kvp.
        self.assertUserDoesntHaveResourceDbPermission(
            resolver=resolver,
            user_db=user_2_db,
            resource_db=kvp_1_db,
            permission_type=PermissionType.KEY_VALUE_PAIR_ALL,
        )

        self.assertUserDoesntHaveResourceDbPermissions(
            resolver=resolver,
            user_db=user_2_db,
            resource_db=kvp_1_db,
            permission_types=self.read_permission_types,
        )

        self.assertUserDoesntHaveResourceDbPermissions(
            resolver=resolver,
            user_db=user_2_db,
            resource_db=kvp_1_db,
            permission_types=self.write_permission_types,
        )
Exemple #32
0
    def test_user_permissions_for_user_scope_kvps(self):
        resolver = KeyValuePermissionsResolver()

        # Setup users. No explicit grant, role, and assignment records should be
        # required for user to access their KVPs
        user_1_db = UserDB(name="user101")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        user_2_db = UserDB(name="user102")
        user_2_db = User.add_or_update(user_2_db)
        self.users[user_2_db.name] = user_2_db

        # Insert user scoped key value pairs for user1.
        key_1_name = "mykey1"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval1",
        )
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        key_2_name = "mykey2"
        key_2_ref = get_key_reference(FULL_USER_SCOPE, key_2_name,
                                      user_1_db.name)
        kvp_2_db = KeyValuePairDB(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_2_ref),
            scope=FULL_USER_SCOPE,
            name=key_2_ref,
            value="myval2",
        )
        kvp_2_db = KeyValuePair.add_or_update(kvp_2_db)
        self.resources[kvp_2_db.uid] = kvp_2_db

        # User1 should have general list permissions on user1's kvps.
        self.assertUserHasResourceDbPermission(
            resolver=resolver,
            user_db=user_1_db,
            resource_db=KeyValuePairDB(scope="%s:%s" %
                                       (FULL_USER_SCOPE, user_1_db.name)),
            permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
        )

        # User1 should have all, read, and write permissions on user1's kvps.
        for k in [key_1_name, key_2_name]:
            kvp_ref = get_key_reference(FULL_USER_SCOPE, k, user_1_db.name)
            kvp_uid = "%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR,
                                    FULL_USER_SCOPE, kvp_ref)
            kvp_db = self.resources[kvp_uid]

            self.assertUserHasResourceDbPermission(
                resolver=resolver,
                user_db=user_1_db,
                resource_db=kvp_db,
                permission_type=PermissionType.KEY_VALUE_PAIR_ALL,
            )

            self.assertUserHasResourceDbPermissions(
                resolver=resolver,
                user_db=user_1_db,
                resource_db=kvp_db,
                permission_types=self.read_permission_types,
            )

            self.assertUserHasResourceDbPermissions(
                resolver=resolver,
                user_db=user_1_db,
                resource_db=kvp_db,
                permission_types=self.write_permission_types,
            )

        # User2 should not have general list permissions on user1's kvps.
        self.assertUserDoesntHaveResourceDbPermission(
            resolver=resolver,
            user_db=user_2_db,
            resource_db=KeyValuePairDB(scope="%s:%s" %
                                       (FULL_USER_SCOPE, user_1_db.name)),
            permission_type=PermissionType.KEY_VALUE_PAIR_LIST,
        )

        # User2 should not have any permissions on user1's kvps.
        for k in [key_1_name, key_2_name]:
            kvp_ref = get_key_reference(FULL_USER_SCOPE, k, user_1_db.name)
            kvp_uid = "%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR,
                                    FULL_USER_SCOPE, kvp_ref)
            kvp_db = self.resources[kvp_uid]

            self.assertUserDoesntHaveResourceDbPermission(
                resolver=resolver,
                user_db=user_2_db,
                resource_db=kvp_db,
                permission_type=PermissionType.KEY_VALUE_PAIR_ALL,
            )

            self.assertUserDoesntHaveResourceDbPermissions(
                resolver=resolver,
                user_db=user_2_db,
                resource_db=kvp_db,
                permission_types=self.read_permission_types,
            )

            self.assertUserDoesntHaveResourceDbPermissions(
                resolver=resolver,
                user_db=user_2_db,
                resource_db=kvp_db,
                permission_types=self.write_permission_types,
            )
Exemple #33
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 #34
0
 def test_get_key_reference_system_scope(self):
     ref = get_key_reference(scope=SYSTEM_SCOPE, name='foo')
     self.assertEqual(ref, 'foo')
    def test_admin_permissions_for_user_scoped_kvps(self):
        # Insert user scoped key value pairs for user1.
        user_1_db = UserDB(name="user115")
        user_1_db = User.add_or_update(user_1_db)
        self.users[user_1_db.name] = user_1_db

        key_1_name = "mykey5"
        key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name,
                                      user_1_db.name)
        kvp_1_api = KeyValuePairSetAPI(
            uid="%s:%s:%s" %
            (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref),
            scope=FULL_USER_SCOPE,
            name=key_1_ref,
            value="myval5",
            secret=True,
        )
        kvp_1_db = KeyValuePairSetAPI.to_model(kvp_1_api)
        kvp_1_db = KeyValuePair.add_or_update(kvp_1_db)
        self.resources[kvp_1_db.uid] = kvp_1_db

        # Set context to user
        self.use_user(self.users["admin"])

        # Admin user should have general list permissions on user1's kvps.
        resp = self.app.get("/v1/keys?limit=-1&scope=user&user=%s" %
                            user_1_db.name)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 1)

        resp = self.app.get("/v1/keys?scope=user&user=%s" % user_1_db.name)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 1)
        self.assertEqual(resp.json[0]["name"], key_1_name)
        self.assertEqual(resp.json[0]["user"], user_1_db.name)

        resp = self.app.get("/v1/keys?decrypt=True&scope=user&user=%s" %
                            user_1_db.name)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 1)
        self.assertEqual(resp.json[0]["name"], key_1_name)
        self.assertEqual(resp.json[0]["user"], user_1_db.name)
        self.assertEqual(resp.json[0]["value"], kvp_1_api.value)

        resp = self.app.get("/v1/keys?scope=all")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 2)
        self.assertTrue(
            all([item["scope"] == FULL_SYSTEM_SCOPE for item in resp.json]))

        resp = self.app.get("/v1/keys?scope=system")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 2)
        self.assertTrue(
            all([item["scope"] == FULL_SYSTEM_SCOPE for item in resp.json]))

        resp = self.app.get("/v1/keys?scope=user")
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(len(resp.json), 0)

        # Admin user should have read and write permissions to user1's kvps.
        k, v = key_1_name, kvp_1_api.value
        url = "/v1/keys/%s?decrypt=True&scope=user&user=%s" % (k,
                                                               user_1_db.name)
        resp = self.app.get(url)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(resp.json["value"], v)

        d = {
            "name": key_1_ref,
            "value": "value for %s" % k,
            "scope": FULL_USER_SCOPE,
            "user": user_1_db.name,
            "secret": True,
        }
        resp = self.app.put_json(url, d)
        self.assertEqual(resp.status_int, http_client.OK)

        resp = self.app.get(url)
        self.assertEqual(resp.status_int, http_client.OK)
        self.assertEqual(resp.json["value"], "value for %s" % k)

        resp = self.app.delete(url)
        self.assertEqual(resp.status_code, http_client.NO_CONTENT)

        resp = self.app.get(url, expect_errors=True)
        self.assertEqual(resp.status_int, http_client.NOT_FOUND)
Exemple #36
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
Exemple #37
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