From 173f110fb248911c7e5748e585e67c0fbbc04b15 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Wed, 24 Jul 2024 16:38:06 +0100 Subject: [PATCH] #2769: initial commit of user account actions --- pyproject.toml | 2 +- src/primaite/game/agent/actions.py | 36 ++++++++++++++++++ .../simulator/network/hardware/base.py | 9 +++++ tests/conftest.py | 3 ++ .../test_remote_user_account_actions.py | 38 +++++++++++++++++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/integration_tests/game_layer/actions/user_account_actions/test_remote_user_account_actions.py diff --git a/pyproject.toml b/pyproject.toml index 9e919604..f63ee4c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ license-files = ["LICENSE"] [project.optional-dependencies] rl = [ - "ray[rllib] >= 2.20.0, < 3", + "ray[rllib] >= 2.32.0, < 3", "tensorflow==2.12.0", "stable-baselines3[extra]==2.1.0", "sb3-contrib==2.1.0", diff --git a/src/primaite/game/agent/actions.py b/src/primaite/game/agent/actions.py index 9a5fedc9..bf8e4323 100644 --- a/src/primaite/game/agent/actions.py +++ b/src/primaite/game/agent/actions.py @@ -1071,6 +1071,39 @@ class NodeNetworkServiceReconAction(AbstractAction): ] +class NodeAccountsChangePasswordAction(AbstractAction): + """Action which changes the password for a user.""" + + def __init__(self, manager: "ActionManager", **kwargs) -> None: + super().__init__(manager=manager) + + def form_request(self) -> RequestFormat: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + pass + + +class NodeSessionsRemoteLoginAction(AbstractAction): + """Action which performs a remote session login.""" + + def __init__(self, manager: "ActionManager", **kwargs) -> None: + pass + + def form_request(self, node_id: str) -> RequestFormat: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + return ["network", "node", node_id, "remote_logon"] + + +class NodeSessionsRemoteLogoutAction(AbstractAction): + """Action which performs a remote session logout.""" + + def __init__(self, manager: "ActionManager", **kwargs) -> None: + pass + + def form_request(self, node_id: str) -> RequestFormat: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + return ["network", "node", node_id, "remote_logoff"] + + class ActionManager: """Class which manages the action space for an agent.""" @@ -1122,6 +1155,9 @@ class ActionManager: "CONFIGURE_DATABASE_CLIENT": ConfigureDatabaseClientAction, "CONFIGURE_RANSOMWARE_SCRIPT": ConfigureRansomwareScriptAction, "CONFIGURE_DOSBOT": ConfigureDoSBotAction, + "NODE_ACCOUNTS_CHANGEPASSWORD": NodeAccountsChangePasswordAction, + "NODE_SESSIONS_REMOTE_LOGIN": NodeSessionsRemoteLoginAction, + "NODE_SESSIONS_REMOTE_LOGOUT": NodeSessionsRemoteLogoutAction, } """Dictionary which maps action type strings to the corresponding action class.""" diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 15c44821..831a8539 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1072,6 +1072,15 @@ class Node(SimComponent): "logoff", RequestType(func=lambda request, context: RequestResponse.from_bool(False), validator=_node_is_on) ) # TODO implement logoff request + rm.add_request( + "remote_logon", + RequestType(func=lambda request, context: RequestResponse.from_bool(False), validator=_node_is_on), + ) # TODO implement remote_logon request + rm.add_request( + "remote_logoff", + RequestType(func=lambda request, context: RequestResponse.from_bool(False), validator=_node_is_on), + ) # TODO implement remote_logoff request + self._os_request_manager = RequestManager() self._os_request_manager.add_request( "scan", diff --git a/tests/conftest.py b/tests/conftest.py index 54519e2b..e1ce41b0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -458,6 +458,9 @@ def game_and_agent(): {"type": "HOST_NIC_DISABLE"}, {"type": "NETWORK_PORT_ENABLE"}, {"type": "NETWORK_PORT_DISABLE"}, + {"type": "NODE_ACCOUNTS_CHANGEPASSWORD"}, + {"type": "NODE_SESSIONS_REMOTE_LOGIN"}, + {"type": "NODE_SESSIONS_REMOTE_LOGOUT"}, ] action_space = ActionManager( diff --git a/tests/integration_tests/game_layer/actions/user_account_actions/test_remote_user_account_actions.py b/tests/integration_tests/game_layer/actions/user_account_actions/test_remote_user_account_actions.py new file mode 100644 index 00000000..807715bb --- /dev/null +++ b/tests/integration_tests/game_layer/actions/user_account_actions/test_remote_user_account_actions.py @@ -0,0 +1,38 @@ +# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK + + +def test_remote_logon(game_and_agent): + """Test that the remote session login action works.""" + game, agent = game_and_agent + + action = ( + "NODE_SESSIONS_REMOTE_LOGIN", + {"node_id": 0}, + ) + agent.store_action(action) + game.step() + + # TODO Assert that there is a logged in user + + +def test_remote_logoff(game_and_agent): + """Test that the remote session logout action works.""" + game, agent = game_and_agent + + action = ( + "NODE_SESSIONS_REMOTE_LOGIN", + {"node_id": 0}, + ) + agent.store_action(action) + game.step() + + # TODO Assert that there is a logged in user + + action = ( + "NODE_SESSIONS_REMOTE_LOGOUT", + {"node_id": 0}, + ) + agent.store_action(action) + game.step() + + # TODO Assert the user has logged out