Merge pull request #2073 from guardicore/1965-credential-endpoints
1965 credential endpoints
This commit is contained in:
commit
774b2c0116
|
@ -13,6 +13,7 @@ from monkey_island.cc.database import database, mongo
|
|||
from monkey_island.cc.resources import (
|
||||
AgentBinaries,
|
||||
ClearSimulationData,
|
||||
PropagationCredentials,
|
||||
RemoteRun,
|
||||
ResetAgentConfiguration,
|
||||
)
|
||||
|
@ -42,7 +43,6 @@ from monkey_island.cc.resources.node import Node
|
|||
from monkey_island.cc.resources.node_states import NodeStates
|
||||
from monkey_island.cc.resources.pba_file_download import PBAFileDownload
|
||||
from monkey_island.cc.resources.pba_file_upload import FileUpload
|
||||
from monkey_island.cc.resources.propagation_credentials import PropagationCredentials
|
||||
from monkey_island.cc.resources.ransomware_report import RansomwareReport
|
||||
from monkey_island.cc.resources.root import Root
|
||||
from monkey_island.cc.resources.security_report import SecurityReport
|
||||
|
|
|
@ -2,3 +2,4 @@ from .remote_run import RemoteRun
|
|||
from .agent_binaries import AgentBinaries
|
||||
from .clear_simulation_data import ClearSimulationData
|
||||
from .reset_agent_configuration import ResetAgentConfiguration
|
||||
from .propagation_credentials import PropagationCredentials
|
||||
|
|
|
@ -1,17 +1,55 @@
|
|||
from flask import make_response
|
||||
from http import HTTPStatus
|
||||
|
||||
from flask import make_response, request
|
||||
|
||||
from common.credentials import Credentials
|
||||
from monkey_island.cc.repository import ICredentialsRepository
|
||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||
|
||||
_configured_collection = "configured-credentials"
|
||||
_stolen_collection = "stolen-credentials"
|
||||
|
||||
|
||||
class PropagationCredentials(AbstractResource):
|
||||
urls = ["/api/propagation-credentials"]
|
||||
urls = ["/api/propagation-credentials/", "/api/propagation-credentials/<string:collection>"]
|
||||
|
||||
def __init__(self, credentials_repository: ICredentialsRepository):
|
||||
self._credentials_repository = credentials_repository
|
||||
|
||||
def get(self):
|
||||
propagation_credentials = self._credentials_repository.get_all_credentials()
|
||||
def get(self, collection=None):
|
||||
if collection == _configured_collection:
|
||||
propagation_credentials = self._credentials_repository.get_configured_credentials()
|
||||
elif collection == _stolen_collection:
|
||||
propagation_credentials = self._credentials_repository.get_stolen_credentials()
|
||||
elif collection is None:
|
||||
propagation_credentials = self._credentials_repository.get_all_credentials()
|
||||
else:
|
||||
return {}, HTTPStatus.NOT_FOUND
|
||||
|
||||
return make_response(Credentials.to_json_array(propagation_credentials), 200)
|
||||
return make_response(Credentials.to_json_array(propagation_credentials), HTTPStatus.OK)
|
||||
|
||||
def post(self, collection=None):
|
||||
credentials = [Credentials.from_json(c) for c in request.json]
|
||||
|
||||
if collection == _configured_collection:
|
||||
self._credentials_repository.save_configured_credentials(credentials)
|
||||
elif collection == _stolen_collection:
|
||||
self._credentials_repository.save_stolen_credentials(credentials)
|
||||
elif collection is None:
|
||||
return {}, HTTPStatus.METHOD_NOT_ALLOWED
|
||||
else:
|
||||
return {}, HTTPStatus.NOT_FOUND
|
||||
|
||||
return {}, HTTPStatus.NO_CONTENT
|
||||
|
||||
def delete(self, collection=None):
|
||||
if collection == _configured_collection:
|
||||
self._credentials_repository.remove_configured_credentials()
|
||||
elif collection == _stolen_collection:
|
||||
self._credentials_repository.remove_stolen_credentials()
|
||||
elif collection is None:
|
||||
self._credentials_repository.remove_all_credentials()
|
||||
else:
|
||||
return {}, HTTPStatus.NOT_FOUND
|
||||
|
||||
return {}, HTTPStatus.NO_CONTENT
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
from common.credentials import Credentials, LMHash, NTHash, Password, Username
|
||||
|
||||
username = "m0nk3y_user"
|
||||
special_username = "m0nk3y.user"
|
||||
nt_hash = "C1C58F96CDF212B50837BC11A00BE47C"
|
||||
lm_hash = "299BD128C1101FD6299BD128C1101FD6"
|
||||
password_1 = "trytostealthis"
|
||||
password_2 = "password"
|
||||
password_3 = "12345678"
|
||||
|
||||
PROPAGATION_CREDENTIALS_1 = Credentials(
|
||||
identities=(Username(username),),
|
||||
secrets=(NTHash(nt_hash), LMHash(lm_hash), Password(password_1)),
|
||||
)
|
||||
PROPAGATION_CREDENTIALS_2 = Credentials(
|
||||
identities=(Username(username), Username(special_username)),
|
||||
secrets=(Password(password_1), Password(password_2), Password(password_3)),
|
||||
)
|
||||
PROPAGATION_CREDENTIALS_3 = Credentials(
|
||||
identities=(Username(username),),
|
||||
secrets=(Password(password_1),),
|
||||
)
|
||||
PROPAGATION_CREDENTIALS_4 = Credentials(
|
||||
identities=(Username(username),),
|
||||
secrets=(Password(password_2),),
|
||||
)
|
|
@ -3,8 +3,4 @@ from .mock_file_repository import MockFileRepository, FILE_CONTENTS, FILE_NAME
|
|||
from .open_error_file_repository import OpenErrorFileRepository
|
||||
from .in_memory_agent_configuration_repository import InMemoryAgentConfigurationRepository
|
||||
from .in_memory_simulation_configuration import InMemorySimulationRepository
|
||||
from .stub_propagation_credentials_repository import StubPropagationCredentialsRepository
|
||||
from .stub_propagation_credentials_repository import (
|
||||
PROPAGATION_CREDENTIALS_1,
|
||||
PROPAGATION_CREDENTIALS_2,
|
||||
)
|
||||
from .in_memory_credentials_repository import InMemoryCredentialsRepository
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
from typing import Sequence
|
||||
|
||||
from common.credentials import Credentials
|
||||
from monkey_island.cc.repository import ICredentialsRepository
|
||||
|
||||
|
||||
class InMemoryCredentialsRepository(ICredentialsRepository):
|
||||
def __init__(self):
|
||||
self._configured_credentials = []
|
||||
self._stolen_credentials = []
|
||||
|
||||
def get_configured_credentials(self) -> Sequence[Credentials]:
|
||||
return self._configured_credentials
|
||||
|
||||
def get_stolen_credentials(self) -> Sequence[Credentials]:
|
||||
return self._stolen_credentials
|
||||
|
||||
def get_all_credentials(self) -> Sequence[Credentials]:
|
||||
return [*self._configured_credentials, *self._stolen_credentials]
|
||||
|
||||
def save_configured_credentials(self, credentials: Sequence[Credentials]):
|
||||
self._configured_credentials.extend(credentials)
|
||||
|
||||
def save_stolen_credentials(self, credentials: Sequence[Credentials]):
|
||||
self._stolen_credentials.extend(credentials)
|
||||
|
||||
def remove_configured_credentials(self):
|
||||
self._configured_credentials = []
|
||||
|
||||
def remove_stolen_credentials(self):
|
||||
self._stolen_credentials = []
|
||||
|
||||
def remove_all_credentials(self):
|
||||
self.remove_configured_credentials()
|
||||
self.remove_stolen_credentials()
|
|
@ -1,47 +0,0 @@
|
|||
from typing import Sequence
|
||||
|
||||
from common.credentials import Credentials, LMHash, NTHash, Password, Username
|
||||
from monkey_island.cc.repository import ICredentialsRepository
|
||||
|
||||
fake_username = "m0nk3y_user"
|
||||
fake_special_username = "m0nk3y.user"
|
||||
fake_nt_hash = "C1C58F96CDF212B50837BC11A00BE47C"
|
||||
fake_lm_hash = "299BD128C1101FD6299BD128C1101FD6"
|
||||
fake_password_1 = "trytostealthis"
|
||||
fake_password_2 = "password"
|
||||
fake_password_3 = "12345678"
|
||||
PROPAGATION_CREDENTIALS_1 = Credentials(
|
||||
identities=(Username(fake_username),),
|
||||
secrets=(NTHash(fake_nt_hash), LMHash(fake_lm_hash), Password(fake_password_1)),
|
||||
)
|
||||
PROPAGATION_CREDENTIALS_2 = Credentials(
|
||||
identities=(Username(fake_username), Username(fake_special_username)),
|
||||
secrets=(Password(fake_password_1), Password(fake_password_2), Password(fake_password_3)),
|
||||
)
|
||||
|
||||
|
||||
class StubPropagationCredentialsRepository(ICredentialsRepository):
|
||||
def get_configured_credentials(self) -> Sequence[Credentials]:
|
||||
pass
|
||||
|
||||
def get_stolen_credentials(self) -> Sequence[Credentials]:
|
||||
pass
|
||||
|
||||
def get_all_credentials(self) -> Sequence[Credentials]:
|
||||
|
||||
return [PROPAGATION_CREDENTIALS_1, PROPAGATION_CREDENTIALS_2]
|
||||
|
||||
def save_configured_credentials(self, credentials: Sequence[Credentials]):
|
||||
pass
|
||||
|
||||
def save_stolen_credentials(self, credentials: Sequence[Credentials]):
|
||||
pass
|
||||
|
||||
def remove_configured_credentials(self):
|
||||
pass
|
||||
|
||||
def remove_stolen_credentials(self):
|
||||
pass
|
||||
|
||||
def remove_all_credentials(self):
|
||||
pass
|
|
@ -1,34 +1,148 @@
|
|||
import json
|
||||
from http import HTTPStatus
|
||||
from typing import Sequence
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import pytest
|
||||
from tests.common import StubDIContainer
|
||||
from tests.monkey_island import (
|
||||
from tests.data_for_tests.propagation_credentials import (
|
||||
PROPAGATION_CREDENTIALS_1,
|
||||
PROPAGATION_CREDENTIALS_2,
|
||||
StubPropagationCredentialsRepository,
|
||||
PROPAGATION_CREDENTIALS_3,
|
||||
PROPAGATION_CREDENTIALS_4,
|
||||
)
|
||||
from tests.unit_tests.monkey_island.conftest import get_url_for_resource
|
||||
from tests.monkey_island import InMemoryCredentialsRepository
|
||||
|
||||
from common.credentials import Credentials
|
||||
from monkey_island.cc.repository import ICredentialsRepository
|
||||
from monkey_island.cc.resources.propagation_credentials import PropagationCredentials
|
||||
from monkey_island.cc.resources import PropagationCredentials
|
||||
from monkey_island.cc.resources.propagation_credentials import (
|
||||
_configured_collection,
|
||||
_stolen_collection,
|
||||
)
|
||||
|
||||
ALL_CREDENTIALS_URL = PropagationCredentials.urls[0]
|
||||
CONFIGURED_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL, _configured_collection)
|
||||
STOLEN_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL, _stolen_collection)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def flask_client(build_flask_client):
|
||||
def credentials_repository():
|
||||
return InMemoryCredentialsRepository()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def flask_client(build_flask_client, credentials_repository):
|
||||
container = StubDIContainer()
|
||||
|
||||
container.register(ICredentialsRepository, StubPropagationCredentialsRepository)
|
||||
container.register_instance(ICredentialsRepository, credentials_repository)
|
||||
|
||||
with build_flask_client(container) as flask_client:
|
||||
yield flask_client
|
||||
|
||||
|
||||
def test_propagation_credentials_endpoint_get(flask_client):
|
||||
propagation_credentials_url = get_url_for_resource(PropagationCredentials)
|
||||
def test_propagation_credentials_endpoint_get(flask_client, credentials_repository):
|
||||
credentials_repository.save_configured_credentials(
|
||||
[PROPAGATION_CREDENTIALS_1, PROPAGATION_CREDENTIALS_3]
|
||||
)
|
||||
credentials_repository.save_stolen_credentials(
|
||||
[PROPAGATION_CREDENTIALS_2, PROPAGATION_CREDENTIALS_4]
|
||||
)
|
||||
|
||||
resp = flask_client.get(propagation_credentials_url)
|
||||
resp = flask_client.get(ALL_CREDENTIALS_URL)
|
||||
actual_propagation_credentials = Credentials.from_json_array(resp.text)
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.status_code == HTTPStatus.OK
|
||||
assert len(actual_propagation_credentials) == 4
|
||||
assert PROPAGATION_CREDENTIALS_1 in actual_propagation_credentials
|
||||
assert PROPAGATION_CREDENTIALS_2 in actual_propagation_credentials
|
||||
assert PROPAGATION_CREDENTIALS_3 in actual_propagation_credentials
|
||||
assert PROPAGATION_CREDENTIALS_4 in actual_propagation_credentials
|
||||
|
||||
|
||||
def pre_populate_repository(
|
||||
url: str, credentials_repository: ICredentialsRepository, credentials: Sequence[Credentials]
|
||||
):
|
||||
if "configured" in url:
|
||||
credentials_repository.save_configured_credentials(credentials)
|
||||
else:
|
||||
credentials_repository.save_stolen_credentials(credentials)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [CONFIGURED_CREDENTIALS_URL, STOLEN_CREDENTIALS_URL])
|
||||
def test_propagation_credentials_endpoint__get_stolen(flask_client, credentials_repository, url):
|
||||
pre_populate_repository(
|
||||
url, credentials_repository, [PROPAGATION_CREDENTIALS_1, PROPAGATION_CREDENTIALS_2]
|
||||
)
|
||||
|
||||
resp = flask_client.get(url)
|
||||
actual_propagation_credentials = Credentials.from_json_array(resp.text)
|
||||
|
||||
assert resp.status_code == HTTPStatus.OK
|
||||
assert len(actual_propagation_credentials) == 2
|
||||
assert actual_propagation_credentials[0] == PROPAGATION_CREDENTIALS_1
|
||||
assert actual_propagation_credentials[1] == PROPAGATION_CREDENTIALS_2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [CONFIGURED_CREDENTIALS_URL, STOLEN_CREDENTIALS_URL])
|
||||
def test_propagation_credentials_endpoint__post_stolen(flask_client, credentials_repository, url):
|
||||
pre_populate_repository(url, credentials_repository, [PROPAGATION_CREDENTIALS_1])
|
||||
|
||||
resp = flask_client.post(
|
||||
url,
|
||||
json=[
|
||||
Credentials.to_json(PROPAGATION_CREDENTIALS_2),
|
||||
Credentials.to_json(PROPAGATION_CREDENTIALS_3),
|
||||
],
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
resp = flask_client.get(url)
|
||||
retrieved_propagation_credentials = Credentials.from_json_array(resp.text)
|
||||
|
||||
assert resp.status_code == HTTPStatus.OK
|
||||
assert len(retrieved_propagation_credentials) == 3
|
||||
assert PROPAGATION_CREDENTIALS_1 in retrieved_propagation_credentials
|
||||
assert PROPAGATION_CREDENTIALS_2 in retrieved_propagation_credentials
|
||||
assert PROPAGATION_CREDENTIALS_3 in retrieved_propagation_credentials
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [CONFIGURED_CREDENTIALS_URL, STOLEN_CREDENTIALS_URL])
|
||||
def test_stolen_propagation_credentials_endpoint_delete(flask_client, credentials_repository, url):
|
||||
pre_populate_repository(
|
||||
url, credentials_repository, [PROPAGATION_CREDENTIALS_1, PROPAGATION_CREDENTIALS_2]
|
||||
)
|
||||
resp = flask_client.delete(url)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
resp = flask_client.get(url)
|
||||
assert len(json.loads(resp.text)) == 0
|
||||
|
||||
|
||||
def test_propagation_credentials_endpoint__propagation_credentials_post_not_allowed(flask_client):
|
||||
resp = flask_client.post(ALL_CREDENTIALS_URL, json=[])
|
||||
assert resp.status_code == HTTPStatus.METHOD_NOT_ALLOWED
|
||||
|
||||
|
||||
NON_EXISTENT_COLLECTION_URL = urljoin(ALL_CREDENTIALS_URL, "bogus-credentials")
|
||||
|
||||
|
||||
def test_propagation_credentials_endpoint__get_not_found(flask_client):
|
||||
resp = flask_client.get(NON_EXISTENT_COLLECTION_URL)
|
||||
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||
|
||||
|
||||
def test_propagation_credentials_endpoint__post_not_found(flask_client):
|
||||
resp = flask_client.post(
|
||||
NON_EXISTENT_COLLECTION_URL,
|
||||
json=[
|
||||
Credentials.to_json(PROPAGATION_CREDENTIALS_2),
|
||||
Credentials.to_json(PROPAGATION_CREDENTIALS_3),
|
||||
],
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||
|
||||
|
||||
def test_propagation_credentials_endpoint__delete_not_found(flask_client):
|
||||
resp = flask_client.delete(NON_EXISTENT_COLLECTION_URL)
|
||||
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||
|
|
Loading…
Reference in New Issue