Skip to content

Commit 482fb67

Browse files
committed
Merge branch '2157-roles-flask' into develop
Issue #2157 PR #3064
2 parents c79e569 + 5aea78b commit 482fb67

24 files changed

+108
-33
lines changed

monkey/common/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
from .agent_signals import AgentSignals
1010
from .agent_heartbeat import AgentHeartbeat
1111
from .hard_coded_manifests import HARD_CODED_EXPLOITER_MANIFESTS
12+
from .account_role import AccountRole

monkey/common/account_role.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from enum import Enum, auto
2+
3+
4+
class AccountRole(Enum):
5+
"""
6+
An Enum representing roles.
7+
This Enum represents roles that an account can have.
8+
"""
9+
10+
ISLAND_INTERFACE = auto()
11+
AGENT = auto()

monkey/monkey_island/cc/app.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from flask import Flask, Response, send_from_directory
66
from flask.sessions import SecureCookieSessionInterface
77
from flask_mongoengine import MongoEngine
8-
from flask_security import ConfirmRegisterForm, MongoEngineUserDatastore, Security
8+
from flask_security import ConfirmRegisterForm, MongoEngineUserDatastore, Security, UserDatastore
99
from werkzeug.exceptions import NotFound
1010
from wtforms import StringField, ValidationError
1111

12-
from common import DIContainer
12+
from common import AccountRole, DIContainer
1313
from monkey_island.cc.flask_utils import FlaskDIWrapper
1414
from monkey_island.cc.models import Role, User
1515
from monkey_island.cc.resources import (
@@ -91,16 +91,16 @@ def setup_authentication(app, data_dir):
9191

9292
# The database object needs to be created after we configure the flask application
9393
db = MongoEngine(app)
94-
9594
user_datastore = MongoEngineUserDatastore(db, User, Role)
9695

96+
_create_roles(user_datastore)
97+
9798
# Only one user can be registered in the Island, so we need a custom validator
9899
def validate_no_user_exists_already(_, field):
99100
if user_datastore.find_user():
100101
raise ValidationError("A user already exists. Only a single user can be registered.")
101102

102103
class CustomConfirmRegisterForm(ConfirmRegisterForm):
103-
104104
# We don't use the email, but the field is required by ConfirmRegisterForm.
105105
# Email validators need to be overriden, otherwise an error about invalid email is raised.
106106
# Added custom validator to the email field because we have to override
@@ -109,6 +109,11 @@ class CustomConfirmRegisterForm(ConfirmRegisterForm):
109109
"Email", default="dummy@dummy.com", validators=[validate_no_user_exists_already]
110110
)
111111

