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
def put(self, name, kvp): """ Create a new entry or update an existing one. """ lock_name = self._get_lock_name_for_key(name=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): existing_kvp = self._get_by_name(resource_name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.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 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 put(self, name, kvp): """ Create a new entry or update an existing one. """ # TODO: There is a race, add custom add_or_update which updates by non # id field existing_kvp = self.__get_by_name(name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.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 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 put(self, name, kvp): """ Create a new entry or update an existing one. """ lock_name = self._get_lock_name_for_key(name=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): existing_kvp = self.__get_by_name(name=name) kvp.name = name try: kvp_db = KeyValuePairAPI.to_model(kvp) if existing_kvp: kvp_db.id = existing_kvp.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 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 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 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)
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)
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
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)
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)
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 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
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 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)