Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions changelog.d/1-api-changes/wp-api-web-push-subscriptions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In API version V17, gundeck adds new web push subscription endpoints under `POST/DELETE/GET /push/web/subscriptions` for delivering notifications to web browsers via the W3C Push API (RFC 8030/8291/8292).
7 changes: 7 additions & 0 deletions charts/integration/templates/integration-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ spec:
configMap:
name: "gundeck"

- name: "gundeck-secrets"
secret:
secretName: "gundeck"

- name: "cargohold-config"
configMap:
name: "cargohold"
Expand Down Expand Up @@ -237,6 +241,9 @@ spec:
- name: gundeck-config
mountPath: /etc/wire/gundeck/conf

- name: gundeck-secrets
mountPath: /etc/wire/gundeck/secrets

- name: cargohold-config
mountPath: /etc/wire/cargohold/conf

Expand Down
6 changes: 6 additions & 0 deletions charts/wire-server/templates/gundeck/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ data:
tlsCa: /etc/wire/gundeck/cassandra/{{- (include "gundeck.tlsSecretRef" . | fromYaml).key }}
{{- end }}

postgresql: {{ toYaml .postgresql | nindent 6 }}
postgresqlPool: {{ toYaml .postgresqlPool | nindent 6 }}
{{- if hasKey $.Values.gundeck.secrets "pgPassword" }}
postgresqlPassword: /etc/wire/gundeck/secrets/pgPassword
{{- end }}