112+
def to_dict(self, only_user):
113+
registration_dict = super().to_dict(only_user)
114+
registration_dict.update({"roles": [AccountRole.ISLAND_INTERFACE.name]})
115+
return registration_dict
116+
112117
app.security = Security(
113118
app,
114119
user_datastore,
@@ -121,6 +126,11 @@ class CustomConfirmRegisterForm(ConfirmRegisterForm):
121126
app.session_interface = disable_session_cookies()
122127

123128

129+
def _create_roles(user_datastore: UserDatastore):
130+
user_datastore.find_or_create_role(name=AccountRole.ISLAND_INTERFACE.name)
131+
user_datastore.find_or_create_role(name=AccountRole.AGENT.name)
132+
133+
124134
def init_app_config(app, mongo_url, data_dir: Path):
125135
app.config["MONGO_URI"] = mongo_url
126136
app.config["MONGODB_SETTINGS"] = [
@@ -207,7 +217,11 @@ def init_rpc_endpoints(api: FlaskDIWrapper):
207217
api.add_resource(TerminateAllAgents)
208218

209219

210-
def init_app(mongo_url: str, container: DIContainer, data_dir: Path):
220+
def init_app(
221+
mongo_url: str,
222+
container: DIContainer,
223+
data_dir: Path,
224+
):
211225
"""
212226
Simple docstirng for init_app
213227

monkey/monkey_island/cc/models/role.py

-2
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,3 @@
66

77
class Role(Document, RoleMixin):
88
name = StringField(max_length=80, unique=True)
9-
description = StringField(max_length=255)
10-
permissions = StringField(max_length=255)

monkey/monkey_island/cc/resources/agent_logs.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
from http import HTTPStatus
33

44
from flask import request
5-
from flask_security import auth_token_required
5+
from flask_security import auth_token_required, roles_required
66

7+
from common import AccountRole
78
from common.types import AgentID
89
from monkey_island.cc.flask_utils import AbstractResource
910
from monkey_island.cc.repositories import IAgentLogRepository, UnknownRecordError
@@ -18,6 +19,7 @@ def __init__(self, agent_log_repository: IAgentLogRepository):
1819
self._agent_log_repository = agent_log_repository
1920

2021
@auth_token_required
22+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
2123
def get(self, agent_id: AgentID):
2224
try:
2325
log_contents = self._agent_log_repository.get_agent_log(agent_id)

monkey/monkey_island/cc/resources/agent_signals/terminate_all_agents.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from json import JSONDecodeError
44

55
from flask import request
6-
from flask_security import auth_token_required
6+
from flask_security import auth_token_required, roles_required
77

8+
from common import AccountRole
89
from monkey_island.cc.event_queue import IIslandEventQueue, IslandEventTopic
910
from monkey_island.cc.flask_utils import AbstractResource
1011
from monkey_island.cc.models import TerminateAllAgents as TerminateAllAgentsObject
@@ -22,6 +23,7 @@ def __init__(
2223
self._island_event_queue = island_event_queue
2324

2425
@auth_token_required
26+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
2527
def post(self):
2628
try:
2729
terminate_all_agents = TerminateAllAgentsObject(**request.json)

monkey/monkey_island/cc/resources/agents.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from http import HTTPStatus
44

55
from flask import make_response, request
6-
from flask_security import auth_token_required
6+
from flask_security import auth_token_required, roles_required
77

8-
from common import AgentRegistrationData
8+
from common import AccountRole, AgentRegistrationData
99
from monkey_island.cc.event_queue import IIslandEventQueue, IslandEventTopic
1010
from monkey_island.cc.flask_utils import AbstractResource
1111
from monkey_island.cc.repositories import IAgentRepository
@@ -21,6 +21,7 @@ def __init__(self, island_event_queue: IIslandEventQueue, agent_repository: IAge
2121
self._agent_repository = agent_repository
2222

2323
@auth_token_required
24+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
2425
def get(self):
2526
return self._agent_repository.get_agents(), HTTPStatus.OK
2627

monkey/monkey_island/cc/resources/clear_simulation_data.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from http import HTTPStatus
22

33
from flask import make_response
4-
from flask_security import auth_token_required
4+
from flask_security import auth_token_required, roles_required
55

6+
from common import AccountRole
67
from monkey_island.cc.event_queue import IIslandEventQueue, IslandEventTopic
78
from monkey_island.cc.flask_utils import AbstractResource
89

@@ -14,6 +15,7 @@ def __init__(self, island_event_queue: IIslandEventQueue):
1415
self._island_event_queue = island_event_queue
1516

1617
@auth_token_required
18+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1719
def post(self):
1820
"""
1921
Clear all data collected during the simulation

monkey/monkey_island/cc/resources/exploitations/monkey_exploitation.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from dataclasses import asdict
22

3-
from flask_security import auth_token_required
3+
from flask_security import auth_token_required, roles_required
44

5+
from common import AccountRole
56
from monkey_island.cc.flask_utils import AbstractResource
67
from monkey_island.cc.repositories import (
78
IAgentEventRepository,
@@ -27,6 +28,7 @@ def __init__(
2728
self._agent_plugin_repository = agent_plugin_repository
2829

2930
@auth_token_required
31+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
3032
def get(self):
3133
monkey_exploitations = [
3234
asdict(exploitation)

monkey/monkey_island/cc/resources/island_log.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import logging
22
from pathlib import Path
33

4-
from flask_security import auth_token_required
4+
from flask_security import auth_token_required, roles_required
55

6+
from common import AccountRole
67
from common.utils.file_utils import get_text_file_contents
78
from monkey_island.cc.flask_utils import AbstractResource
89

@@ -16,6 +17,7 @@ def __init__(self, island_log_file_path: Path):
1617
self._island_log_file_path = island_log_file_path
1718

1819
@auth_token_required
20+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1921
def get(self):
2022
try:
2123
return get_text_file_contents(self._island_log_file_path)

monkey/monkey_island/cc/resources/island_mode.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from http import HTTPStatus
44

55
from flask import request
6-
from flask_security import auth_token_required
6+
from flask_security import auth_token_required, roles_required
77

8+
from common import AccountRole
89
from monkey_island.cc.event_queue import IIslandEventQueue, IslandEventTopic
910
from monkey_island.cc.flask_utils import AbstractResource
1011
from monkey_island.cc.models import IslandMode as IslandModeEnum
@@ -25,6 +26,7 @@ def __init__(
2526
self._simulation_repository = simulation_repository
2627

2728
@auth_token_required
29+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
2830
def put(self):
2931
try:
3032
mode = IslandModeEnum(request.json)
@@ -36,6 +38,7 @@ def put(self):
3638
return {}, HTTPStatus.UNPROCESSABLE_ENTITY
3739

3840
@auth_token_required
41+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
3942
def get(self):
4043
island_mode = self._simulation_repository.get_mode()
4144
return island_mode.value, HTTPStatus.OK

monkey/monkey_island/cc/resources/local_run.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
22

33
from flask import jsonify, make_response, request
4-
from flask_security import auth_token_required
4+
from flask_security import auth_token_required, roles_required
55

6+
from common import AccountRole
67
from monkey_island.cc.flask_utils import AbstractResource
78
from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService
89

@@ -15,6 +16,7 @@ def __init__(self, local_monkey_run_service: LocalMonkeyRunService):
1516

1617
# API Spec: This should be an RPC-style endpoint
1718
@auth_token_required
19+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1820
def post(self):
1921
body = json.loads(request.data)
2022
if body.get("action") == "run":

monkey/monkey_island/cc/resources/machines.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from http import HTTPStatus
22

3-
from flask_security import auth_token_required
3+
from flask_security import auth_token_required, roles_required
44

5+
from common import AccountRole
56
from monkey_island.cc.flask_utils import AbstractResource
67
from monkey_island.cc.repositories import IMachineRepository
78

@@ -13,5 +14,6 @@ def __init__(self, machine_repository: IMachineRepository):
1314
self._machine_repository = machine_repository
1415

1516
@auth_token_required
17+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1618
def get(self):
1719
return self._machine_repository.get_machines(), HTTPStatus.OK

monkey/monkey_island/cc/resources/nodes.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from http import HTTPStatus
22

3-
from flask_security import auth_token_required
3+
from flask_security import auth_token_required, roles_required
44

5+
from common import AccountRole
56
from monkey_island.cc.flask_utils import AbstractResource
67
from monkey_island.cc.repositories import INodeRepository
78

@@ -13,5 +14,6 @@ def __init__(self, node_repository: INodeRepository):
1314
self._node_repository = node_repository
1415

1516
@auth_token_required
17+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1618
def get(self):
1719
return self._node_repository.get_nodes(), HTTPStatus.OK

monkey/monkey_island/cc/resources/ransomware_report.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from flask import jsonify
2-
from flask_security import auth_token_required
2+
from flask_security import auth_token_required, roles_required
33

4+
from common import AccountRole
45
from monkey_island.cc.flask_utils import AbstractResource
56
from monkey_island.cc.repositories import (
67
IAgentEventRepository,
@@ -24,6 +25,7 @@ def __init__(
2425
self._agent_plugin_repository = agent_plugin_repository
2526

2627
@auth_token_required
28+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
2729
def get(self):
2830
return jsonify(
2931
{

monkey/monkey_island/cc/resources/remote_run.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
from botocore.exceptions import ClientError, NoCredentialsError
55
from flask import jsonify, make_response, request
6-
from flask_security import auth_token_required
6+
from flask_security import auth_token_required, roles_required
77

8+
from common import AccountRole
89
from monkey_island.cc.flask_utils import AbstractResource
910
from monkey_island.cc.services import AWSService
1011
from monkey_island.cc.services.aws import AWSCommandResults
@@ -29,6 +30,7 @@ def __init__(self, aws_service: AWSService):
2930
self._aws_service = aws_service
3031

3132
@auth_token_required
33+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
3234
def get(self):
3335
action = request.args.get("action")
3436
if action == "list_aws":
@@ -50,6 +52,7 @@ def get(self):
5052
return {}
5153

5254
@auth_token_required
55+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
5356
def post(self):
5457
body = json.loads(request.data)
5558
if body.get("type") == "aws":

monkey/monkey_island/cc/resources/report_generation_status.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from flask import jsonify
2-
from flask_security import auth_token_required
2+
from flask_security import auth_token_required, roles_required
33

4+
from common import AccountRole
45
from monkey_island.cc.flask_utils import AbstractResource
56
from monkey_island.cc.repositories import IAgentRepository
67
from monkey_island.cc.services.infection_lifecycle import is_report_done
@@ -13,6 +14,7 @@ def __init__(self, agent_repository: IAgentRepository):
1314
self._agent_repository = agent_repository
1415

1516
@auth_token_required
17+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1618
def get(self):
1719
return self.report_generation_status()
1820

monkey/monkey_island/cc/resources/reset_agent_configuration.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from http import HTTPStatus
22

33
from flask import make_response
4-
from flask_security import auth_token_required
4+
from flask_security import auth_token_required, roles_required
55

6+
from common import AccountRole
67
from monkey_island.cc.event_queue import IIslandEventQueue, IslandEventTopic
78
from monkey_island.cc.flask_utils import AbstractResource
89

@@ -14,6 +15,7 @@ def __init__(self, island_event_queue: IIslandEventQueue):
1415
self._island_event_queue = island_event_queue
1516

1617
@auth_token_required
18+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1719
def post(self):
1820
"""
1921
Reset the agent configuration to its default values
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
from flask_security import auth_token_required
1+
from flask_security import auth_token_required, roles_required
22

3+
from common import AccountRole
34
from monkey_island.cc.flask_utils import AbstractResource
45
from monkey_island.cc.services.reporting.report import ReportService
56

@@ -8,6 +9,7 @@ class SecurityReport(AbstractResource):
89
urls = ["/api/report/security"]
910

1011
@auth_token_required
12+
@roles_required(AccountRole.ISLAND_INTERFACE.name)
1113
def get(self):
1214
ReportService.update_report()
1315
return ReportService.get_report()

0 commit comments

Comments
 (0)