From eec1bcb2b87a67ec6edd2e0f43dcb42d49296394 Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Sun, 28 Apr 2024 19:37:43 +0100 Subject: [PATCH] Add location validator --- .env-sample | 5 +++++ api/logics.py | 20 ++++++++++++++++++-- api/utils.py | 27 +++++++++++++++++++++++++++ api/views.py | 4 ++++ requirements.txt | 1 + 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/.env-sample b/.env-sample index cfb09671..40ea92b4 100644 --- a/.env-sample +++ b/.env-sample @@ -58,6 +58,11 @@ SECRET_KEY = 'django-insecure-6^&6uw$b5^en%(cu2kc7_o)(mgpazx#j_znwlym0vxfamn2uo- # e.g. robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion ONION_LOCATION = '' +# Geoblocked countries (will reject F2F trades). +# List of A3 country codes (see fhttps://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) +# Example 'NOR,USA,CZE' +GEOBLOCKED_COUNTRIES = 'NOR,USA,CZE' + # Link to robosats alternative site (shown in frontend in statsfornerds so users can switch mainnet/testnet) ALTERNATIVE_SITE = 'RoboSats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion' ALTERNATIVE_NAME = 'RoboSats Mainnet' diff --git a/api/logics.py b/api/logics.py index 54918014..8ba9ba33 100644 --- a/api/logics.py +++ b/api/logics.py @@ -1,7 +1,7 @@ import math from datetime import timedelta -from decouple import config +from decouple import config, Csv from django.contrib.auth.models import User from django.db.models import Q, Sum from django.utils import timezone @@ -9,7 +9,7 @@ from django.utils import timezone from api.lightning.node import LNNode from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order from api.tasks import send_devfund_donation, send_notification -from api.utils import get_minning_fee, validate_onchain_address +from api.utils import get_minning_fee, validate_onchain_address, location_country from chat.models import Message FEE = float(config("FEE")) @@ -29,6 +29,8 @@ MAX_MINING_NETWORK_SPEEDUP_EXPECTED = float( config("MAX_MINING_NETWORK_SPEEDUP_EXPECTED") ) +GEOBLOCKED_COUNTRIES = config("GEOBLOCKED_COUNTRIES", cast=Csv(), default=[]) + class Logics: @classmethod @@ -137,6 +139,20 @@ class Logics: return True, None + @classmethod + def validate_location(cls, order) -> bool: + if not (order.latitude or order.longitude): + return True, None + + country = location_country(order.longitude, order.latitude) + print(country, "COUNTRYYY") + if country in GEOBLOCKED_COUNTRIES: + return False, { + "bad_request": f"The coordinator does not support orders in {country}" + } + else: + return True, None + def validate_amount_within_range(order, amount): if amount > float(order.max_amount) or amount < float(order.min_amount): return False, { diff --git a/api/utils.py b/api/utils.py index 85b5dbcf..13fad5b0 100644 --- a/api/utils.py +++ b/api/utils.py @@ -479,6 +479,33 @@ def is_valid_token(token: str) -> bool: return all(c in charset for c in token) +def location_country(lon: float, lat: float) -> str: + """ + Returns the country code of a lon/lat location + """ + + from shapely.geometry import shape, Point + from shapely.prepared import prep + + # Load the GeoJSON data from a local file + with open("frontend/static/assets/geo/countries-coastline-10km.geo.json") as f: + countries_geojeson = json.load(f) + + # Prepare the countries for reverse geocoding + countries = {} + for feature in countries_geojeson["features"]: + geom = feature["geometry"] + country_code = feature["properties"]["A3"] + countries[country_code] = prep(shape(geom)) + + point = Point(lon, lat) + for country_code, geom in countries.items(): + if geom.contains(point): + return country_code + + return "unknown" + + def objects_to_hyperlinks(logs: str) -> str: """ Parses strings that have Object(ID,NAME) that match API models. diff --git a/api/views.py b/api/views.py index 53e15292..78bd275c 100644 --- a/api/views.py +++ b/api/views.py @@ -166,6 +166,10 @@ class MakerView(CreateAPIView): if not valid: return Response(context, status.HTTP_400_BAD_REQUEST) + valid, context = Logics.validate_location(order) + if not valid: + return Response(context, status.HTTP_400_BAD_REQUEST) + order.save() order.log( f"Order({order.id},{order}) created by Robot({request.user.robot.id},{request.user})" diff --git a/requirements.txt b/requirements.txt index 3fdd9276..b6b89c85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ psycopg2==2.9.9 SQLAlchemy==2.0.16 django-import-export==3.3.8 requests[socks] +shapely==2.0.4 python-gnupg==0.5.2 daphne==4.1.0 drf-spectacular==0.27.2