Loading exercise/lib/exercise_manager.py +2 −2 Original line number Diff line number Diff line Loading @@ -222,8 +222,8 @@ def create_exercise( create_team_questionnaire_states(teams, definition) create_drive_permissions(teams, definition.files.filter(is_drive=True)) if exercise.technical: create_opensearch_exercise(teams, exercise.id) # if exercise.technical: # create_opensearch_exercise(teams, exercise.id) return exercise Loading running_exercise/schema/query.py +2 −2 Original line number Diff line number Diff line Loading @@ -268,8 +268,8 @@ class Query(graphene.ObjectType): if access.group < User.AuthGroup.INSTRUCTOR: type_filter.extend(ActionLog.INVISIBLE_LOG_TYPES) if not LogType.SANDBOX_LOG in log_filter.type_blacklist: sync_sandbox_logs(log_filter.team_ids) # if not LogType.SANDBOX_LOG in log_filter.type_blacklist: # sync_sandbox_logs(log_filter.team_ids) logs = ( ActionLog.objects.filter(team_id__in=log_filter.team_ids) Loading running_exercise/urls.py +5 −0 Original line number Diff line number Diff line Loading @@ -13,4 +13,9 @@ urlpatterns = [ views.UploadFileView.as_view(), name="upload-file", ), path( "sandbox/<int:team_id>/", views.CreateSandboxLogView.as_view(), name="create-sandbox-log", ), ] running_exercise/views.py +71 −0 Original line number Diff line number Diff line Loading @@ -5,16 +5,22 @@ from rest_framework import parsers from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView from dateutil import parser # type: ignore[import-untyped] from aai.access import ( exercise_access, ) from aai.decorators import protected from common_lib.exceptions import ApiException from common_lib.utils import ensure_exists from exercise.models import Exercise, Team from exercise_definition.models.models import Channel, InjectTypes from running_exercise.lib.file_handler import ( upload_file, get_uploaded_file, ) from running_exercise.lib.utils import create_action_log from running_exercise.models import SandboxLogDetails from user.models import User Loading Loading @@ -67,3 +73,68 @@ class UploadFileView(APIView): "detail": str(file_id), } ) class CreateSandboxLogView(APIView): parser_classes = [ parsers.MultiPartParser, parsers.JSONParser, parsers.FormParser, ] renderer_classes = [JSONRenderer] # TODO: @protected(User.AuthGroup.TRAINEE) def post(self, request, *args, **kwargs): """Create a sandbox log entry.""" team_id = self.kwargs.get("team_id", None) if team_id is None: raise ApiException("Invalid request, missing required parameters") team = ensure_exists(Team.objects.filter(id=team_id)) # TODO: ensure the user belongs to the team exercise_id = team.exercise_id exercise = ensure_exists(Exercise.objects.filter(id=exercise_id)) # the order by id should ensure that the same channel is always selected # TODO: add a channel for sandbox logs? channel = ensure_exists( Channel.objects.filter( definition_id=exercise.definition_id, type=InjectTypes.INFO ).order_by("id") ) timestamp_str = request.data.get("@timestamp", "") try: timestamp = parser.parse(timestamp_str) except (ValueError, TypeError): return Response( { "status": "error", "detail": "Invalid or missing @timestamp", }, status=400, ) create_action_log( team_id=team_id, channel_id=channel.id, details=SandboxLogDetails.objects.create( cmd=request.data.get("cmd", ""), cmd_source=request.data.get("cmd_source", ""), working_directory=request.data.get("working_directory", ""), username=request.data.get("username", ""), container=request.data.get("container", ""), ), # TODO: calculate in_exercise_time? in_exercise_time=0, # if team only has one user, use it? timestamp=timestamp, ) # TODO: subscription broadcast? return Response( { "status": "ok", "detail": "Sandbox log entry created", } ) Loading
exercise/lib/exercise_manager.py +2 −2 Original line number Diff line number Diff line Loading @@ -222,8 +222,8 @@ def create_exercise( create_team_questionnaire_states(teams, definition) create_drive_permissions(teams, definition.files.filter(is_drive=True)) if exercise.technical: create_opensearch_exercise(teams, exercise.id) # if exercise.technical: # create_opensearch_exercise(teams, exercise.id) return exercise Loading
running_exercise/schema/query.py +2 −2 Original line number Diff line number Diff line Loading @@ -268,8 +268,8 @@ class Query(graphene.ObjectType): if access.group < User.AuthGroup.INSTRUCTOR: type_filter.extend(ActionLog.INVISIBLE_LOG_TYPES) if not LogType.SANDBOX_LOG in log_filter.type_blacklist: sync_sandbox_logs(log_filter.team_ids) # if not LogType.SANDBOX_LOG in log_filter.type_blacklist: # sync_sandbox_logs(log_filter.team_ids) logs = ( ActionLog.objects.filter(team_id__in=log_filter.team_ids) Loading
running_exercise/urls.py +5 −0 Original line number Diff line number Diff line Loading @@ -13,4 +13,9 @@ urlpatterns = [ views.UploadFileView.as_view(), name="upload-file", ), path( "sandbox/<int:team_id>/", views.CreateSandboxLogView.as_view(), name="create-sandbox-log", ), ]
running_exercise/views.py +71 −0 Original line number Diff line number Diff line Loading @@ -5,16 +5,22 @@ from rest_framework import parsers from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView from dateutil import parser # type: ignore[import-untyped] from aai.access import ( exercise_access, ) from aai.decorators import protected from common_lib.exceptions import ApiException from common_lib.utils import ensure_exists from exercise.models import Exercise, Team from exercise_definition.models.models import Channel, InjectTypes from running_exercise.lib.file_handler import ( upload_file, get_uploaded_file, ) from running_exercise.lib.utils import create_action_log from running_exercise.models import SandboxLogDetails from user.models import User Loading Loading @@ -67,3 +73,68 @@ class UploadFileView(APIView): "detail": str(file_id), } ) class CreateSandboxLogView(APIView): parser_classes = [ parsers.MultiPartParser, parsers.JSONParser, parsers.FormParser, ] renderer_classes = [JSONRenderer] # TODO: @protected(User.AuthGroup.TRAINEE) def post(self, request, *args, **kwargs): """Create a sandbox log entry.""" team_id = self.kwargs.get("team_id", None) if team_id is None: raise ApiException("Invalid request, missing required parameters") team = ensure_exists(Team.objects.filter(id=team_id)) # TODO: ensure the user belongs to the team exercise_id = team.exercise_id exercise = ensure_exists(Exercise.objects.filter(id=exercise_id)) # the order by id should ensure that the same channel is always selected # TODO: add a channel for sandbox logs? channel = ensure_exists( Channel.objects.filter( definition_id=exercise.definition_id, type=InjectTypes.INFO ).order_by("id") ) timestamp_str = request.data.get("@timestamp", "") try: timestamp = parser.parse(timestamp_str) except (ValueError, TypeError): return Response( { "status": "error", "detail": "Invalid or missing @timestamp", }, status=400, ) create_action_log( team_id=team_id, channel_id=channel.id, details=SandboxLogDetails.objects.create( cmd=request.data.get("cmd", ""), cmd_source=request.data.get("cmd_source", ""), working_directory=request.data.get("working_directory", ""), username=request.data.get("username", ""), container=request.data.get("container", ""), ), # TODO: calculate in_exercise_time? in_exercise_time=0, # if team only has one user, use it? timestamp=timestamp, ) # TODO: subscription broadcast? return Response( { "status": "ok", "detail": "Sandbox log entry created", } )