mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Add firewall and auth on driver API
This commit is contained in:
@@ -3,4 +3,27 @@ from solo.admin import SingletonModelAdmin
|
|||||||
|
|
||||||
from driver.models import DriverConfiguration
|
from driver.models import DriverConfiguration
|
||||||
|
|
||||||
admin.site.register(DriverConfiguration, SingletonModelAdmin)
|
|
||||||
|
@admin.register(DriverConfiguration)
|
||||||
|
class DriverConfigurationAdmin(SingletonModelAdmin):
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
"Remote DCC-EX configuration",
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"remote_host",
|
||||||
|
"remote_port",
|
||||||
|
"timeout",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Firewall setting",
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"network",
|
||||||
|
"subnet_mask",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.0.3 on 2022-04-10 15:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('driver', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='network',
|
||||||
|
field=models.GenericIPAddressField(default='192.168.4.0', protocol='IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='subnet_mask',
|
||||||
|
field=models.GenericIPAddressField(default='255.255.255.0', protocol='IPv4'),
|
||||||
|
),
|
||||||
|
]
|
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 4.0.3 on 2022-04-10 16:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('driver', '0002_driverconfiguration_network_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='network',
|
||||||
|
field=models.GenericIPAddressField(blank=True, default='192.168.4.0', null=True, protocol='IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='remote_host',
|
||||||
|
field=models.GenericIPAddressField(blank=True, default='192.168.4.1', null=True, protocol='IPv4'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='subnet_mask',
|
||||||
|
field=models.GenericIPAddressField(blank=True, default='255.255.255.0', null=True, protocol='IPv4'),
|
||||||
|
),
|
||||||
|
]
|
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.0.3 on 2022-04-10 16:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('driver', '0003_alter_driverconfiguration_network_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='driverconfiguration',
|
||||||
|
name='remote_host',
|
||||||
|
field=models.GenericIPAddressField(default='192.168.4.1', protocol='IPv4'),
|
||||||
|
),
|
||||||
|
]
|
@@ -1,16 +1,41 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from ipaddress import IPv4Address, IPv4Network
|
||||||
from solo.models import SingletonModel
|
from solo.models import SingletonModel
|
||||||
|
|
||||||
|
|
||||||
class DriverConfiguration(SingletonModel):
|
class DriverConfiguration(SingletonModel):
|
||||||
remote_host = models.GenericIPAddressField(
|
remote_host = models.GenericIPAddressField(
|
||||||
protocol="IPv4", default="192.168.4.1"
|
protocol="IPv4",
|
||||||
|
default="192.168.4.1"
|
||||||
)
|
)
|
||||||
remote_port = models.SmallIntegerField(default=2560)
|
remote_port = models.SmallIntegerField(default=2560)
|
||||||
timeout = models.SmallIntegerField(default=250)
|
timeout = models.SmallIntegerField(default=250)
|
||||||
|
|
||||||
|
network = models.GenericIPAddressField(
|
||||||
|
protocol="IPv4",
|
||||||
|
default="192.168.4.0",
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
subnet_mask = models.GenericIPAddressField(
|
||||||
|
protocol="IPv4",
|
||||||
|
default="255.255.255.0",
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Configuration"
|
return "Configuration"
|
||||||
|
|
||||||
|
def clean(self, *args, **kwargs):
|
||||||
|
if self.network:
|
||||||
|
try:
|
||||||
|
IPv4Network(
|
||||||
|
"{0}/{1}".format(self.network, self.subnet_mask))
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValidationError(e)
|
||||||
|
super().clean(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Configuration"
|
verbose_name = "Configuration"
|
||||||
|
@@ -1,10 +1,17 @@
|
|||||||
|
from ipaddress import IPv4Address, IPv4Network
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from rest_framework import status, serializers
|
from rest_framework import status, serializers
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.permissions import (
|
||||||
|
IsAuthenticated,
|
||||||
|
BasePermission,
|
||||||
|
SAFE_METHODS
|
||||||
|
)
|
||||||
|
|
||||||
from dcc.parsers import PlainTextParser
|
from dcc.parsers import PlainTextParser
|
||||||
|
from driver.models import DriverConfiguration
|
||||||
from driver.connector import Connector
|
from driver.connector import Connector
|
||||||
from driver.serializers import (
|
from driver.serializers import (
|
||||||
FunctionSerializer,
|
FunctionSerializer,
|
||||||
@@ -27,12 +34,35 @@ def addresschecker(f):
|
|||||||
return addresslookup
|
return addresslookup
|
||||||
|
|
||||||
|
|
||||||
|
class Firewall(BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
config = DriverConfiguration.get_solo()
|
||||||
|
if not config.network:
|
||||||
|
return request.method in SAFE_METHODS
|
||||||
|
|
||||||
|
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||||
|
if x_forwarded_for:
|
||||||
|
ip = IPv4Address(x_forwarded_for.split(',')[0])
|
||||||
|
else:
|
||||||
|
ip = IPv4Address(request.META.get("REMOTE_ADDR"))
|
||||||
|
|
||||||
|
network = IPv4Network("{0}/{1}".format(
|
||||||
|
config.network,
|
||||||
|
config.subnet_mask
|
||||||
|
))
|
||||||
|
|
||||||
|
# accept IP configured is settings or localhost
|
||||||
|
if ip in network or ip in IPv4Network("127.0.0.0/8"):
|
||||||
|
return request.method in SAFE_METHODS
|
||||||
|
|
||||||
|
|
||||||
class Test(APIView):
|
class Test(APIView):
|
||||||
"""
|
"""
|
||||||
Send a test <s> command
|
Send a test <s> command
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parser_classes = [PlainTextParser]
|
parser_classes = [PlainTextParser]
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
response = Connector().passthrough("<s>")
|
response = Connector().passthrough("<s>")
|
||||||
@@ -47,6 +77,7 @@ class SendCommand(APIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
parser_classes = [PlainTextParser]
|
parser_classes = [PlainTextParser]
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
data = request.data
|
data = request.data
|
||||||
@@ -70,6 +101,7 @@ class Function(APIView):
|
|||||||
"""
|
"""
|
||||||
Send "Function" commands to a valid DCC address
|
Send "Function" commands to a valid DCC address
|
||||||
"""
|
"""
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def put(self, request, address):
|
def put(self, request, address):
|
||||||
serializer = FunctionSerializer(data=request.data)
|
serializer = FunctionSerializer(data=request.data)
|
||||||
@@ -85,6 +117,7 @@ class Cab(APIView):
|
|||||||
"""
|
"""
|
||||||
Send "Cab" commands to a valid DCC address
|
Send "Cab" commands to a valid DCC address
|
||||||
"""
|
"""
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def put(self, request, address):
|
def put(self, request, address):
|
||||||
serializer = CabSerializer(data=request.data)
|
serializer = CabSerializer(data=request.data)
|
||||||
@@ -99,6 +132,7 @@ class Infra(APIView):
|
|||||||
"""
|
"""
|
||||||
Send "Infra" commands to a valid DCC address
|
Send "Infra" commands to a valid DCC address
|
||||||
"""
|
"""
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
serializer = InfraSerializer(data=request.data)
|
serializer = InfraSerializer(data=request.data)
|
||||||
@@ -113,6 +147,7 @@ class Emergency(APIView):
|
|||||||
"""
|
"""
|
||||||
Send an "Emergency" stop, no matter the HTTP method used
|
Send an "Emergency" stop, no matter the HTTP method used
|
||||||
"""
|
"""
|
||||||
|
permission_classes = [IsAuthenticated | Firewall]
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
Connector().emergency()
|
Connector().emergency()
|
||||||
|
@@ -1,3 +1,7 @@
|
|||||||
.card > a > img {
|
.card > a > img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#footer > p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
@@ -5,8 +5,12 @@
|
|||||||
<p class="float-end mb-1">
|
<p class="float-end mb-1">
|
||||||
<a href="#">Back to top</a>
|
<a href="#">Back to top</a>
|
||||||
</p>
|
</p>
|
||||||
<p class="mb-1">© {% now "Y" %} {{ site_conf.footer | markdown | safe }}</p>
|
<div id="footer" class="mb-1">
|
||||||
<p class="mb-0">{{ site_conf.footer_extended | markdown | safe }}</p>
|
<p>© {% now "Y" %}</p> {{ site_conf.footer | markdown | safe }}
|
||||||
|
</div>
|
||||||
|
<div id="footer_extended" class="mb-0">
|
||||||
|
{{ site_conf.footer_extended | markdown | safe }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if site_conf.show_version %}
|
{% if site_conf.show_version %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
Reference in New Issue
Block a user