Use salt instead of device id for email settings
This commit is contained in:
parent
2b2515b1be
commit
42561c8c02
2 changed files with 38 additions and 7 deletions
|
|
@ -4,9 +4,8 @@ import {Address, DELETE, makeEvent, makeHttpAuth, makeHttpAuthHeader} from "@wel
|
||||||
import {Relays, User} from "@welshman/app"
|
import {Relays, User} from "@welshman/app"
|
||||||
import {buildUrl} from "@lib/util"
|
import {buildUrl} from "@lib/util"
|
||||||
import {app, thunks} from "@app/core"
|
import {app, thunks} from "@app/core"
|
||||||
import {device} from "@app/device"
|
|
||||||
import {EMAIL_NOTIFICATION_SERVER, PUSH_BRIDGE} from "@app/env"
|
import {EMAIL_NOTIFICATION_SERVER, PUSH_BRIDGE} from "@app/env"
|
||||||
import {userSettingsValues} from "@app/settings"
|
import {ensureEmailSalt, userSettingsValues} from "@app/settings"
|
||||||
import {emailNotificationState, syncRelaySubscriptions} from "@app/push/adapters/common"
|
import {emailNotificationState, syncRelaySubscriptions} from "@app/push/adapters/common"
|
||||||
|
|
||||||
export class MailNotifications {
|
export class MailNotifications {
|
||||||
|
|
@ -138,8 +137,11 @@ export class MailNotifications {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_getSubscriptionIdentifier = (relay: string, key: string) =>
|
_getSubscriptionIdentifier = async (relay: string, key: string) => {
|
||||||
String(hash(relay + key + device.get() + "email"))
|
const salt = await ensureEmailSalt()
|
||||||
|
|
||||||
|
return String(hash(relay + key + salt + "email"))
|
||||||
|
}
|
||||||
|
|
||||||
_getPushUrl = async (url: string) => {
|
_getPushUrl = async (url: string) => {
|
||||||
for (const candidate of [url, PUSH_BRIDGE]) {
|
for (const candidate of [url, PUSH_BRIDGE]) {
|
||||||
|
|
@ -161,7 +163,7 @@ export class MailNotifications {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const identifier = this._getSubscriptionIdentifier(relay, key)
|
const identifier = await this._getSubscriptionIdentifier(relay, key)
|
||||||
|
|
||||||
const thunk = thunks.get().publish({
|
const thunk = thunks.get().publish({
|
||||||
relays: [url],
|
relays: [url],
|
||||||
|
|
@ -188,7 +190,7 @@ export class MailNotifications {
|
||||||
if (!url) return
|
if (!url) return
|
||||||
|
|
||||||
const $pubkey = User.require(app.get()).pubkey
|
const $pubkey = User.require(app.get()).pubkey
|
||||||
const identifier = this._getSubscriptionIdentifier(relay, key)
|
const identifier = await this._getSubscriptionIdentifier(relay, key)
|
||||||
const address = new Address(30390, $pubkey, identifier).toString()
|
const address = new Address(30390, $pubkey, identifier).toString()
|
||||||
const event = makeEvent(DELETE, {tags: [["a", address]]})
|
const event = makeEvent(DELETE, {tags: [["a", address]]})
|
||||||
const error = await thunks
|
const error = await thunks
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import {derived, writable} from "svelte/store"
|
import {derived, writable} from "svelte/store"
|
||||||
import {append, remove, spec} from "@welshman/lib"
|
import {append, call, randomId, remove, spec} from "@welshman/lib"
|
||||||
import {APP_DATA} from "@welshman/util"
|
import {APP_DATA} from "@welshman/util"
|
||||||
import {withGetter} from "@welshman/store"
|
import {withGetter} from "@welshman/store"
|
||||||
import {AppData} from "@welshman/domain"
|
import {AppData} from "@welshman/domain"
|
||||||
|
|
@ -10,6 +10,8 @@ import {app, fromApp, usePlugin} from "@app/core"
|
||||||
|
|
||||||
export const SETTINGS = "flotilla/settings"
|
export const SETTINGS = "flotilla/settings"
|
||||||
|
|
||||||
|
const emailSaltsByPubkey = new Map<string, Promise<string>>()
|
||||||
|
|
||||||
export enum RelayAuthMode {
|
export enum RelayAuthMode {
|
||||||
Aggressive = "aggressive",
|
Aggressive = "aggressive",
|
||||||
Conservative = "conservative",
|
Conservative = "conservative",
|
||||||
|
|
@ -34,6 +36,7 @@ export type SettingsValues = {
|
||||||
email: boolean
|
email: boolean
|
||||||
emailAddress: string
|
emailAddress: string
|
||||||
emailFrequency: string
|
emailFrequency: string
|
||||||
|
emailSalt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSettings: SettingsValues = {
|
export const defaultSettings: SettingsValues = {
|
||||||
|
|
@ -49,6 +52,7 @@ export const defaultSettings: SettingsValues = {
|
||||||
email: false,
|
email: false,
|
||||||
emailAddress: "",
|
emailAddress: "",
|
||||||
emailFrequency: "daily",
|
emailFrequency: "daily",
|
||||||
|
emailSalt: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Settings extends DerivedPlugin<AppDataReader> {
|
export class Settings extends DerivedPlugin<AppDataReader> {
|
||||||
|
|
@ -121,6 +125,31 @@ export const publishSettings = async (params: Partial<SettingsValues>) => {
|
||||||
return command.publish()
|
return command.publish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A per-account salt for building deterministic identifiers (e.g. kind 30390 email
|
||||||
|
// subscription ids) that reproduce on any device of the user, stored with the settings.
|
||||||
|
export const ensureEmailSalt = () => {
|
||||||
|
const $pubkey = User.require(app.get()).pubkey
|
||||||
|
const existing = emailSaltsByPubkey.get($pubkey)
|
||||||
|
if (existing) return existing
|
||||||
|
|
||||||
|
// Force-load from the network so a salt synced by another device wins over a fresh one,
|
||||||
|
// and memoize the in-flight promise so parallel syncs (one per space relay) share a salt.
|
||||||
|
const promise = call(async () => {
|
||||||
|
await settings.get().forceLoad($pubkey)
|
||||||
|
const salt = userSettingsValues.get().emailSalt
|
||||||
|
if (salt) return salt
|
||||||
|
|
||||||
|
const emailSalt = randomId()
|
||||||
|
await publishSettings({emailSalt})
|
||||||
|
|
||||||
|
return emailSalt
|
||||||
|
})
|
||||||
|
|
||||||
|
emailSaltsByPubkey.set($pubkey, promise)
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
export const addTrustedRelay = (url: string) =>
|
export const addTrustedRelay = (url: string) =>
|
||||||
publishSettings({trusted_relays: append(url, getSetting("trusted_relays"))})
|
publishSettings({trusted_relays: append(url, getSetting("trusted_relays"))})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue