Island: Modify PostBreachFilesService to wrap IFileStorageService
This commit is contained in:
parent
d157bf7a40
commit
a487aa4058
|
@ -1,4 +1,5 @@
|
||||||
from .authentication.authentication_service import AuthenticationService
|
|
||||||
from .authentication.json_file_user_datastore import JsonFileUserDatastore
|
|
||||||
from .i_file_storage_service import IFileStorageService
|
from .i_file_storage_service import IFileStorageService
|
||||||
from .directory_file_storage_service import DirectoryFileStorageService
|
from .directory_file_storage_service import DirectoryFileStorageService
|
||||||
|
|
||||||
|
from .authentication.authentication_service import AuthenticationService
|
||||||
|
from .authentication.json_file_user_datastore import JsonFileUserDatastore
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from monkey_island.cc.services import DirectoryFileStorageService
|
||||||
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
||||||
from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService
|
from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService
|
||||||
|
|
||||||
|
@ -5,6 +6,8 @@ from . import AuthenticationService, JsonFileUserDatastore
|
||||||
|
|
||||||
|
|
||||||
def initialize_services(data_dir):
|
def initialize_services(data_dir):
|
||||||
PostBreachFilesService.initialize(data_dir)
|
# This is temporary until we get DI all worked out.
|
||||||
|
PostBreachFilesService.initialize(DirectoryFileStorageService(data_dir / "custom_pbas"))
|
||||||
|
|
||||||
LocalMonkeyRunService.initialize(data_dir)
|
LocalMonkeyRunService.initialize(data_dir)
|
||||||
AuthenticationService.initialize(data_dir, JsonFileUserDatastore(data_dir))
|
AuthenticationService.initialize(data_dir, JsonFileUserDatastore(data_dir))
|
||||||
|
|
|
@ -1,46 +1,24 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
|
|
||||||
from monkey_island.cc.server_utils.file_utils import create_secure_directory
|
from monkey_island.cc.services import IFileStorageService
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: This service wraps an IFileStorageService for the sole purpose of making the
|
||||||
|
# `remove_PBA_files()` method available to the ConfigService. This whole service can be
|
||||||
|
# removed once ConfigService is refactored to be stateful (it already is but everything is
|
||||||
|
# still statically/globally scoped) and use dependency injection.
|
||||||
class PostBreachFilesService:
|
class PostBreachFilesService:
|
||||||
DATA_DIR = None
|
_file_storage_service = None
|
||||||
CUSTOM_PBA_DIRNAME = "custom_pbas"
|
|
||||||
|
|
||||||
# TODO: A number of these services should be instance objects instead of
|
# TODO: A number of these services should be instance objects instead of
|
||||||
# static/singleton hybrids. At the moment, this requires invasive refactoring that's
|
# static/singleton hybrids. At the moment, this requires invasive refactoring that's
|
||||||
# not a priority.
|
# not a priority.
|
||||||
@classmethod
|
@classmethod
|
||||||
def initialize(cls, data_dir):
|
def initialize(cls, file_storage_service: IFileStorageService):
|
||||||
cls.DATA_DIR = data_dir
|
cls._file_storage_service = file_storage_service
|
||||||
custom_pba_dir = cls.get_custom_pba_directory()
|
|
||||||
create_secure_directory(custom_pba_dir)
|
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def save_file(filename: str, file_contents: bytes):
|
def remove_PBA_files(cls):
|
||||||
file_path = os.path.join(PostBreachFilesService.get_custom_pba_directory(), filename)
|
cls._file_storage_service.delete_all_files()
|
||||||
with open(file_path, "wb") as f:
|
|
||||||
f.write(file_contents)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def remove_PBA_files():
|
|
||||||
for f in os.listdir(PostBreachFilesService.get_custom_pba_directory()):
|
|
||||||
PostBreachFilesService.remove_file(f)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def remove_file(file_name):
|
|
||||||
file_path = os.path.join(PostBreachFilesService.get_custom_pba_directory(), file_name)
|
|
||||||
try:
|
|
||||||
if os.path.exists(file_path):
|
|
||||||
os.remove(file_path)
|
|
||||||
except OSError as e:
|
|
||||||
logger.error("Can't remove previously uploaded post breach files: %s" % e)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_custom_pba_directory():
|
|
||||||
return os.path.join(
|
|
||||||
PostBreachFilesService.DATA_DIR, PostBreachFilesService.CUSTOM_PBA_DIRNAME
|
|
||||||
)
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import pytest
|
||||||
from tests.utils import raise_
|
from tests.utils import raise_
|
||||||
|
|
||||||
from monkey_island.cc.resources.pba_file_upload import LINUX_PBA_TYPE, WINDOWS_PBA_TYPE
|
from monkey_island.cc.resources.pba_file_upload import LINUX_PBA_TYPE, WINDOWS_PBA_TYPE
|
||||||
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
|
||||||
|
|
||||||
TEST_FILE = b"""-----------------------------1
|
TEST_FILE = b"""-----------------------------1
|
||||||
Content-Disposition: form-data; name="filepond"
|
Content-Disposition: form-data; name="filepond"
|
||||||
|
@ -16,11 +15,6 @@ m0nk3y
|
||||||
-----------------------------1--"""
|
-----------------------------1--"""
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def custom_pba_directory(tmpdir):
|
|
||||||
PostBreachFilesService.initialize(tmpdir)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_set_config_value(monkeypatch):
|
def mock_set_config_value(monkeypatch):
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from tests.monkey_island.utils import assert_windows_permissions
|
|
||||||
from tests.utils import raise_
|
from tests.utils import raise_
|
||||||
|
|
||||||
from monkey_island.cc.server_utils.file_utils import is_windows_os
|
from monkey_island.cc.services import DirectoryFileStorageService
|
||||||
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def file_storage_service(tmp_path):
|
||||||
|
return DirectoryFileStorageService(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def custom_pba_directory(tmpdir):
|
def post_breach_files_service(file_storage_service):
|
||||||
PostBreachFilesService.initialize(tmpdir)
|
PostBreachFilesService.initialize(file_storage_service)
|
||||||
|
|
||||||
|
|
||||||
def create_custom_pba_file(filename):
|
def test_remove_pba_files(file_storage_service, tmp_path):
|
||||||
PostBreachFilesService.save_file(filename, b"")
|
file_storage_service.save_file("linux_file", io.BytesIO(b""))
|
||||||
|
file_storage_service.save_file("windows_file", io.BytesIO(b""))
|
||||||
|
assert not dir_is_empty(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
def test_remove_pba_files():
|
|
||||||
create_custom_pba_file("linux_file")
|
|
||||||
create_custom_pba_file("windows_file")
|
|
||||||
|
|
||||||
assert not dir_is_empty(PostBreachFilesService.get_custom_pba_directory())
|
|
||||||
PostBreachFilesService.remove_PBA_files()
|
PostBreachFilesService.remove_PBA_files()
|
||||||
assert dir_is_empty(PostBreachFilesService.get_custom_pba_directory())
|
|
||||||
|
assert dir_is_empty(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
def dir_is_empty(dir_path):
|
def dir_is_empty(dir_path):
|
||||||
|
@ -31,45 +33,11 @@ def dir_is_empty(dir_path):
|
||||||
return len(dir_contents) == 0
|
return len(dir_contents) == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(is_windows_os(), reason="Tests Posix (not Windows) permissions.")
|
def test_remove_failure(file_storage_service, monkeypatch):
|
||||||
def test_custom_pba_dir_permissions_linux():
|
|
||||||
st = os.stat(PostBreachFilesService.get_custom_pba_directory())
|
|
||||||
|
|
||||||
assert st.st_mode == 0o40700
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not is_windows_os(), reason="Tests Windows (not Posix) permissions.")
|
|
||||||
def test_custom_pba_dir_permissions_windows():
|
|
||||||
pba_dir = PostBreachFilesService.get_custom_pba_directory()
|
|
||||||
|
|
||||||
assert_windows_permissions(pba_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def test_remove_failure(monkeypatch):
|
|
||||||
monkeypatch.setattr(os, "remove", lambda x: raise_(OSError("Permission denied")))
|
monkeypatch.setattr(os, "remove", lambda x: raise_(OSError("Permission denied")))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
create_custom_pba_file("windows_file")
|
file_storage_service.save_file("windows_file", io.BytesIO(b""))
|
||||||
PostBreachFilesService.remove_PBA_files()
|
PostBreachFilesService.remove_PBA_files()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
pytest.fail(f"Unxepected exception: {ex}")
|
pytest.fail(f"Unxepected exception: {ex}")
|
||||||
|
|
||||||
|
|
||||||
def test_remove_nonexistant_file(monkeypatch):
|
|
||||||
monkeypatch.setattr(os, "remove", lambda x: raise_(FileNotFoundError("FileNotFound")))
|
|
||||||
|
|
||||||
try:
|
|
||||||
PostBreachFilesService.remove_file("/nonexistant/file")
|
|
||||||
except Exception as ex:
|
|
||||||
pytest.fail(f"Unxepected exception: {ex}")
|
|
||||||
|
|
||||||
|
|
||||||
def test_save_file():
|
|
||||||
FILE_NAME = "test_file"
|
|
||||||
FILE_CONTENTS = b"hello"
|
|
||||||
PostBreachFilesService.save_file(FILE_NAME, FILE_CONTENTS)
|
|
||||||
|
|
||||||
expected_file_path = os.path.join(PostBreachFilesService.get_custom_pba_directory(), FILE_NAME)
|
|
||||||
|
|
||||||
assert os.path.isfile(expected_file_path)
|
|
||||||
assert FILE_CONTENTS == open(expected_file_path, "rb").read()
|
|
||||||
|
|
Loading…
Reference in New Issue