Skip to content
Merged
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
1 change: 1 addition & 0 deletions cassandra-schema.cql
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,7 @@ CREATE TABLE galley_test.team_features (
mls_e2eid_lock_status int,
mls_e2eid_status int,
mls_e2eid_ver_exp timestamp,
mls_lock_status int,
mls_migration_finalise_regardless_after timestamp,
mls_migration_lock_status int,
mls_migration_start_time timestamp,
Expand Down
14 changes: 14 additions & 0 deletions changelog.d/0-release-notes/WPB-5143
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
The mls team feature now has a lock status. If the current installation has default settings defined in wire-server's `values.yaml`, the `lockStatus` has to be added, e.g.:

```yaml
mls:
defaults:
status: enabled
config:
protocolToggleUsers: []
defaultProtocol: mls
allowedCipherSuites: [1]
defaultCipherSuite: 1
supportedProtocols: [proteus, mls] # must contain defaultProtocol
lockStatus: locked
```
1 change: 1 addition & 0 deletions changelog.d/2-features/WPB-5143
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The mls team feature now has a lock status
5 changes: 3 additions & 2 deletions charts/galley/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ config:
conversationCodeURI: null
#
# `multiIngress` is a `Z-Host` depended setting of conversationCodeURI.
# Use this only if you want to expose the instance on mutliple ingresses.
# Use this only if you want to expose the instance on multiple ingresses.
# If set it must a map from `Z-Host` to URI prefix
# Example:
# multiIngress:
# multiIngress:
# example.com: https://accounts.example.com/conversation-join/
# example.net: https://accounts.example.net/conversation-join/
multiIngress: null
Expand Down Expand Up @@ -83,6 +83,7 @@ config:
allowedCipherSuites: [1]
defaultCipherSuite: 1
supportedProtocols: [proteus, mls] # must contain defaultProtocol
lockStatus: unlocked
searchVisibilityInbound:
defaults:
status: disabled
Expand Down
25 changes: 13 additions & 12 deletions docs/src/developer/reference/config-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,17 +299,18 @@ If this feature is disabled then clients will use the Proteus protocol with this

The default configuration that applies to all teams that didn't explicitly change their feature configuration can be given in galley's `featureFlags` section in the config file:

```
```yaml
# galley.yaml
mls:
defaults:
status: disabled
status: enabled
config:
protocolToggleUsers: []
defaultProtocol: proteus
defaultProtocol: mls
allowedCipherSuites: [1]
defaultCipherSuite: 1

supportedProtocols: [proteus, mls] # must contain defaultProtocol
lockStatus: locked
```

This default configuration can be overriden on a per-team basis through the [feature config API](../developer/features.md)
Expand Down Expand Up @@ -748,7 +749,7 @@ to the configuration example above:

![Sequence Diagram: Alice and Bob download an asset](./multi-ingress-example-sequence.svg)

<!--
<!--
Unfortunately, kroki currently doesn't work on our CI: SQPIT-1810
Link to diagram:
https://mermaid.live/edit#pako:eNrdVbFu2zAQ_ZUDJ7ewDdhtUkBDgBRB0CHIYCNL4eVEnmWiMk8lKbttkH8vJbsW5dCOUXSqBkHiPT6-e3yinoVkRSITEC5H32syku40FhbXCwP7C6VnC1hqSQNL6l1XeWRPwBuKqxk8OXKwpRyrahxGxvQD11VJY8mvSHPOB4UlMknSrtonbcfStBVar6Wu0HjQJgCdGwUNKfaonMGMax8WeH9acIq5FXKOuwVE7BcqN4U2v9IlibbgFZcqXZ5_ABeMxYK6uiXpwRb5YHp1NYTJ9FN7ixw3jW6ri5UHXva28rZ5BsVbUzIqB-gc-WgTD9DRzU3Pz7v9FChZYnk8L4KGiW23Gdyz3aJVQW7IoYvQbT3gDq2_wsIIbpWCr6MvHF5WhIpsL2p6g6HFhHePvdajFR6Yv0Fd7ZTDquF9mj3AMoR2t0zHcZg1CiJj92akdGP-OLBJ9JpDFOa73YGNxnRAFZ3Te9rxey5L3gZHdmueMrsLyBnHDwpScerGQr_9dn1tzfFeR_2k2MioRFIn15MhTD82Sb0-ndT4fPjM-emcdsDItf23eVlSW_D_ltXYv0uzenTknU_rOd_fzOsfy_9xYvtN_21ixVCsya5Rq_D3fG6KC-FXtKaFyMKjoiXWpV-IhXkJUKw9z38aKTJvaxqKulKBff-jFdkSS0cvvwHKl250
Expand All @@ -761,21 +762,21 @@ For conversation invite links to be correct in a multi-ingress setup `settings.m
Example:

```yaml
multiIngress:
multiIngress:
red.example.com: https://accounts.red.example.com/conversation-join/
green.example.com: https://accounts.green.example.net/conversation-join/
```

### Webapp

The webapp runs its own web server (a NodeJS server) to serve static files and the webapp config (based on environment variables).
In a multi-ingress configuration, a single webapp instance will be deployed and be accessible from multiple domains (say `webapp.red.example.com` and `webapp.green.example.com`).
When the webapp is loaded from one of those domains it first does a request to the web server to get the config (that will give it, for example, the backend endpoint that it should hit).
The webapp runs its own web server (a NodeJS server) to serve static files and the webapp config (based on environment variables).
In a multi-ingress configuration, a single webapp instance will be deployed and be accessible from multiple domains (say `webapp.red.example.com` and `webapp.green.example.com`).
When the webapp is loaded from one of those domains it first does a request to the web server to get the config (that will give it, for example, the backend endpoint that it should hit).

Because of the single instance nature of the webapp, by default the configuration is static and the root url to the backend API can be set there (say `nginz-https.root.example.com`).
Because of the single instance nature of the webapp, by default the configuration is static and the root url to the backend API can be set there (say `nginz-https.root.example.com`).
In order to completely hide this root domain to the webapp, an environment variable can be set to allow the webapp hostname to be used to generate the API endpoint, team settings links, account page links and CSP headers.

The "hostname" is the result of the domain name minus the `webapp.` part of it.
The "hostname" is the result of the domain name minus the `webapp.` part of it.
So querying the webapp on `webapp.red.example.com` will resolve to `red.example.com`.

To enable dynamic hostname replacement, first set this variable:
Expand All @@ -784,7 +785,7 @@ To enable dynamic hostname replacement, first set this variable:
ENABLE_DYNAMIC_HOSTNAME="true"
```

Then, any other variable that will contain the string `[[hostname]]` will be replaced by the hostname of the running webapp. (eg. if a webapp is running on `webapp.red.example.com` then any occurrence of `[[hostname]]` in the config will be replaced by `red.example.com`).
Then, any other variable that will contain the string `[[hostname]]` will be replaced by the hostname of the running webapp. (eg. if a webapp is running on `webapp.red.example.com` then any occurrence of `[[hostname]]` in the config will be replaced by `red.example.com`).

You may use the template variable `[[hostname]]` in any environment variable to not provide (reveal) actual domain names.

Expand Down
4 changes: 2 additions & 2 deletions libs/galley-types/src/Galley/Types/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ data FeatureFlags = FeatureFlags
_flagsTeamFeatureValidateSAMLEmailsStatus :: !(Defaults (ImplicitLockStatus ValidateSAMLEmailsConfig)),
_flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)),
_flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)),
_flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)),
_flagMLS :: !(Defaults (WithStatus MLSConfig)),
_flagOutlookCalIntegration :: !(Defaults (WithStatus OutlookCalIntegrationConfig)),
_flagMlsE2EId :: !(Defaults (WithStatus MlsE2EIdConfig)),
_flagMlsMigration :: !(Defaults (WithStatus MlsMigrationConfig))
Expand Down Expand Up @@ -214,7 +214,7 @@ instance FromJSON FeatureFlags where
<*> withImplicitLockStatusOrDefault obj "validateSAMLEmails"
<*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge"))
<*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound"
<*> withImplicitLockStatusOrDefault obj "mls"
<*> (fromMaybe (Defaults (defFeatureStatus @MLSConfig)) <$> (obj .:? "mls"))
<*> (fromMaybe (Defaults (defFeatureStatus @OutlookCalIntegrationConfig)) <$> (obj .:? "outlookCalIntegration"))
<*> (fromMaybe (Defaults (defFeatureStatus @MlsE2EIdConfig)) <$> (obj .:? "mlsE2EId"))
<*> (fromMaybe (Defaults (defFeatureStatus @MlsMigrationConfig)) <$> (obj .:? "mlsMigration"))
Expand Down
2 changes: 1 addition & 1 deletion libs/galley-types/test/unit/Test/Galley/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ instance Arbitrary FeatureFlags where
<*> fmap (fmap unlocked) arbitrary
<*> arbitrary
<*> fmap (fmap unlocked) arbitrary
<*> fmap (fmap unlocked) arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/src/Wire/API/Routes/Internal/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ type IFeatureAPI =
:<|> IFeatureStatusGet MLSConfig
:<|> IFeatureStatusPut '[] '() MLSConfig
:<|> IFeatureStatusPatch '[] '() MLSConfig
:<|> IFeatureStatusLockStatusPut MLSConfig
-- ExposeInvitationURLsToTeamAdminConfig
:<|> IFeatureStatusGet ExposeInvitationURLsToTeamAdminConfig
:<|> IFeatureStatusPut '[] '() ExposeInvitationURLsToTeamAdminConfig
Expand Down
1 change: 1 addition & 0 deletions services/galley/galley.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ library
Galley.Schema.V86_TeamFeatureMlsMigration
Galley.Schema.V87_TeamFeatureSupportedProtocols
Galley.Schema.V88_RemoveMemberClientAndTruncateMLSGroupMemberClient
Galley.Schema.V89_MlsLockStatus
Galley.Types.Clients
Galley.Types.ToUserRole
Galley.Types.UserList
Expand Down
1 change: 1 addition & 0 deletions services/galley/src/Galley/API/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ featureAPI =
<@> mkNamedAPI @'("iget", MLSConfig) (getFeatureStatus DontDoAuth)
<@> mkNamedAPI @'("iput", MLSConfig) setFeatureStatusInternal
<@> mkNamedAPI @'("ipatch", MLSConfig) patchFeatureStatusInternal
<@> mkNamedAPI @'("ilock", MLSConfig) (updateLockStatus @MLSConfig)
<@> mkNamedAPI @'("iget", ExposeInvitationURLsToTeamAdminConfig) (getFeatureStatus DontDoAuth)
<@> mkNamedAPI @'("iput", ExposeInvitationURLsToTeamAdminConfig) setFeatureStatusInternal
<@> mkNamedAPI @'("ipatch", ExposeInvitationURLsToTeamAdminConfig) patchFeatureStatusInternal
Expand Down
5 changes: 4 additions & 1 deletion services/galley/src/Galley/API/Teams/Features.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,13 @@ patchFeatureStatusInternal ::
WithStatusPatch cfg ->
Sem r (WithStatus cfg)
patchFeatureStatusInternal tid patch = do
assertTeamExists tid
currentFeatureStatus <- getFeatureStatus @cfg DontDoAuth tid
let newFeatureStatus = applyPatch currentFeatureStatus
-- setting the config can fail, so we need to do it first
void $ setConfigForTeam @cfg tid (forgetLock newFeatureStatus)
when (isJust $ wspLockStatus patch) $ void $ updateLockStatus @cfg tid (wsLockStatus newFeatureStatus)
setConfigForTeam @cfg tid (forgetLock newFeatureStatus)
getFeatureStatus @cfg DontDoAuth tid
where
applyPatch :: WithStatus cfg -> WithStatus cfg
applyPatch current =
Expand Down
2 changes: 1 addition & 1 deletion services/galley/src/Galley/API/Teams/Features/Get.hs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ instance GetFeatureConfig SearchVisibilityInboundConfig where

instance GetFeatureConfig MLSConfig where
getConfigForServer =
input <&> view (settings . featureFlags . flagMLS . unDefaults . unImplicitLockStatus)
input <&> view (settings . featureFlags . flagMLS . unDefaults)

instance GetFeatureConfig ExposeInvitationURLsToTeamAdminConfig where
getConfigForTeam tid = do
Expand Down
2 changes: 1 addition & 1 deletion services/galley/src/Galley/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ validateOptions o = do
(Nothing, Just _) -> error "RabbitMQ config is specified and federator is not, please specify both or none"
(Just _, Nothing) -> error "Federator is specified and RabbitMQ config is not, please specify both or none"
_ -> pure ()
let mlsFlag = settings' ^. featureFlags . Teams.flagMLS . Teams.unDefaults . Teams.unImplicitLockStatus
let mlsFlag = settings' ^. featureFlags . Teams.flagMLS . Teams.unDefaults
mlsConfig = wsConfig mlsFlag
migrationStatus = wsStatus $ settings' ^. featureFlags . Teams.flagMlsMigration . Teams.unDefaults
when (migrationStatus == FeatureStatusEnabled && ProtocolMLSTag `notElem` mlsSupportedProtocols mlsConfig) $
Expand Down
2 changes: 2 additions & 0 deletions services/galley/src/Galley/Cassandra/TeamFeatures.hs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ getFeatureLockStatus FeatureSingletonSndFactorPasswordChallengeConfig tid = getL
getFeatureLockStatus FeatureSingletonMlsE2EIdConfig tid = getLockStatusC "mls_e2eid_lock_status" tid
getFeatureLockStatus FeatureSingletonMlsMigration tid = getLockStatusC "mls_migration_lock_status" tid
getFeatureLockStatus FeatureSingletonOutlookCalIntegrationConfig tid = getLockStatusC "outlook_cal_integration_lock_status" tid
getFeatureLockStatus FeatureSingletonMLSConfig tid = getLockStatusC "mls_lock_status" tid
getFeatureLockStatus _ _ = pure Nothing

setFeatureLockStatus :: MonadClient m => FeatureSingleton cfg -> TeamId -> LockStatus -> m ()
Expand All @@ -265,6 +266,7 @@ setFeatureLockStatus FeatureSingletonSndFactorPasswordChallengeConfig tid status
setFeatureLockStatus FeatureSingletonMlsE2EIdConfig tid status = setLockStatusC "mls_e2eid_lock_status" tid status
setFeatureLockStatus FeatureSingletonMlsMigration tid status = setLockStatusC "mls_migration_lock_status" tid status
setFeatureLockStatus FeatureSingletonOutlookCalIntegrationConfig tid status = setLockStatusC "outlook_cal_integration_lock_status" tid status
setFeatureLockStatus FeatureSingletonMLSConfig tid status = setLockStatusC "mls_lock_status" tid status
setFeatureLockStatus _ _tid _status = pure ()

getTrivialConfigC ::
Expand Down
4 changes: 3 additions & 1 deletion services/galley/src/Galley/Schema/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import Galley.Schema.V85_MLSDraft17 qualified as V85_MLSDraft17
import Galley.Schema.V86_TeamFeatureMlsMigration qualified as V86_TeamFeatureMlsMigration
import Galley.Schema.V87_TeamFeatureSupportedProtocols qualified as V87_TeamFeatureSupportedProtocols
import Galley.Schema.V88_RemoveMemberClientAndTruncateMLSGroupMemberClient qualified as V88_RemoveMemberClientAndTruncateMLSGroupMemberClient
import Galley.Schema.V89_MlsLockStatus qualified as V89_MlsLockStatus
import Imports
import Options.Applicative
import System.Logger.Extended qualified as Log
Expand Down Expand Up @@ -177,7 +178,8 @@ migrations =
V85_MLSDraft17.migration,
V86_TeamFeatureMlsMigration.migration,
V87_TeamFeatureSupportedProtocols.migration,
V88_RemoveMemberClientAndTruncateMLSGroupMemberClient.migration
V88_RemoveMemberClientAndTruncateMLSGroupMemberClient.migration,
V89_MlsLockStatus.migration
-- FUTUREWORK: once #1726 has made its way to master/production,
-- the 'message' field in connections table can be dropped.
-- See also https://github.com/wireapp/wire-server/pull/1747/files
Expand Down
33 changes: 33 additions & 0 deletions services/galley/src/Galley/Schema/V89_MlsLockStatus.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2023 Wire Swiss GmbH <[email protected]>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.
module Galley.Schema.V89_MlsLockStatus
( migration,
)
where

import Cassandra.Schema
import Imports
import Text.RawString.QQ

migration :: Migration
migration =
Migration 89 "Add lock status for MLSConfig" $
schema'
[r| ALTER TABLE team_features ADD (
mls_lock_status int
)
|]
18 changes: 16 additions & 2 deletions services/galley/test/integration/API/Teams/Feature.hs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ tests s =
testPatch IgnoreLockStatusChange FeatureStatusEnabled SearchVisibilityAvailableConfig,
test s (unpack $ featureNameBS @MLSConfig) $
testPatchWithCustomGen
IgnoreLockStatusChange
FeatureStatusEnabled
AssertLockStatusChange
FeatureStatusDisabled
( MLSConfig
[]
ProtocolProteusTag
Expand Down Expand Up @@ -1278,6 +1278,10 @@ testMLS = do
setForTeamInternal :: HasCallStack => WithStatusNoLock MLSConfig -> TestM ()
setForTeamInternal = setForTeamInternalWithStatusCode expect2xx

setLockStatus :: HasCallStack => LockStatus -> TestM ()
setLockStatus lockStatus =
Util.setLockStatusInternal @MLSConfig galley tid lockStatus !!! statusCode === const 200

let cipherSuite = MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
defaultConfig =
WithStatusNoLock
Expand All @@ -1302,13 +1306,23 @@ testMLS = do

getViaEndpoints defaultConfig

-- when the feature is locked it cannot be changed
setLockStatus LockStatusLocked
setForTeamWithStatusCode 409 config2
setLockStatus LockStatusUnlocked

WS.bracketR cannon member $ \ws -> do
setForTeam config2
void . liftIO $
WS.assertMatch (5 # Second) ws $
wsAssertFeatureConfigUpdate @MLSConfig config2 LockStatusUnlocked
getViaEndpoints config2

-- when the feature is locked the default config is returned
setLockStatus LockStatusLocked
getViaEndpoints defaultConfig
setLockStatus LockStatusUnlocked

WS.bracketR cannon member $ \ws -> do
setForTeamWithStatusCode 400 invalidConfig
void . liftIO $
Expand Down