Security
This commit is contained in:
parent
f32a552f46
commit
05b8ee06c1
5 changed files with 28 additions and 16 deletions
|
|
@ -55,9 +55,13 @@ impl TokenCache {
|
|||
// Evict expired entries first
|
||||
let now = Instant::now();
|
||||
map.retain(|_, (_, created)| now.duration_since(*created).as_secs() < TOKEN_TTL_SECS);
|
||||
// If still too many, clear all
|
||||
// If still too many, evict oldest half instead of clearing all
|
||||
// (avoids thundering herd where every request re-validates at once)
|
||||
if map.len() >= MAX_CACHE_ENTRIES {
|
||||
map.clear();
|
||||
let mut ages: Vec<Instant> = map.values().map(|(_, created)| *created).collect();
|
||||
ages.sort();
|
||||
let median = ages[ages.len() / 2];
|
||||
map.retain(|_, (_, created)| *created >= median);
|
||||
}
|
||||
}
|
||||
map.insert(token, (user, Instant::now()));
|
||||
|
|
|
|||
|
|
@ -28,8 +28,3 @@ pub const SERVICE_CALL_TIMEOUT: u64 = 120;
|
|||
/// Inner London free zone bounds (south, west, north, east) — roughly zone 1.
|
||||
/// Users without a license can only query data within these bounds.
|
||||
pub const FREE_ZONE_BOUNDS: (f64, f64, f64, f64) = (51.44, -0.31, 51.59, 0.05);
|
||||
|
||||
/// Exact demo bounds (south, west, north, east) sent by the homepage ScrollStory.
|
||||
/// Requests matching these exact values bypass the license check so the
|
||||
/// animation works for anonymous visitors. Only this specific viewport is allowed.
|
||||
pub const DEMO_BOUNDS: (f64, f64, f64, f64) = (46.0, -12.0, 56.5, 12.0);
|
||||
|
|
|
|||
|
|
@ -435,10 +435,19 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
let api = Router::new()
|
||||
.route("/api/features", get(routes::get_features))
|
||||
.route("/api/hexagons", get(routes::get_hexagons))
|
||||
.route("/api/postcodes", get(routes::get_postcodes))
|
||||
.route(
|
||||
"/api/hexagons",
|
||||
get(routes::get_hexagons).layer(ConcurrencyLimitLayer::new(20)),
|
||||
)
|
||||
.route(
|
||||
"/api/postcodes",
|
||||
get(routes::get_postcodes).layer(ConcurrencyLimitLayer::new(20)),
|
||||
)
|
||||
.route("/api/postcode/{postcode}", get(routes::get_postcode_lookup))
|
||||
.route("/api/pois", get(routes::get_pois))
|
||||
.route(
|
||||
"/api/pois",
|
||||
get(routes::get_pois).layer(ConcurrencyLimitLayer::new(20)),
|
||||
)
|
||||
.route("/api/poi-categories", get(routes::get_poi_categories))
|
||||
.route("/api/places", get(routes::get_places))
|
||||
.route("/api/travel-modes", get(routes::get_travel_modes))
|
||||
|
|
@ -485,7 +494,10 @@ async fn main() -> anyhow::Result<()> {
|
|||
.route("/s/{code}", get(routes::get_short_url))
|
||||
.route("/api/telemetry", post(routes::post_telemetry))
|
||||
.route("/api/reload", post(routes::post_reload))
|
||||
.route("/pb/{*rest}", any(routes::proxy_to_pocketbase))
|
||||
.route(
|
||||
"/pb/{*rest}",
|
||||
any(routes::proxy_to_pocketbase).layer(ConcurrencyLimitLayer::new(10)),
|
||||
)
|
||||
// Tile routes use a different state type — kept as closures
|
||||
.route(
|
||||
"/api/tiles/{z}/{x}/{y}",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use tracing::info;
|
|||
|
||||
use crate::aggregation::Aggregator;
|
||||
use crate::auth::OptionalUser;
|
||||
use crate::consts::{DEMO_BOUNDS, MAX_CELLS_PER_REQUEST};
|
||||
use crate::consts::MAX_CELLS_PER_REQUEST;
|
||||
use crate::data::travel_time::TravelData;
|
||||
use crate::licensing::check_license_bounds;
|
||||
use crate::parsing::{
|
||||
|
|
@ -193,10 +193,7 @@ pub async fn get_hexagons(
|
|||
let (south, west, north, east) =
|
||||
require_bounds(params.bounds).map_err(IntoResponse::into_response)?;
|
||||
|
||||
let is_demo_view = (south, west, north, east) == DEMO_BOUNDS;
|
||||
if !is_demo_view {
|
||||
check_license_bounds(&user.0, (south, west, north, east))?;
|
||||
}
|
||||
|
||||
let quant = state.data.quant_ref();
|
||||
let (parsed_filters, parsed_enum_filters) = parse_filters(
|
||||
|
|
|
|||
|
|
@ -105,6 +105,10 @@ pub async fn post_stripe_webhook(
|
|||
warn!("checkout.session.completed missing client_reference_id");
|
||||
return StatusCode::OK.into_response();
|
||||
}
|
||||
if !user_id.bytes().all(|b| b.is_ascii_alphanumeric()) || user_id.len() > 20 {
|
||||
warn!(user_id, "Invalid client_reference_id format in webhook");
|
||||
return StatusCode::BAD_REQUEST.into_response();
|
||||
}
|
||||
|
||||
// Update user subscription to "licensed" via PocketBase superuser auth
|
||||
let token = match get_superuser_token(&state).await {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue