Make HSLan always authenticated for GET

pull/1/head
palid 2023-07-17 21:35:45 +02:00
parent 30c3c3eb7a
commit 154e1079da
Signed by: palid
SSH Key Fingerprint: SHA256:Mus3wCd2x6nxtARI0DpWGT7lIWbNy3R90BVDg0j35PI
3 changed files with 93 additions and 24 deletions

View File

@ -34,7 +34,7 @@ DEBUG = not PROD
ALLOWED_HOSTS = env(
"ALLOWED_HOSTS",
"devinventory,inventory.waw.hackerspace.pl,inventory.hackerspace.pl,i,inventory"
+ (",127.0.0.1" if not PROD else ""),
+ (",127.0.0.1,locahost,*" if not PROD else ""),
).split(",")
LOGIN_REDIRECT_URL = "/admin/"
@ -102,7 +102,7 @@ DATABASES = {
"NAME": env("DB_NAME", "postgres"),
"USER": env("DB_USER", "postgres"),
"PASSWORD": env("DB_PASSWORD", None),
"HOST": env("DB_HOST", "db"),
"HOST": env("DB_HOST", "127.0.0.1"),
"PORT": env("DB_PORT", 5432),
}
}
@ -171,12 +171,10 @@ REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
"rest_framework.permissions.IsAuthenticatedOrReadOnly",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.BasicAuthentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
"storage.authentication.LanAuthentication",
],
}
@ -188,3 +186,6 @@ SOCIAL_AUTH_JSONFIELD_ENABLED = True
LABEL_API = env("LABEL_API", "http://label.waw.hackerspace.pl:4567")
LOGIN_URL = "/admin/login/"
LAN_ALLOWED_ADDRES_SPACE = "10.8.0.0/16"
LAN_ALLOWED_HEADER = "X-LAN-ALLOWED"
PROXY_TRUSTED_IPS = ["172.21.37.1"]

View File

@ -1,13 +1,11 @@
from rest_framework import viewsets, generics, filters
from rest_framework import viewsets, filters
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from storage.authentication import LanAuthentication
from storage.models import Item, Label
from storage.serializers import ItemSerializer, LabelSerializer
from django.http import Http404
from django.shortcuts import get_object_or_404
from storage.views import apply_smart_search
@ -40,7 +38,10 @@ class LabelViewSet(viewsets.ModelViewSet):
queryset = Label.objects.all()
serializer_class = LabelSerializer
@action(detail=True, methods=["post"], permission_classes=[AllowAny])
@action(
detail=True,
methods=["post"],
)
def print(self, request, pk):
return api_print(request.query_params.get("quantity", 1), self.get_object())
@ -77,40 +78,35 @@ class ItemViewSet(viewsets.ModelViewSet):
except Label.DoesNotExist:
raise Http404()
@action(detail=True, methods=["post"], permission_classes=[AllowAny])
@action(
detail=True,
methods=["post"],
)
def print(self, request, pk):
return api_print(request.query_params.get("quantity", 1), self.get_object())
@action(
detail=True,
)
@action(detail=True, authentication_classes=[LanAuthentication])
def children(self, request, pk):
item = self.get_object()
return Response(
self.serializer_class(item.get_children().all(), many=True).data
)
@action(
detail=True,
)
@action(detail=True, authentication_classes=[LanAuthentication])
def ancestors(self, request, pk):
item = self.get_object()
return Response(
self.serializer_class(item.get_ancestors().all(), many=True).data
)
@action(
detail=True,
)
@action(detail=True, authentication_classes=[LanAuthentication])
def descendants(self, request, pk):
item = self.get_object()
return Response(
self.serializer_class(item.get_descendants().all(), many=True).data
)
@action(
detail=True,
)
@action(detail=True, authentication_classes=[LanAuthentication])
def siblings(self, request, pk):
item = self.get_object()
return Response(

72
storage/authentication.py Normal file
View File

@ -0,0 +1,72 @@
import ipaddress
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from spejstore.settings import (
LAN_ALLOWED_ADDRES_SPACE,
LAN_ALLOWED_HEADER,
PROD,
PROXY_TRUSTED_IPS,
)
headers_to_check_for_ip = [
"HTTP_X_FORWARDED_FOR",
"X_FORWARDED_FOR",
"HTTP_CLIENT_IP",
"HTTP_X_REAL_IP",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
]
def get_request_meta(request, key):
value = request.META.get(key, request).strip()
if value == "":
return None
return value
def get_ip_from_request(request):
for header in headers_to_check_for_ip:
ip = get_request_meta(request, header)
if not ip:
ip = get_request_meta(request, header.replace("_", "-"))
if ip:
return ip
return None
class LanAuthentication(BaseAuthentication):
def authenticate(self, request):
is_authorized = self.has_permission(request)
if is_authorized:
user = getattr(request._request, "user", None)
return (user, "authorized")
else:
raise exceptions.AuthenticationFailed(
"Unauthorized: not in subnet of " + LAN_ALLOWED_ADDRES_SPACE
)
def authenticate_header(self, request):
return LAN_ALLOWED_HEADER
def has_permission(self, request):
if PROD:
client_ip = get_ip_from_request(request)
if client_ip is None:
raise exceptions.AuthenticationFailed("Unauthorized: no ip detected?")
# Make sure that we need to check PROXY_TRUSTED_IPS here
if len(PROXY_TRUSTED_IPS) > 0:
if request.META["REMOTE_ADDR"] not in PROXY_TRUSTED_IPS:
raise exceptions.AuthenticationFailed(
"Unauthorized: request is not coming from the PROXY_TRUSTED_IPS machine"
)
return ipaddress.IPv4Address(client_ip) in ipaddress.IPv4Network(
LAN_ALLOWED_ADDRES_SPACE
)
else:
return True