def test_delete_action_files_from_pack(self): """ Test that the action files present in the pack and removed on the call of delete_action_files_from_pack function. """ entry_point = os.path.join(TEST_PACK_PATH, "actions", "test_entry_point.py") metadata_file = os.path.join(TEST_PACK_PATH, "actions", "test_metadata.yaml") # creating entry_point file in dummy pack with open(entry_point, "w") as f: f.write("# entry point file to be removed") # creating metadata file in dummy pack with open(metadata_file, "w") as f: f.write("# metadata file to be removed") # asserting both entry_point and metadata files exists self.assertTrue(os.path.exists(entry_point)) self.assertTrue(os.path.exists(metadata_file)) delete_action_files_from_pack(TEST_PACK, entry_point, metadata_file) # asserting both entry_point and metadata files removed and they doesn't exist self.assertFalse(os.path.exists(entry_point)) self.assertFalse(os.path.exists(metadata_file))
def test_exception_to_remove_resource_metadata_file(self, remove): entry_point = os.path.join(TEST_PACK_PATH, "actions", "test_entry_point.py") metadata_file = os.path.join(TEST_PACK_PATH, "actions", "test_metadata.yaml") remove.side_effect = Exception("Another exception occured") # asserting metadata file exists self.assertTrue(os.path.exists(metadata_file)) # asserting entry_point file doesn't exist self.assertFalse(os.path.exists(entry_point)) expected_msg = ( 'The action file "%s" could not be removed from disk, please ' "check the logs or ask your StackStorm administrator to check " "and delete the actions files manually" % (metadata_file)) # asserting exception with message on call of delete_action_files_from_pack # to delete metadata file with self.assertRaisesRegexp(Exception, expected_msg): delete_action_files_from_pack(TEST_PACK, entry_point, metadata_file)
def test_permission_error_to_remove_resource_entry_point_file( self, remove): entry_point = os.path.join(TEST_PACK_PATH, "actions", "test_entry_point.py") metadata_file = os.path.join(TEST_PACK_PATH, "actions", "test_metadata.yaml") remove.side_effect = PermissionError( "No permission to delete file from disk") # asserting entry_point file exists self.assertTrue(os.path.exists(entry_point)) # asserting metadata file doesn't exist self.assertFalse(os.path.exists(metadata_file)) expected_msg = 'No permission to delete "%s" file from disk' % ( entry_point) # asserting PermissionError with message on call of delete_action_files_from_pack # to delete entry_point file with self.assertRaisesRegexp(PermissionError, expected_msg): delete_action_files_from_pack(TEST_PACK, entry_point, metadata_file)
def test_metadata_file_does_not_exists(self): """ Tests that metadata file doesn't exists at the path and if action delete api calls delete_action_files_from_pack function, it doesn't affect. """ entry_point = os.path.join(TEST_PACK_PATH, "actions", "test_entry_point.py") metadata_file = os.path.join(TEST_PACK_PATH, "actions", "test_metadata.yaml") # creating only entry_point file in dummy pack with open(entry_point, "w") as f: f.write("# entry point file to be removed") # asserting metadata file doesn't exist self.assertFalse(os.path.exists(metadata_file)) # asserting entry_point file exists self.assertTrue(os.path.exists(entry_point)) delete_action_files_from_pack(TEST_PACK, entry_point, metadata_file) # asserting both entry_point and metadata files don't exist self.assertFalse(os.path.exists(entry_point)) self.assertFalse(os.path.exists(metadata_file))
def delete(self, options, ref_or_id, requester_user): """ Delete an action. Handles requests: POST /actions/1?_method=delete DELETE /actions/1 DELETE /actions/mypack.myaction """ action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) action_id = action_db.id permission_type = PermissionType.ACTION_DELETE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=permission_type, ) try: validate_not_part_of_system_pack(action_db) except ValueValidationException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) LOG.debug( "DELETE /actions/ lookup with ref_or_id=%s found object: %s", ref_or_id, action_db, ) pack_name = action_db["pack"] entry_point = action_db["entry_point"] metadata_file = action_db["metadata_file"] try: Action.delete(action_db) except Exception as e: LOG.error( 'Database delete encountered exception during delete of id="%s". ' "Exception was %s", action_id, e, ) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return if options.remove_files: try: delete_action_files_from_pack( pack_name=pack_name, entry_point=entry_point, metadata_file=metadata_file, ) except PermissionError as e: LOG.error("No permission to delete resource files from disk.") action_db.id = None Action.add_or_update(action_db) abort(http_client.FORBIDDEN, six.text_type(e)) return except Exception as e: LOG.error( "Exception encountered during deleting resource files from disk. " "Exception was %s", e, ) action_db.id = None Action.add_or_update(action_db) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) return extra = {"action_db": action_db} LOG.audit("Action deleted. Action.id=%s" % (action_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def clone(self, dest_data, ref_or_id, requester_user): """ Clone an action from source pack to destination pack. Handles requests: POST /actions/{ref_or_id}/clone """ source_action_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) if not source_action_db: msg = "The requested source for cloning operation doesn't exists" abort(http_client.BAD_REQUEST, six.text_type(msg)) extra = {"action_db": source_action_db} LOG.audit("Source action found. Action.id=%s" % (source_action_db.id), extra=extra) try: permission_type = PermissionType.ACTION_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=source_action_db, permission_type=permission_type, ) except ResourceAccessDeniedError as e: abort(http_client.UNAUTHORIZED, six.text_type(e)) cloned_dest_action_db = clone_action_db( source_action_db=source_action_db, dest_pack=dest_data.dest_pack, dest_action=dest_data.dest_action, ) cloned_action_api = ActionAPI.from_model(cloned_dest_action_db) try: permission_type = PermissionType.ACTION_CREATE rbac_utils.assert_user_has_resource_api_permission( user_db=requester_user, resource_api=cloned_action_api, permission_type=permission_type, ) except ResourceAccessDeniedError as e: abort(http_client.UNAUTHORIZED, six.text_type(e)) dest_pack_base_path = get_pack_base_path(pack_name=dest_data.dest_pack) if not os.path.isdir(dest_pack_base_path): msg = "Destination pack '%s' doesn't exist" % (dest_data.dest_pack) abort(http_client.BAD_REQUEST, six.text_type(msg)) dest_pack_base_path = get_pack_base_path(pack_name=dest_data.dest_pack) dest_ref = ".".join([dest_data.dest_pack, dest_data.dest_action]) dest_action_db = self._get_by_ref(resource_ref=dest_ref) try: validate_not_part_of_system_pack_by_name(dest_data.dest_pack) except ValueValidationException as e: abort(http_client.BAD_REQUEST, six.text_type(e)) if dest_action_db: if not dest_data.overwrite: msg = "The requested destination action already exists" abort(http_client.BAD_REQUEST, six.text_type(msg)) try: permission_type = PermissionType.ACTION_DELETE rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=dest_action_db, permission_type=permission_type, ) options = GenericRequestParam(remove_files=True) dest_metadata_file = dest_action_db["metadata_file"] dest_entry_point = dest_action_db["entry_point"] temp_sub_dir = str(uuid.uuid4()) temp_backup_action_files( dest_pack_base_path, dest_metadata_file, dest_entry_point, temp_sub_dir, ) self.delete(options, dest_ref, requester_user) except ResourceAccessDeniedError as e: abort(http_client.UNAUTHORIZED, six.text_type(e)) except Exception as e: LOG.debug( "Exception encountered during deleting existing destination action. " "Exception was: %s", e, ) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e)) try: post_response = self.post(cloned_action_api, requester_user) if post_response.status_code != http_client.CREATED: raise Exception("Could not add cloned action to database.") cloned_dest_action_db["id"] = post_response.json["id"] clone_action_files( source_action_db=source_action_db, dest_action_db=cloned_dest_action_db, dest_pack_base_path=dest_pack_base_path, ) extra = {"cloned_acion_db": cloned_dest_action_db} LOG.audit("Action cloned. Action.id=%s" % (cloned_dest_action_db.id), extra=extra) if dest_action_db: remove_temp_action_files(temp_sub_dir) return post_response except PermissionError as e: LOG.error("No permission to clone the action. Exception was %s", e) delete_action_files_from_pack( pack_name=cloned_dest_action_db["pack"], entry_point=cloned_dest_action_db["entry_point"], metadata_file=cloned_dest_action_db["metadata_file"], ) if post_response.status_code == http_client.CREATED: Action.delete(cloned_dest_action_db) if dest_action_db: self._restore_action(dest_action_db, dest_pack_base_path, temp_sub_dir) abort(http_client.FORBIDDEN, six.text_type(e)) except Exception as e: LOG.error( "Exception encountered during cloning action. Exception was %s", e, ) delete_action_files_from_pack( pack_name=cloned_dest_action_db["pack"], entry_point=cloned_dest_action_db["entry_point"], metadata_file=cloned_dest_action_db["metadata_file"], ) if post_response.status_code == http_client.CREATED: Action.delete(cloned_dest_action_db) if dest_action_db: self._restore_action(dest_action_db, dest_pack_base_path, temp_sub_dir) abort(http_client.INTERNAL_SERVER_ERROR, six.text_type(e))