From 91b836bf661e33e3a921a1d768af36021ade6878 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 6 May 2024 14:23:54 +0200 Subject: [PATCH] Enable more privacy for some themes and layers --- assets/layers/advertising/advertising.json | 1 + assets/layers/brothel/brothel.json | 1 + assets/layers/love_hotel/love_hotel.json | 1 + assets/layers/stripclub/stripclub.json | 1 + .../surveillance_camera.json | 1 + assets/themes/openlovemap/openlovemap.json | 1 + assets/themes/surveillance/surveillance.json | 1 + src/Logic/Osm/Changes.ts | 9 ++++++-- src/Logic/Osm/ChangesetHandler.ts | 21 +++++++------------ src/Logic/State/FeatureSwitchState.ts | 9 ++++++++ .../ThemeConfig/Json/LayerConfigJson.ts | 11 ++++++++++ .../ThemeConfig/Json/LayoutConfigJson.ts | 12 +++++++++++ src/Models/ThemeConfig/LayerConfig.ts | 2 ++ src/Models/ThemeConfig/LayoutConfig.ts | 3 +++ src/Models/ThemeViewState.ts | 1 + 15 files changed, 59 insertions(+), 16 deletions(-) diff --git a/assets/layers/advertising/advertising.json b/assets/layers/advertising/advertising.json index df7ea81fb..f46dc2306 100644 --- a/assets/layers/advertising/advertising.json +++ b/assets/layers/advertising/advertising.json @@ -28,6 +28,7 @@ "pt_BR": "Completaremos os dados das características de publicidade com referência, operador e iluminação", "it": "Completeremo i dati da caratteristiche pubblicitarie, con referenza, operatore e illuminazione" }, + "enableMorePrivacy": true, "source": { "osmTags": { "and": [ diff --git a/assets/layers/brothel/brothel.json b/assets/layers/brothel/brothel.json index 40c3406bd..780171d75 100644 --- a/assets/layers/brothel/brothel.json +++ b/assets/layers/brothel/brothel.json @@ -11,6 +11,7 @@ "source": { "osmTags": "amenity=brothel" }, + "enableMorePrivacy": true, "minzoom": 6, "title": { "render": { diff --git a/assets/layers/love_hotel/love_hotel.json b/assets/layers/love_hotel/love_hotel.json index 6d0418285..15f63255c 100644 --- a/assets/layers/love_hotel/love_hotel.json +++ b/assets/layers/love_hotel/love_hotel.json @@ -8,6 +8,7 @@ "en": "A love hotel is a type of short-stay hotel found around the world operated primarily for the purpose of allowing guests privacy for sexual activities", "de": "Ein Love Hotel ist eine Art Kurzzeithotel, das in erster Linie zu dem Zweck betrieben wird, den Gästen Privatsphäre für sexuelle Aktivitäten zu bieten" }, + "enableMorePrivacy": true, "source": { "osmTags": "amenity=love_hotel" }, diff --git a/assets/layers/stripclub/stripclub.json b/assets/layers/stripclub/stripclub.json index 048469402..af62d7b65 100644 --- a/assets/layers/stripclub/stripclub.json +++ b/assets/layers/stripclub/stripclub.json @@ -8,6 +8,7 @@ "en": "A venue where erotic dance, striptease, or lap dances are performed commercially. ", "de": "Ein Ort, an dem erotische Tanz-, Striptease- oder Lapdances kommerziell durchgeführt werden. " }, + "enableMorePrivacy": true, "source": { "osmTags": "amenity=stripclub" }, diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index a4536e938..f1f2d9063 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -22,6 +22,7 @@ "cs": "Tato vrstva zobrazuje sledovací kamery a umožňuje přispěvateli aktualizovat informace a přidávat nové kamery", "sl": "Ta sloj prikazuje nadzorne kamere in urednikom omogoča posodabljanje informacij obstoječih in dodajanje novih kamer" }, + "enableMorePrivacy": true, "source": { "osmTags": { "and": [ diff --git a/assets/themes/openlovemap/openlovemap.json b/assets/themes/openlovemap/openlovemap.json index 5591b0afc..8de16546c 100644 --- a/assets/themes/openlovemap/openlovemap.json +++ b/assets/themes/openlovemap/openlovemap.json @@ -12,6 +12,7 @@ }, "icon": "./assets/layers/stripclub/stripclub.svg", "hideFromOverview": true, + "enableMorePrivacy": true, "layers": [ "brothel", "stripclub", diff --git a/assets/themes/surveillance/surveillance.json b/assets/themes/surveillance/surveillance.json index 16ea327c6..455a25204 100644 --- a/assets/themes/surveillance/surveillance.json +++ b/assets/themes/surveillance/surveillance.json @@ -52,6 +52,7 @@ }, "icon": "./assets/themes/surveillance/logo.svg", "defaultBackgroundId": "maptiler.carto", + "enableMorePrivacy": true, "layers": [ "surveillance_camera", { diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 2ec95db6f..25315546a 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -19,6 +19,7 @@ import Title from "../../UI/Base/Title" import Table from "../../UI/Base/Table" import ChangeLocationAction from "./Actions/ChangeLocationAction" import ChangeTagAction from "./Actions/ChangeTagAction" +import FeatureSwitchState from "../State/FeatureSwitchState" /** * Handles all changes made to OSM. @@ -28,7 +29,7 @@ export class Changes { public readonly pendingChanges: UIEventSource = LocalStorageSource.GetParsed("pending-changes", []) public readonly allChanges = new UIEventSource(undefined) - public readonly state: { allElements?: IndexedFeatureSource; osmConnection: OsmConnection } + public readonly state: { allElements?: IndexedFeatureSource; osmConnection: OsmConnection, featureSwitches?: FeatureSwitchState } public readonly extraComment: UIEventSource = new UIEventSource(undefined) public readonly backend: string public readonly isUploading = new UIEventSource(false) @@ -45,7 +46,8 @@ export class Changes { allElements?: IndexedFeatureSource featurePropertiesStore?: FeaturePropertiesStore osmConnection: OsmConnection - historicalUserLocations?: FeatureSource + historicalUserLocations?: FeatureSource, + featureSwitches?: FeatureSwitchState }, leftRightSensitive: boolean = false ) { @@ -431,6 +433,9 @@ export class Changes { // Probably irrelevant, such as a new helper node return } + if(this.state.featureSwitches.featureSwitchMorePrivacy?.data){ + return + } const now = new Date() const recentLocationPoints = locations diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index 71c0e299f..45f7695c5 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -6,14 +6,6 @@ import Constants from "../../Models/Constants" import { Changes } from "./Changes" import { Utils } from "../../Utils" import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" -import ChangeLocationAction from "./Actions/ChangeLocationAction" -import ChangeTagAction from "./Actions/ChangeTagAction" -import DeleteAction from "./Actions/DeleteAction" -import LinkImageAction from "./Actions/LinkImageAction" -import OsmChangeAction from "./Actions/OsmChangeAction" -import RelationSplitHandler from "./Actions/RelationSplitHandler" -import ReplaceGeometryAction from "./Actions/ReplaceGeometryAction" -import SplitAction from "./Actions/SplitAction" export interface ChangesetTag { key: string @@ -232,7 +224,7 @@ export class ChangesetHandler { if (newMetaTag === undefined) { extraMetaTags.push({ key: key, - value: oldCsTags[key], + value: oldCsTags[key] }) continue } @@ -361,21 +353,22 @@ export class ChangesetHandler { } private defaultChangesetTags(): ChangesetTag[] { + const usedGps = this.changes.state["currentUserLocation"]?.features?.data?.length > 0 + const hasMorePrivacy = !!this.changes.state?.featureSwitches?.featureSwitchMorePrivacy?.data + const setSourceAsSurvey = !hasMorePrivacy && usedGps return [ ["created_by", `MapComplete ${Constants.vNumber}`], ["locale", Locale.language.data], ["host", `${window.location.origin}${window.location.pathname}`], [ "source", - this.changes.state["currentUserLocation"]?.features?.data?.length > 0 - ? "survey" - : undefined, + setSourceAsSurvey ? "survey" : undefined ], - ["imagery", this.changes.state["backgroundLayer"]?.data?.id], + ["imagery", this.changes.state["backgroundLayer"]?.data?.id] ].map(([key, value]) => ({ key, value, - aggregate: false, + aggregate: false })) } diff --git a/src/Logic/State/FeatureSwitchState.ts b/src/Logic/State/FeatureSwitchState.ts index d88ad9826..cf3bc6359 100644 --- a/src/Logic/State/FeatureSwitchState.ts +++ b/src/Logic/State/FeatureSwitchState.ts @@ -63,6 +63,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { public readonly overpassMaxZoom: UIEventSource public readonly osmApiTileSize: UIEventSource public readonly backgroundLayerId: UIEventSource + public readonly featureSwitchMorePrivacy: UIEventSource public constructor(layoutToUse?: LayoutConfig) { super() @@ -164,6 +165,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { "If true, shows some extra debugging help such as all the available tags on every object" ) + + this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter( + "moreprivacy", + layoutToUse.enableMorePrivacy, + "If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken." + ) + + this.overpassUrl = QueryParameters.GetQueryParameter( "overpassUrl", (layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","), diff --git a/src/Models/ThemeConfig/Json/LayerConfigJson.ts b/src/Models/ThemeConfig/Json/LayerConfigJson.ts index a2053e097..00bf57e4e 100644 --- a/src/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/src/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -554,4 +554,15 @@ export interface LayerConfigJson { * group: hidden */ fullNodeDatabase?: boolean + + /** + * question: Should a theme using this layer leak some location info when making changes? + * + * When a changeset is made, a 'distance to object'-class is written to the changeset. + * For some particular themes and layers, this might leak too much information, and we want to obfuscate this + * + * ifunset: Write 'change_within_x_m' as usual and if GPS is enabled + * iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey + */ + enableMorePrivacy: boolean } diff --git a/src/Models/ThemeConfig/Json/LayoutConfigJson.ts b/src/Models/ThemeConfig/Json/LayoutConfigJson.ts index c32efc166..a787d20c7 100644 --- a/src/Models/ThemeConfig/Json/LayoutConfigJson.ts +++ b/src/Models/ThemeConfig/Json/LayoutConfigJson.ts @@ -439,4 +439,16 @@ export interface LayoutConfigJson { * group: hidden */ enableNodeDatabase?: boolean + + /** + * question: Should this theme leak some location info when making changes? + * + * When a changeset is made, a 'distance to object'-class is written to the changeset. + * For some particular themes and layers, this might leak too much information, and we want to obfuscate this + * + * ifunset: Write 'change_within_x_m' as usual and if GPS is enabled + * iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey + */ + enableMorePrivacy: boolean + } diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 5d0a15fc0..2114c76af 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -67,6 +67,7 @@ export default class LayerConfig extends WithContextLoader { public readonly _needsFullNodeDatabase: boolean public readonly popupInFloatover: boolean | string + public readonly enableMorePrivacy: boolean constructor(json: LayerConfigJson, context?: string, official: boolean = true) { context = context + "." + json.id @@ -149,6 +150,7 @@ export default class LayerConfig extends WithContextLoader { this.shownByDefault = json.shownByDefault ?? true this.doCount = json.isCounted ?? this.shownByDefault ?? true this.forceLoad = json.forceLoad ?? false + this.enableMorePrivacy = json.enableMorePrivacy ?? false if (json.presets === null) json.presets = undefined if (json.presets !== undefined && json.presets?.map === undefined) { throw "Presets should be a list of items (at " + context + ")" diff --git a/src/Models/ThemeConfig/LayoutConfig.ts b/src/Models/ThemeConfig/LayoutConfig.ts index 0306db182..02672bbf1 100644 --- a/src/Models/ThemeConfig/LayoutConfig.ts +++ b/src/Models/ThemeConfig/LayoutConfig.ts @@ -63,6 +63,8 @@ export default class LayoutConfig implements LayoutInformation { public readonly enableExportButton: boolean public readonly enablePdfDownload: boolean public readonly enableTerrain: boolean + public readonly enableMorePrivacy: boolean + public readonly customCss?: string @@ -204,6 +206,7 @@ export default class LayoutConfig implements LayoutInformation { this.overpassTimeout = json.overpassTimeout ?? 30 this.overpassMaxZoom = json.overpassMaxZoom ?? 16 this.osmApiTileSize = json.osmApiTileSize ?? this.overpassMaxZoom + 1 + this.enableMorePrivacy = json.enableMorePrivacy || json.layers.some(l => ( l).enableMorePrivacy) this.layersDict = new Map() for (const layer of this.layers) { diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 49b3a29b9..635157026 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -265,6 +265,7 @@ export default class ThemeViewState implements SpecialVisualizationState { featurePropertiesStore: this.featureProperties, osmConnection: this.osmConnection, historicalUserLocations: this.geolocation.historicalUserLocations, + featureSwitches: this.featureSwitches }, layout?.isLeftRightSensitive() ?? false )