diff --git a/ram/driver/admin.py b/ram/driver/admin.py index 57d2e02..fb55098 100644 --- a/ram/driver/admin.py +++ b/ram/driver/admin.py @@ -7,6 +7,14 @@ from driver.models import DriverConfiguration @admin.register(DriverConfiguration) class DriverConfigurationAdmin(SingletonModelAdmin): fieldsets = ( + ( + "General configuration", + { + "fields": ( + "enabled", + ) + }, + ), ( "Remote DCC-EX configuration", { diff --git a/ram/driver/migrations/0005_driverconfiguration_enabled.py b/ram/driver/migrations/0005_driverconfiguration_enabled.py new file mode 100644 index 0000000..a1a02e1 --- /dev/null +++ b/ram/driver/migrations/0005_driverconfiguration_enabled.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-07-11 18:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('driver', '0004_alter_driverconfiguration_remote_host'), + ] + + operations = [ + migrations.AddField( + model_name='driverconfiguration', + name='enabled', + field=models.BooleanField(default=False), + ), + ] diff --git a/ram/driver/models.py b/ram/driver/models.py index 18fd6eb..58c8918 100644 --- a/ram/driver/models.py +++ b/ram/driver/models.py @@ -1,10 +1,11 @@ from django.db import models from django.core.exceptions import ValidationError -from ipaddress import IPv4Address, IPv4Network +from ipaddress import IPv4Network from solo.models import SingletonModel class DriverConfiguration(SingletonModel): + enabled = models.BooleanField(default=False) remote_host = models.GenericIPAddressField( protocol="IPv4", default="192.168.4.1" ) diff --git a/ram/driver/views.py b/ram/driver/views.py index 7fa5a15..0b45e95 100644 --- a/ram/driver/views.py +++ b/ram/driver/views.py @@ -34,9 +34,22 @@ def addresschecker(f): return addresslookup +class IsEnabled(BasePermission): + def has_permission(self, request, view): + config = DriverConfiguration.get_solo() + + # if driver is disabled, block all connections + if not config.enabled: + raise Http404 + + return True + + class Firewall(BasePermission): def has_permission(self, request, view): config = DriverConfiguration.get_solo() + + # if network is not configured, accept only read ops if not config.network: return request.method in SAFE_METHODS @@ -61,7 +74,7 @@ class Test(APIView): """ parser_classes = [PlainTextParser] - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def get(self, request): response = Connector().passthrough("") @@ -76,7 +89,7 @@ class SendCommand(APIView): """ parser_classes = [PlainTextParser] - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def put(self, request): data = request.data @@ -101,7 +114,7 @@ class Function(APIView): Send "Function" commands to a valid DCC address """ - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def put(self, request, address): serializer = FunctionSerializer(data=request.data) @@ -118,7 +131,7 @@ class Cab(APIView): Send "Cab" commands to a valid DCC address """ - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def put(self, request, address): serializer = CabSerializer(data=request.data) @@ -134,7 +147,7 @@ class Infra(APIView): Send "Infra" commands to a valid DCC address """ - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def put(self, request): serializer = InfraSerializer(data=request.data) @@ -150,7 +163,7 @@ class Emergency(APIView): Send an "Emergency" stop, no matter the HTTP method used """ - permission_classes = [IsAuthenticated | Firewall] + permission_classes = [IsEnabled & IsAuthenticated | Firewall] def put(self, request): Connector().emergency()