{{- with .rabbitmq }}
rabbitmq:
host: {{ .host }}
Expand Down
9 changes: 9 additions & 0 deletions charts/wire-server/templates/gundeck/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ spec:
- name: "gundeck-config"
configMap:
name: "gundeck"
{{- if not (empty .Values.gundeck.secrets) }}
- name: "gundeck-secrets"
secret:
secretName: "gundeck"
{{- end }}
{{- if and .Values.gundeck.config.rabbitmq .Values.gundeck.config.rabbitmq.tlsCaSecretRef }}
- name: "rabbitmq-ca"
secret:
Expand Down Expand Up @@ -71,6 +76,10 @@ spec:
volumeMounts:
- name: "gundeck-config"
mountPath: "/etc/wire/gundeck/conf"
{{- if not (empty .Values.gundeck.secrets) }}
- name: "gundeck-secrets"
mountPath: "/etc/wire/gundeck/secrets"
{{- end }}
{{- if eq (include "useCassandraTLS" .Values.gundeck.config.cassandra) "true" }}
- name: "gundeck-cassandra"
mountPath: "/etc/wire/gundeck/cassandra"
Expand Down
3 changes: 3 additions & 0 deletions charts/wire-server/templates/gundeck/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@ data:
{{- if hasKey . "redisAdditionalWritePassword" }}
redisAdditionalWritePassword: {{ .redisAdditionalWritePassword | b64enc | quote }}
{{- end }}
{{- if hasKey . "pgPassword" }}
pgPassword: {{ .pgPassword | b64enc | quote }}
{{- end }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ spec:
- name: "gundeck-config"
configMap:
name: "gundeck"
- name: "gundeck-secrets"
secret:
secretName: "gundeck"
{{- if eq (include "useCassandraTLS" .Values.gundeck.config.cassandra) "true" }}
- name: "gundeck-cassandra"
secret:
Expand Down Expand Up @@ -69,6 +72,8 @@ spec:
mountPath: "/etc/wire/integration"
- name: "gundeck-config"
mountPath: "/etc/wire/gundeck/conf"
- name: "gundeck-secrets"
mountPath: "/etc/wire/gundeck/secrets"
{{- if eq (include "useCassandraTLS" .Values.gundeck.config.cassandra) "true" }}
- name: "gundeck-cassandra"
mountPath: "/etc/wire/gundeck/cassandra"
Expand Down
18 changes: 18 additions & 0 deletions charts/wire-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,24 @@ gundeck:
# To enable cells notifications
# cellsEventQueue: cells_events

# Postgres connection settings for web push subscription storage.
# Web push is the first gundeck subsystem backed by Postgres; Cassandra
# remains the store for native push. Migrations run unconditionally at
# startup, so these settings are required even when web push is disabled.
#
# Values are described in https://www.postgresql.org/docs/17/libpq-connect.html#LIBPQ-PARAMKEYWORDS
# To set the password via a gundeck secret see `secrets.pgPassword`.
postgresql:
host: postgresql # DNS name without protocol
port: "5432"
user: wire-server
dbname: wire-server
postgresqlPool:
size: 100
acquisitionTimeout: 10s
agingTimeout: 1d
idlenessTimeout: 10m

serviceAccount:
# When setting this to 'false', either make sure that a service account named
# 'gundeck' exists or change the 'name' field to 'default'
Expand Down
12 changes: 12 additions & 0 deletions deploy/dockerephemeral/federation-v2/gundeck.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,17 @@ settings:
disabledAPIVersions: []
cellsEventQueue: "cells_events"

# Web Push (RFC 8030/8291/8292). Optional: omit this section to disable web
# push entirely. When enabled, gundeck stores browser push subscriptions and
# POSTs encrypted notifications to them. The VAPID private key is a secret —
# prefer the GUNDECK_WEBPUSH_VAPID_PRIVATE_KEY env var over committing it here.
# webpush:
# vapidSubject: "mailto:[email protected]"
# vapidPrivateKey: "BASE64URL_P256_PRIVATE_KEY" # 32-byte scalar, base64url
# endpointAllowlist: # SSRF allowlist (host suffixes)
# - "fcm.googleapis.com"
# - "updates.push.services.mozilla.com"
# defaultTTL: 2419200 # seconds (~4 weeks)

logLevel: Warn
logNetStrings: false
11 changes: 11 additions & 0 deletions hack/helm_vars/wire-server/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,21 @@ gundeck:
hard: 30
soft: 10
cellsEventQueue: cells_events
postgresql:
host: "postgresql"
port: "5432"
user: wire-server
dbname: wire-server
postgresqlPool:
size: 100
acquisitionTimeout: 10s
agingTimeout: 1d
idlenessTimeout: 10m
secrets:
awsKeyId: dummykey
awsSecretKey: dummysecret
redisPassword: very-secure-redis-master-password
pgPassword: posty-the-gres
rabbitmq:
username: {{ .Values.rabbitmqUsername }}
password: {{ .Values.rabbitmqPassword }}
Expand Down
1 change: 1 addition & 0 deletions integration/test/Testlib/ModService.hs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ defaultOverrides resource =
def
{ brigCfg = setField "postgresql.dbname" resource.berPostgresqlDBName,
galleyCfg = setField "postgresql.dbname" resource.berPostgresqlDBName,
gundeckCfg = setField "postgresql.dbname" resource.berPostgresqlDBName,
backgroundWorkerCfg = setField "postgresql.dbname" resource.berPostgresqlDBName
}

Expand Down
9 changes: 9 additions & 0 deletions libs/wire-api/src/Wire/API/Error/Gundeck.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ data GundeckError
| AddTokenErrorApnsVoipNotSupported
| TokenNotFound
| NotificationNotFound
| WebPushErrorInvalid
| WebPushSubscriptionNotFound
| WebPushErrorTooMany

instance (Typeable (MapError e), KnownError (MapError e)) => IsSwaggerError (e :: GundeckError) where
addToOpenApi = addStaticErrorToSwagger @(MapError e)
Expand All @@ -48,3 +51,9 @@ type instance MapError 'AddTokenErrorApnsVoipNotSupported = 'StaticError 400 "ap
type instance MapError 'TokenNotFound = 'StaticError 404 "not-found" "Push token not found"

type instance MapError 'NotificationNotFound = 'StaticError 404 "not-found" "Some notifications not found"

type instance MapError 'WebPushErrorInvalid = 'StaticError 400 "invalid-web-push-subscription" "Invalid web push subscription"

type instance MapError 'WebPushSubscriptionNotFound = 'StaticError 404 "not-found" "Web push subscription not found"

type instance MapError 'WebPushErrorTooMany = 'StaticError 413 "too-many-web-push-subscriptions" "Too many web push subscriptions"
38 changes: 38 additions & 0 deletions libs/wire-api/src/Wire/API/PostgresMarshall.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ where
import Data.Aeson
import Data.Bifunctor (first)
import Data.ByteString qualified as BS
import Data.ByteString.Base64.URL qualified as B64U
import Data.ByteString.Conversion (toByteString')
import Data.ByteString.Conversion qualified as BSC
import Data.Code qualified as Code
Expand All @@ -49,6 +50,7 @@ import Hasql.Statement
import Imports
import SAML2.WebSSO qualified as SAML
import Wire.API.EnterpriseLogin
import Wire.API.Push.V2.WebSubscription

class PostgresMarshall db domain where
postgresMarshall :: domain -> db
Expand Down Expand Up @@ -536,6 +538,9 @@ instance PostgresMarshall Value Object where
instance PostgresMarshall Int64 Milliseconds where
postgresMarshall = msToInt64

instance PostgresMarshall Int64 Word64 where
postgresMarshall = fromIntegral

instance PostgresMarshall Text Domain where
postgresMarshall = domainText

Expand Down Expand Up @@ -590,6 +595,18 @@ instance PostgresMarshall Int32 TeamInviteTag where
instance PostgresMarshall UUID SAML.IdPId where
postgresMarshall = SAML.fromIdPId

instance PostgresMarshall Text EndpointUrl where
postgresMarshall = endpointUrlText

instance PostgresMarshall Text P256dhKey where
postgresMarshall = Text.decodeUtf8 . B64U.encodeUnpadded . p256dhKeyBytes

instance PostgresMarshall Text AuthSecret where
postgresMarshall = Text.decodeUtf8 . B64U.encodeUnpadded . authSecretBytes

instance PostgresMarshall ByteString ConnId where
postgresMarshall = fromConnId

---

class PostgresUnmarshall db domain where
Expand Down Expand Up @@ -1021,6 +1038,9 @@ instance (PostgresUnmarshall a b, Ord b) => PostgresUnmarshall (Vector a) (Set b
instance PostgresUnmarshall Int64 Milliseconds where
postgresUnmarshall = Right . int64ToMs

instance PostgresUnmarshall Int64 Word64 where
postgresUnmarshall = Right . fromIntegral

instance PostgresUnmarshall Text Code.Key where
postgresUnmarshall = mapLeft Text.pack . BSC.runParser BSC.parser . Text.encodeUtf8

Expand Down Expand Up @@ -1059,6 +1079,24 @@ instance PostgresUnmarshall UUID SAML.IdPId where
instance PostgresUnmarshall Text Handle where
postgresUnmarshall = mapLeft Text.pack . parseHandleEither

instance PostgresUnmarshall Text EndpointUrl where
postgresUnmarshall = mapLeft Text.pack . mkEndpointUrl

instance PostgresUnmarshall Text P256dhKey where
postgresUnmarshall t =
case B64U.decodeUnpadded (Text.encodeUtf8 t) of
Left e -> Left $ "Invalid base64url p256dh key: " <> Text.pack e
Right bs -> mapLeft Text.pack (mkP256dhKey bs)

instance PostgresUnmarshall Text AuthSecret where
postgresUnmarshall t =
case B64U.decodeUnpadded (Text.encodeUtf8 t) of
Left e -> Left $ "Invalid base64url auth secret: " <> Text.pack e
Right bs -> mapLeft Text.pack (mkAuthSecret bs)

instance PostgresUnmarshall ByteString ConnId where
postgresUnmarshall = Right . ConnId

instance PostgresUnmarshall UTCTime UTCTimeMillis where
postgresUnmarshall = Right . toUTCTimeMillis

Expand Down
20 changes: 20 additions & 0 deletions libs/wire-api/src/Wire/API/Push/V2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ module Wire.API.Push.V2
Token (..),
Transport (..),
AppName (..),

-- * WebPushSubscription (re-export)
WebPushSubscriptionList (..),
WebPushSubscription,
webPushSubscription,
wpsEndpoint,
wpsKeys,
wpsExpirationTime,
wpsClient,
EndpointUrl (..),
P256dhKey (..),
AuthSecret (..),
WebPushKeys (..),
AddWebPushError (..),
AddWebPushSuccess (..),
AddWebPushResponses,
DeleteWebPushRequest (..),
DeleteWebPushResponses,
VapidPublicKeyResponse (..),
)
where

Expand All @@ -86,6 +105,7 @@ import Imports
import Test.QuickCheck (oneof)
import Wire.API.Message (Priority (..))
import Wire.API.Push.V2.Token
import Wire.API.Push.V2.WebSubscription
import Wire.Arbitrary

-----------------------------------------------------------------------------
Expand Down
Loading