mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-20 22:37:44 +00:00
Improve checking callsign validity by regex, and fix passing v1 type credentials into v2 APIs
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from core.constants import CALL_ONLY_PATTERN
|
||||
from core.data_providers import DATA_PROVIDERS
|
||||
from data.callsign import Callsign
|
||||
|
||||
@@ -9,7 +10,8 @@ def get_call_info(callsign, lookup_credentials):
|
||||
|
||||
callsign_data = Callsign(call=callsign)
|
||||
|
||||
if callsign:
|
||||
# First check our input looks like a real callsign
|
||||
if callsign and CALL_ONLY_PATTERN.match(callsign):
|
||||
# Sort callsign providers by priority order, so we query the highest priority (lowest numbers) first, and only
|
||||
# query other providers for data we are missing as we go along.
|
||||
for p in sorted(DATA_PROVIDERS.callsign_data_providers, key=lambda p2: p2.priority):
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import re
|
||||
|
||||
from core.config import SERVER_OWNER_CALLSIGN
|
||||
from data.band import Band
|
||||
from data.sig import SIG
|
||||
@@ -9,6 +11,11 @@ SOFTWARE_VERSION = "2.0-pre"
|
||||
HTTP_HEADERS = {"User-Agent": f"Spothole v{SOFTWARE_VERSION} (operated by {SERVER_OWNER_CALLSIGN})"}
|
||||
HAMQTH_PRG = f"Spothole v{SOFTWARE_VERSION} operated by {SERVER_OWNER_CALLSIGN}".replace(" ", "_")
|
||||
|
||||
# Generally useful regexes
|
||||
CALL_REGEX = r"[A-Za-z0-9/\-]+"
|
||||
CALL_PATTERN = re.compile(CALL_REGEX)
|
||||
CALL_ONLY_PATTERN = re.compile(rf"^{CALL_REGEX}$")
|
||||
|
||||
# Special Interest Groups
|
||||
SIGS = [
|
||||
SIG(
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ from pyhamtools.locator import latlong_to_locator, locator_to_latlong
|
||||
|
||||
from core.call_lookup_helper import get_call_info
|
||||
from core.config import MAX_SPOT_AGE
|
||||
from core.constants import MODE_ALIASES, PROPAGATION_MODES
|
||||
from core.constants import CALL_REGEX, MODE_ALIASES, PROPAGATION_MODES
|
||||
from core.geo_utils import lat_lon_to_cq_zone, lat_lon_to_itu_zone
|
||||
from core.sig_lookup_helper import populate_missing_sig_ref_info
|
||||
from core.sig_utils import (
|
||||
@@ -201,14 +201,14 @@ class Spot:
|
||||
# If we have a spotter of "RBNHOLE", we should have the actual spotter callsign in the comment, so extract it.
|
||||
# RBNHole posts come from a number of providers, so it's dealt with here in the generic spot handling code.
|
||||
if self.de_call == "RBNHOLE" and self.comment:
|
||||
rbnhole_call_match = re.search(r"\Wat ([a-z0-9/]+)\W", self.comment, re.IGNORECASE)
|
||||
rbnhole_call_match = re.search(rf"\Wat ({CALL_REGEX})\W", self.comment, re.IGNORECASE)
|
||||
if rbnhole_call_match:
|
||||
self.de_call = rbnhole_call_match.group(1).upper()
|
||||
|
||||
# If we have a spotter of "SOTAMAT", we might have the actual spotter callsign in the comment, if so extract it.
|
||||
# SOTAMAT can do POTA as well as SOTA, so it's dealt with here in the generic spot handling code.
|
||||
if self.de_call == "SOTAMAT" and self.comment:
|
||||
sotamat_call_match = re.search(r"\Wfrom ([a-z0-9/]+)]", self.comment, re.IGNORECASE)
|
||||
sotamat_call_match = re.search(rf"\Wfrom ({CALL_REGEX})]", self.comment, re.IGNORECASE)
|
||||
if sotamat_call_match:
|
||||
self.de_call = sotamat_call_match.group(1).upper()
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import pytz
|
||||
from rss_parser import Parser
|
||||
from rss_parser.models.rss import RSS
|
||||
|
||||
from core.constants import CALL_REGEX
|
||||
from data.alert import Alert
|
||||
from providers.alert.http_alert_provider import HTTPAlertProvider
|
||||
|
||||
@@ -15,7 +16,7 @@ class NG3K(HTTPAlertProvider):
|
||||
|
||||
POLL_INTERVAL_SEC = 1800
|
||||
ALERTS_URL = "https://www.ng3k.com/adxo.xml"
|
||||
AS_CALL_PATTERN = re.compile("as ([a-z0-9/]+)", re.IGNORECASE)
|
||||
AS_CALL_PATTERN = re.compile(rf"as ({CALL_REGEX})", re.IGNORECASE)
|
||||
|
||||
def __init__(self, provider_config):
|
||||
super().__init__("NG3K", provider_config, self.ALERTS_URL, self.POLL_INTERVAL_SEC)
|
||||
|
||||
@@ -8,6 +8,7 @@ import pytz
|
||||
import telnetlib3
|
||||
|
||||
from core.config import SERVER_OWNER_CALLSIGN
|
||||
from core.constants import CALL_REGEX
|
||||
from data.spot import Spot
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
@@ -19,11 +20,11 @@ class DXCluster(SpotProvider):
|
||||
See config-example.yml for examples."""
|
||||
|
||||
_LINE_PATTERN_EXCLUDE_RBN = re.compile(
|
||||
r"^DX de ([a-z0-9/]+):\s+([0-9.]+)\s+([a-z0-9/]+)\s+(.*)\s+(\d{4}Z)",
|
||||
rf"^DX de ({CALL_REGEX}):\s+([0-9.]+)\s+({CALL_REGEX})\s+(.*)\s+(\d{4}Z)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_LINE_PATTERN_ALLOW_RBN = re.compile(
|
||||
r"^DX de ([a-z0-9/]+)-?#?:\s+([0-9.]+)\s+([a-z0-9/]+)\s+(.*)\s+(\d{4}Z)",
|
||||
rf"^DX de ({CALL_REGEX})-?#?:\s+([0-9.]+)\s+({CALL_REGEX})\s+(.*)\s+(\d{4}Z)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from datetime import datetime
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
from core.constants import HTTP_HEADERS
|
||||
from core.constants import HTTP_HEADERS, CALL_REGEX
|
||||
from data.sig_ref import SIGRef
|
||||
from data.spot import Spot
|
||||
from providers.spot.http_spot_provider import HTTPSpotProvider
|
||||
@@ -60,7 +60,7 @@ class ParksNPeaks(HTTPSpotProvider):
|
||||
)
|
||||
|
||||
# Extract a de_call if it's in the comment but not in the "actSpoter" field
|
||||
m = re.search(r"\(de ([A-Za-z0-9]*)\)", spot.comment or "")
|
||||
m = re.search(rf"\(de ({CALL_REGEX})\)", spot.comment or "")
|
||||
if not spot.de_call and m:
|
||||
spot.de_call = str(m.group(1))
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import pytz
|
||||
import telnetlib3
|
||||
|
||||
from core.config import SERVER_OWNER_CALLSIGN
|
||||
from core.constants import CALL_REGEX
|
||||
from data.spot import Spot
|
||||
from providers.spot.spot_provider import SpotProvider
|
||||
|
||||
@@ -19,7 +20,7 @@ class RBN(SpotProvider):
|
||||
(port 7001) you need to instantiate two copies of this. The port is provided as an argument to the constructor."""
|
||||
|
||||
_LINE_PATTERN = re.compile(
|
||||
r"^DX de ([a-z0-9/]+)-.*:\s+([0-9.]+)\s+([a-z0-9/]+)\s+(.*)\s+(\d{4}Z)",
|
||||
rf"^DX de ({CALL_REGEX})-.*:\s+([0-9.]+)\s+({CALL_REGEX})\s+(.*)\s+(\d{4}Z)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from tornado import httputil
|
||||
from tornado.web import Application
|
||||
|
||||
from core.config import ALLOW_SPOTTING, ALLOW_UPSTREAM_SPOTTING, RECAPTCHA_SECRET_KEY
|
||||
from core.constants import UNKNOWN_BAND
|
||||
from core.constants import CALL_ONLY_PATTERN, UNKNOWN_BAND
|
||||
from core.prometheus_metrics_handler import api_requests_counter
|
||||
from core.sig_utils import get_ref_regex_for_sig
|
||||
from core.utils import infer_band_from_freq, safe_json_dumps
|
||||
@@ -121,13 +121,13 @@ class APISpotHandler(tornado.web.RequestHandler):
|
||||
return
|
||||
|
||||
# Reject invalid-looking callsigns
|
||||
if not re.match(r"^[A-Za-z0-9/\-]*$", spot.dx_call):
|
||||
if not CALL_ONLY_PATTERN.match(spot.dx_call):
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps(f"Error - '{spot.dx_call}' does not look like a valid callsign."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
self.set_header("Content-Type", "application/json")
|
||||
return
|
||||
if not re.match(r"^[A-Za-z0-9/\-]*$", spot.de_call):
|
||||
if not CALL_ONLY_PATTERN.match(spot.de_call):
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps(f"Error - '{spot.de_call}' does not look like a valid callsign."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
|
||||
@@ -9,7 +9,7 @@ from tornado import httputil
|
||||
from tornado.web import Application
|
||||
|
||||
from core.call_lookup_helper import get_call_info
|
||||
from core.constants import SIGS
|
||||
from core.constants import CALL_ONLY_PATTERN, SIGS
|
||||
from core.geo_utils import (
|
||||
lat_lon_for_grid_sw_corner_plus_size,
|
||||
lat_lon_to_cq_zone,
|
||||
@@ -55,7 +55,7 @@ class APILookupCallHandler(tornado.web.RequestHandler):
|
||||
# The "call" query param must exist and look like a callsign
|
||||
if "call" in query_params:
|
||||
call = str(query_params.get("call")).upper()
|
||||
if re.match(r"^[A-Z0-9/\-]*$", call):
|
||||
if CALL_ONLY_PATTERN.match(call):
|
||||
credentials = extract_credentials(self.request.headers)
|
||||
callsign_data = get_call_info(call, credentials)
|
||||
self.write(safe_json_dumps(callsign_data))
|
||||
|
||||
@@ -9,7 +9,7 @@ from tornado import httputil
|
||||
from tornado.web import Application
|
||||
|
||||
from core.config import ALLOW_SPOTTING
|
||||
from core.constants import UNKNOWN_BAND
|
||||
from core.constants import CALL_ONLY_PATTERN, UNKNOWN_BAND
|
||||
from core.prometheus_metrics_handler import api_requests_counter
|
||||
from core.sig_utils import get_ref_regex_for_sig
|
||||
from core.utils import infer_band_from_freq, safe_json_dumps
|
||||
@@ -83,13 +83,13 @@ class V1APISpotHandler(tornado.web.RequestHandler):
|
||||
return
|
||||
|
||||
# Reject invalid-looking callsigns
|
||||
if not re.match(r"^[A-Za-z0-9/\-]*$", spot.dx_call):
|
||||
if not CALL_ONLY_PATTERN.match(spot.dx_call):
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps(f"Error - '{spot.dx_call}' does not look like a valid callsign."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
self.set_header("Content-Type", "application/json")
|
||||
return
|
||||
if not re.match(r"^[A-Za-z0-9/\-]*$", spot.de_call):
|
||||
if not CALL_ONLY_PATTERN.match(spot.de_call):
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps(f"Error - '{spot.de_call}' does not look like a valid callsign."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
|
||||
@@ -2,7 +2,6 @@ import tornado
|
||||
from tornado.httpclient import AsyncHTTPClient
|
||||
from tornado.httputil import HTTPHeaders
|
||||
|
||||
|
||||
_LEGACY_PARAM_TO_HEADER_MAP = {
|
||||
"qrz_username": "X-QRZ-Username",
|
||||
"qrz_password": "X-QRZ-Password",
|
||||
@@ -12,6 +11,7 @@ _LEGACY_PARAM_TO_HEADER_MAP = {
|
||||
"hamqth_session_id": "X-HamQTH-Session-ID",
|
||||
}
|
||||
|
||||
|
||||
class V1RedirectHandler(tornado.web.RequestHandler):
|
||||
"""Transparently proxies requests from the old API to the new one,
|
||||
returning whatever the v2 endpoint returns, for endpoints with no breaking changes."""
|
||||
@@ -34,7 +34,7 @@ class V1RedirectHandler(tornado.web.RequestHandler):
|
||||
response = await client.fetch(
|
||||
new_url,
|
||||
method=self.request.method,
|
||||
headers=self.request.headers,
|
||||
headers=headers,
|
||||
body=None if self.request.method == "GET" else (self.request.body or b""),
|
||||
raise_error=False,
|
||||
follow_redirects=False,
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/js/add-spot.js?v=1786970620"></script>
|
||||
<script src="/static/js/add-spot.js?v=1786980624"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-add-spot").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/js/alerts.js?v=1786970620"></script>
|
||||
<script src="/static/js/alerts.js?v=1786980625"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-alerts").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
@@ -76,8 +76,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786970620"></script>
|
||||
<script src="/static/js/bands.js?v=1786970620"></script>
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786980624"></script>
|
||||
<script src="/static/js/bands.js?v=1786980624"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-bands").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=1786970620" type="text/css">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=1786980624" type="text/css">
|
||||
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
|
||||
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
|
||||
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
|
||||
@@ -15,10 +15,10 @@
|
||||
window.fetchEventSource = fetchEventSource;
|
||||
</script>
|
||||
|
||||
<script src="/static/js/utils.js?v=1786970620"></script>
|
||||
<script src="/static/js/ui-ham.js?v=1786970620"></script>
|
||||
<script src="/static/js/geo.js?v=1786970620"></script>
|
||||
<script src="/static/js/common.js?v=1786970620"></script>
|
||||
<script src="/static/js/utils.js?v=1786980624"></script>
|
||||
<script src="/static/js/ui-ham.js?v=1786980624"></script>
|
||||
<script src="/static/js/geo.js?v=1786980624"></script>
|
||||
<script src="/static/js/common.js?v=1786980624"></script>
|
||||
{% end %}
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
|
||||
<script src="/static/js/conditions.js?v=1786970620"></script>
|
||||
<script src="/static/js/conditions.js?v=1786980624"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-conditions").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
+2
-2
@@ -109,8 +109,8 @@
|
||||
<script src="/static/vendor/js/leaflet-cqzones.js"></script>
|
||||
<script src="/static/vendor/js/leaflet-workedallbritainireland.js" type="module"></script>
|
||||
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786970620"></script>
|
||||
<script src="/static/js/map.js?v=1786970620"></script>
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786980625"></script>
|
||||
<script src="/static/js/map.js?v=1786980625"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-map").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
@@ -113,8 +113,8 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786970620"></script>
|
||||
<script src="/static/js/spots.js?v=1786970620"></script>
|
||||
<script src="/static/js/spotsbandsandmap.js?v=1786980624"></script>
|
||||
<script src="/static/js/spots.js?v=1786980624"></script>
|
||||
<script>$(document).ready(function () {
|
||||
$("#nav-link-spots").addClass("active");
|
||||
}); <!-- highlight active page in nav --></script>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/status.js?v=1786970620"></script>
|
||||
<script src="/static/js/status.js?v=1786980624"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$("#nav-link-status").addClass("active");
|
||||
|
||||
Reference in New Issue
Block a user