From 9b4351dc78e5e20e53037ee975d3eca3c47b647b Mon Sep 17 00:00:00 2001
From: Benjamin Ledel <benjamin@schule-plus.com>
Date: Tue, 29 Apr 2025 14:40:44 +0200
Subject: [PATCH] * add local runner / fix auth problems of runner

---
 src/backend/settings.py               |  2 +-
 src/scheduler/authentication.py       | 15 +++++++++++++++
 src/scheduler/views.py                |  3 +++
 tools/local-runner/docker-compose.yml | 10 ++++++++++
 4 files changed, 29 insertions(+), 1 deletion(-)
 create mode 100644 src/scheduler/authentication.py
 create mode 100644 tools/local-runner/docker-compose.yml

diff --git a/src/backend/settings.py b/src/backend/settings.py
index f1cbad5..2fe93d2 100644
--- a/src/backend/settings.py
+++ b/src/backend/settings.py
@@ -226,7 +226,7 @@ CORS_ORIGIN_ALLOW_ALL = True
 
 AUTH_USER_MODEL = "users.CustomUser"
 REST_FRAMEWORK = {
-    "DEFAULT_AUTHENTICATION_CLASSES": (
+    "DEFAULT_AUTHENTICATION_CLASSES": (  
         "rest_framework_simplejwt.authentication.JWTAuthentication",
     ),
     'DEFAULT_THROTTLE_CLASSES': [
diff --git a/src/scheduler/authentication.py b/src/scheduler/authentication.py
new file mode 100644
index 0000000..0fca841
--- /dev/null
+++ b/src/scheduler/authentication.py
@@ -0,0 +1,15 @@
+from rest_framework import authentication, exceptions
+from .models import Runner
+
+class APIKeyAuthentication(authentication.BaseAuthentication):
+    """Authenticate requests using the runner API key."""
+    def authenticate(self, request):
+        auth = request.META.get('HTTP_AUTHORIZATION', '')
+        if not auth.lower().startswith('bearer '):
+            return None
+        token = auth.split()[1]
+        try:
+            runner = Runner.objects.get(api_key=token)
+        except Runner.DoesNotExist:
+            raise exceptions.AuthenticationFailed('Invalid API key')
+        return (runner, None)
\ No newline at end of file
diff --git a/src/scheduler/views.py b/src/scheduler/views.py
index 456898a..06e648b 100644
--- a/src/scheduler/views.py
+++ b/src/scheduler/views.py
@@ -11,10 +11,13 @@ from .serializers import (
     RunnerSerializer,
     AdminRunnerSerializer
 )
+from .authentication import APIKeyAuthentication
 
 
 class NextAnalyticsJobView(APIView):
     """GET /jobs/next/ → returns the next pending analytics job or 204."""
+    authentication_classes = [APIKeyAuthentication]
+    permission_classes = []
     def get(self, request):
         job = AnalyticsJob.objects.filter(status=AnalyticsJob.STATUS_PENDING).order_by('created_at').first()
         if not job:
diff --git a/tools/local-runner/docker-compose.yml b/tools/local-runner/docker-compose.yml
new file mode 100644
index 0000000..d4b46c8
--- /dev/null
+++ b/tools/local-runner/docker-compose.yml
@@ -0,0 +1,10 @@
+services:
+  runner:
+    image: registry.git.rwth-aachen.de/polaris/runner-v2/main:latest
+    environment:
+      - API_URL=http://host.docker.internal:8000
+      - API_KEY=4ef8d05a2b3340508d7b55a12a8630d6         # from your host env or .env file
+      - POLL_INTERVAL=5
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
+    restart: always
-- 
GitLab