diff --git a/libs/wire-api/src/Wire/API/SystemSettings.hs b/libs/wire-api/src/Wire/API/SystemSettings.hs index b41a17acc59..c3860d69083 100644 --- a/libs/wire-api/src/Wire/API/SystemSettings.hs +++ b/libs/wire-api/src/Wire/API/SystemSettings.hs @@ -26,7 +26,7 @@ import Servant.OpenApi.Internal.Orphans () import Test.QuickCheck import Wire.Arbitrary --- | Subset of `Brig.Options.Settings` that is safe to be shown in public. +-- | Subset of `Wire.Options.Settings` that is safe to be shown in public. -- -- Used to expose settings via the @/system/settings/unauthorized@ endpoint. -- ALWAYS CHECK WITH SECURITY IF YOU WANT TO ADD SETTINGS HERE. diff --git a/libs/wire-subsystems/src/Wire/Options.hs b/libs/wire-subsystems/src/Wire/Options.hs new file mode 100644 index 00000000000..d3229c393f4 --- /dev/null +++ b/libs/wire-subsystems/src/Wire/Options.hs @@ -0,0 +1,629 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Wire.Options where + +import Amazonka (Region) +import Amazonka.Types (S3AddressingStyle) +import Data.Aeson (FromJSON (..), Value (..), parseJSON, withObject, (.:), (.:?)) +import Data.Aeson.TH (Options (..), defaultOptions, deriveFromJSON, deriveJSON) +import Data.Aeson.Types qualified as A +import Data.Char qualified as Char +import Data.Code qualified as Code +import Data.Domain (Domain (..)) +import Data.Id (ProviderId) +import Data.LanguageCodes (ISO639_1 (EN)) +import Data.Proxy (Proxy (..)) +import Data.Range (Range, toRange) +import Data.Text qualified as Text +import Data.Text.Encoding qualified as Text +import Data.Time (DiffTime, secondsToDiffTime) +import Database.Bloodhound.Types qualified as ES +import Hasql.Pool.Extended (PoolConfig) +import Imports +import Network.AMQP.Extended (AmqpEndpoint) +import Network.DNS qualified as DNS +import System.Logger.Extended (Level, LogFormat) +import Util.Options +import Util.Options.Common (toOptionFieldName) +import Util.Timeout (Timeout) +import Wire.API.Allowlists (AllowlistEmailDomains (..)) +import Wire.API.Routes.FederationDomainConfig (FederationDomainConfig (..), FederationRestriction (..), FederationStrategy) +import Wire.API.Team.FeatureFlags (FeatureFlags) +import Wire.API.User +import Wire.AuthenticationSubsystem.Config (ZAuthSettings) +import Wire.AuthenticationSubsystem.Cookie.Limit (CookieThrottle) +import Wire.EmailSending.SMTP (SMTPConnType (..)) +import Wire.EmailSubsystem.Template (TeamOpts) +import Wire.PostgresMigrationOpts (PostgresMigrationOpts) + +asciiOnly :: Text -> A.Parser ByteString +asciiOnly t = + if Text.all Char.isAscii t + then pure $ Text.encodeUtf8 t + else fail $ "Expected ascii string only, found: " <> Text.unpack t + +defaultLocale :: Locale +defaultLocale = Locale (Language EN) Nothing + +data WireConfig = WireConfig + { internalServices :: InternalServices, + externalServices :: ExternalServices, + settings :: WireSettings + } + +data InternalServices = InternalServices + { brig :: !Endpoint, + cargohold :: !Endpoint, + galley :: !Endpoint, + spar :: !Endpoint, + gundeck :: !Endpoint, + federatorInternal :: !(Maybe Endpoint), + backgroundWorker :: !Endpoint, + wireServerEnterprise :: !(Maybe Endpoint) + } + +data ExternalServices = ExternalServices + { cassandraBrig :: !CassandraOpts, + cassandraGalley :: !CassandraOpts, + cassandraGundeck :: !CassandraOpts, + cassandraSpar :: !CassandraOpts, + elasticsearch :: !ElasticSearchOpts, + redis :: !RedisEndpoint, + redisAdditionalWrite :: !(Maybe RedisEndpoint), + -- | Postgresql settings, the key values must be in libpq format. + -- https://www.postgresql.org/docs/17/libpq-connect.html#LIBPQ-PARAMKEYWORDS + postgresql :: !(Map Text Text), + postgresqlPassword :: !(Maybe FilePathSecrets), + postgresqlPool :: !PoolConfig, + rabbitmq :: !AmqpEndpoint, + email :: !EmailOpts, + prekeySelection :: !PrekeySelectionOpts, + -- TODO: See if user and team journal can even be configured differently. If + -- everything is supposed to be used by ibis, we cannot actually configure + -- them seperately anyway. + -- userJournal :: !(Maybe SqsOpts), + -- teamJournal :: !(Maybe SqsOpts), + sqs :: !SqsOpts, + assets :: !AssetOpts, + pushNotifications :: !PushNotifiactionOpts + } + +data RedisConnectionMode + = Master + | Cluster + deriving (Show, Generic) + +data RedisEndpoint = RedisEndpoint + { _host :: !Text, + _port :: !Word16, + _connectionMode :: !RedisConnectionMode, + _enableTls :: !Bool, + -- | When not specified, use system CA bundle + _tlsCa :: !(Maybe FilePath), + -- | When 'True', uses TLS but does not verify hostname or CA or validity of + -- the cert. Not recommended to set to 'True'. + _insecureSkipVerifyTls :: !Bool + } + deriving (Show, Generic) + +data PrekeySelectionOpts + = RandomPrekeySelection + | DynamoDBPrekeySelection !DynamoDBPrekeySelectionOpts + +data DynamoDBPrekeySelectionOpts = DynamoDBPrekeySelectionOpts + { dynamoDBEndpoint :: !AWSEndpoint, + tableName :: !Text + } + +data SqsOpts = SqsOpts + { sqsEndpoint :: !AWSEndpoint, + internalEventsQueue :: !Text, + userJournalQueue :: !(Maybe Text), + teamJournalQueue :: !(Maybe Text) + } + +data AssetOpts = AssetOpts + { s3Endpoint :: !AWSEndpoint, + -- | S3 can either by addressed in path style, i.e. + -- https:////, or vhost style, i.e. + -- https://./. AWS's S3 offering has + -- deprecated path style addressing for S3 and completely disabled it for + -- buckets created after 30 Sep 2020: + -- https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ + -- + -- However other object storage providers (specially self-deployed ones like + -- MinIO) may not support vhost style addressing yet (or ever?). Users of + -- such buckets should configure this option to "path". + -- + -- Installations using S3 service provided by AWS, should use "auto", this + -- option will ensure that vhost style is only used when it is possible to + -- construct a valid hostname from the bucket name and the bucket name + -- doesn't contain a '.'. Having a '.' in the bucket name causes TLS + -- validation to fail, hence it is not used by default. + -- + -- Using "virtual" as an option is only useful in situations where vhost + -- style addressing must be used even if it is not possible to construct a + -- valid hostname from the bucket name or the S3 service provider can ensure + -- correct certificate is issued for bucket which contain one or more '.'s + -- in the name. + -- + -- When this option is unspecified, we default to path style addressing to + -- ensure smooth transition for older deployments. + s3AddressingStyle :: !(Maybe OptS3AddressingStyle), + -- | S3 endpoint for generating download links. Useful if Cargohold is configured to use + -- an S3 replacement running inside the internal network (in which case internally we + -- would use one hostname for S3, and when generating an asset link for a client app, we + -- would use another hostname). + s3DownloadEndpoint :: !(Maybe AWSEndpoint), + s3Bucket :: !Text, + -- | Enable this option for compatibility with specific S3 backends. + s3Compatibility :: !(Maybe S3Compatibility), + cloudFront :: !(Maybe CloudFrontOpts), + -- | @Z-Host@ header to s3 download endpoint `Map` + -- + -- This logic is: If the @Z-Host@ header is provided and found in this map, + -- the map's values is taken as s3 download endpoint to redirect to; + -- otherwise a 404 is retuned. This option is only useful + -- in the context of multi-ingress setups where one backend / deployment is + -- reachable under several domains. + multiIngress :: !(Maybe (Map String AWSEndpoint)) + } + +newtype OptS3AddressingStyle = OptS3AddressingStyle + { unwrapS3AddressingStyle :: S3AddressingStyle + } + +data WireSettings = WireSettings + { users :: UserSettings, + search :: SearchSettings, + teams :: TeamSettings, + conversations :: ConversationSettings, + auth :: AuthSettings, + calling :: CallingSettings, + notifications :: NotificationSettings, + federation :: FederationSettings, + email :: EmailSettings, + featureFlags :: FeatureFlags, + assets :: AssetSettings, + bots :: BotSettings, + postgresMigration :: PostgresMigrationOpts, + logSettings :: LogSettings + } + +data SearchSettings = SearchSettings + { emailVisibility :: !EmailVisibilityConfig, + -- | When true, search only + -- returns users from the same team + searchSameTeamOnly :: !(Maybe Bool) + } + +data LogSettings = LogSettings + { logLevel :: !Level, + logFormat :: !(Maybe LogFormat) + } + +data UserSettings = UserSettings + { -- \| Activation timeout, in seconds + activationTimeout :: !Timeout, + -- | Default verification code timeout, in seconds + -- use `verificationTimeout` as the getter function which always provides a default value + verificationCodeTimeoutInternal :: !(Maybe Code.Timeout), + -- | Check for expired users every so often, in seconds + expiredUserCleanupTimeout :: !(Maybe Timeout), + -- | Whitelist of allowed emails/phones + allowlistEmailDomains :: !(Maybe AllowlistEmailDomains), + -- | Max. number of sent/accepted + -- connections per user + userMaxConnections :: !Int64, + -- | Max. number of permanent clients per user + userMaxPermClients :: !(Maybe Int), + suspendInactiveUsers :: !(Maybe SuspendInactiveUsers), + -- | Max size of rich info (number of chars in + -- field names and values), should be in sync + -- with Spar + richInfoLimit :: !Int, + -- | Default locale to use when selecting templates use + -- `defaultTemplateLocale` as the getter function which always provides a + -- default value. TODO: Merge this and next. + defaultTemplateLocaleInternal :: !(Maybe Locale), + -- | Default locale to use for users + -- use `defaultUserLocale` as the getter function which always provides a default value + defaultUserLocaleInternal :: !(Maybe Locale), + propertyMaxKeyLen :: !(Maybe Int64), + propertyMaxValueLen :: !(Maybe Int64), + -- | How long, in milliseconds, to wait in between processing delete events + -- from the internal delete queue + deleteThrottleMillis :: !(Maybe Int), + -- | The amount of time in milliseconds to wait after reading from an SQS queue + -- returns no message, before asking for messages from SQS again. + -- defaults to 'defSqsThrottleMillis'. + -- When using real SQS from AWS, throttling isn't needed as much, since using + -- >>> SQS.rmWaitTimeSeconds (Just 20) in Brig.AWS.listen + -- ensures that there is only one request every 20 seconds. + -- However, that parameter is not honoured when using fake-sqs + -- (where throttling can thus make sense) + sqsThrottleMillis :: !(Maybe Int), + -- | Do not allow certain user creation flows. + -- docs/reference/user/registration.md {#RefRestrictRegistration}. + restrictUserCreation :: !(Maybe Bool) + } + +-- TODO: Make this the actual default when the YAML doesn't specify a value. +defaultTemplateLocale :: UserSettings -> Locale +defaultTemplateLocale = fromMaybe defaultLocale . defaultTemplateLocaleInternal + +data TeamSettings = TeamSettings + { -- \| Team invitation timeout, in seconds + teamInvitationTimeout :: !Timeout, + -- | Max. # of members in a team. + maxTeamSize :: !Word32 + } + +data ConversationSettings = ConversationSettings + { -- | Max. # of members in a conversation. + maxConvSize :: !Word16 + } + +data AuthSettings = AuthSettings + { zauth :: !ZAuthOpts, + -- | Whether to allow plain HTTP transmission of cookies (for testing + -- purposes only) + cookieInsecure :: !Bool, + -- | Minimum age of a user cookie before it is renewed during token refresh + userCookieRenewAge :: !Integer, + -- | Max. # of cookies per user and cookie type + userCookieLimit :: !Int, + -- | Throttling tings (not to be confused with 'LoginRetryOpts') + userCookieThrottle :: !CookieThrottle, + -- | Block user from logging in for m minutes after n failed logins + limitFailedLogins :: !(Maybe LimitFailedLogins) + } + +data NotificationSettings = NotificationSettings {} + +data FederationSettings = FederationSettings + { -- \| FederationDomain is required, even when not wanting to federate with other backends + -- (in that case the 'federationStrategy' can be set to `allowNone` below, or to + -- `allowDynamic` while keeping the list of allowed domains empty, see + -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections) + -- Federation domain is used to qualify local IDs and handles, + -- e.g. 0c4d8944-70fa-480e-a8b7-9d929862d18c@wire.com and somehandle@wire.com. + -- It should also match the SRV DNS records under which other wire-server installations can find this backend: + -- >>> _wire-server-federator._tcp. + -- Once set, DO NOT change it: if you do, existing users may have a broken experience and/or stop working. + -- Remember to keep it the same in all services. + federationDomain :: !Domain, + -- | See https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections + -- default: AllowNone + federationStrategy :: !(Maybe FederationStrategy), + -- | 'federationDomainConfigs' is introduced in + -- https://github.com/wireapp/wire-server/pull/3260 for the sole purpose of transitioning + -- to dynamic federation remote configuration. See + -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections + -- for details. + -- default: [] + federationDomainConfigs :: !(Maybe [ImplicitNoFederationRestriction]), + -- | In seconds. Default: 10 seconds. Values <1 are silently replaced by 1. See + -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections + federationDomainConfigsUpdateFreq :: !(Maybe Int) + } + +data AssetSettings = AssetSettings {} + +data CallingSettings = CallingSettings + { turn :: !TurnOpts, + sft :: !(Maybe SFTOptions), + multiSFT :: !(Maybe Bool) + } + +data BotSettings = BotSettings + { -- \| Filter ONLY services with + -- the given provider id + providerSearchFilter :: !(Maybe ProviderId) + } + +data S3Compatibility + = -- | Scality RING, might also work for Zenko CloudServer + -- + S3CompatibilityScalityRing + +-- | AWS CloudFront settings. +data CloudFrontOpts = CloudFrontOpts + { -- | Domain + domain :: CFDomain, + -- | Keypair ID + keyPairId :: CFKeyPairId, + -- | Path to private key + privateKey :: FilePath + } + deriving (Show, Generic) + +-- TODO: This is copied from cargohold, dedupe +newtype CFKeyPairId = CFKeyPairId Text + deriving (Eq, Show, Generic) + +-- TODO: This is copied from cargohold, dedupe +newtype CFDomain = CFDomain Text + deriving (Eq, Show, Generic) + +data PushNotifiactionOpts = PushNotifiactionOpts + { -- \| AWS account + _account :: !Text, + -- | AWS region name + _region :: !Region, + -- | Environment name to scope ARNs to. TODO: Add explanation for on-prem operators. + _arnEnv :: !Text, + -- | SQS queue name + _queueName :: !Text, + _sqsEndpoint :: !AWSEndpoint, + _snsEndpoint :: !AWSEndpoint + } + +-- | Wraps 'FederationDomainConfig' with a 'FromJSON' instance that defaults +-- 'FederationRestriction' to 'FederationRestrictionAllowAll' when absent. +newtype ImplicitNoFederationRestriction = ImplicitNoFederationRestriction + {federationDomainConfig :: FederationDomainConfig} + deriving (Show, Eq, Generic) + +instance FromJSON ImplicitNoFederationRestriction where + parseJSON = + withObject + "ImplicitNoFederationRestriction" + ( \obj -> do + domain <- obj .: "domain" + searchPolicy <- obj .: "search_policy" + pure . ImplicitNoFederationRestriction $ + FederationDomainConfig domain searchPolicy FederationRestrictionAllowAll + ) + +-- --------------------------------------------------------------------------- +-- Types moved from Wire.Options +-- --------------------------------------------------------------------------- + +data ElasticSearchOpts = ElasticSearchOpts + { url :: !ES.Server, + index :: !ES.IndexName, + additionalWriteIndex :: !(Maybe ES.IndexName), + additionalWriteIndexUrl :: !(Maybe ES.Server), + credentials :: !(Maybe FilePathSecrets), + additionalCredentials :: !(Maybe FilePathSecrets), + insecureSkipVerifyTls :: Bool, + caCert :: Maybe FilePath, + additionalInsecureSkipVerifyTls :: Bool, + additionalCaCert :: Maybe FilePath + } + deriving (Show, Generic) + +instance FromJSON ElasticSearchOpts + +data EmailAWSOpts = EmailAWSOpts + { -- | Event feedback queue for SES (e.g. for email bounces and complaints) + sesQueue :: !Text, + -- | AWS SES endpoint + sesEndpoint :: !AWSEndpoint + } + deriving (Show, Generic) + +instance FromJSON EmailAWSOpts + +data EmailSMTPCredentials = EmailSMTPCredentials + { smtpUsername :: !Text, + smtpPassword :: !FilePathSecrets + } + deriving (Show, Generic) + +instance FromJSON EmailSMTPCredentials + +data EmailSMTPOpts = EmailSMTPOpts + { smtpEndpoint :: !Endpoint, + smtpCredentials :: !(Maybe EmailSMTPCredentials), + smtpConnType :: !SMTPConnType + } + deriving (Show, Generic) + +instance FromJSON EmailSMTPOpts + +data EmailOpts + = EmailAWS EmailAWSOpts + | EmailSMTP EmailSMTPOpts + deriving (Show, Generic) + +instance FromJSON EmailOpts where + parseJSON o = + EmailAWS + <$> parseJSON o + <|> EmailSMTP + <$> parseJSON o + +data BrandingOpts = BrandingOpts + { brand :: !Text, + brandUrl :: !Text, + brandLabelUrl :: !Text, + brandLogoUrl :: !Text, + brandService :: !Text, + copyright :: !Text, + misuse :: !Text, + legal :: !Text, + forgot :: !Text, + support :: !Text + } + deriving (Show, Generic) + +instance FromJSON BrandingOpts + +data EmailSMSGeneralOpts = EmailSMSGeneralOpts + { templateDir :: !FilePath, + emailSender :: !EmailAddress, + smsSender :: !Text, + templateBranding :: !BrandingOpts + } + deriving (Show, Generic) + +instance FromJSON EmailSMSGeneralOpts + +data EmailUserOpts = EmailUserOpts + { activationUrl :: !Text, + smsActivationUrl :: !Text, + passwordResetUrl :: !Text, + deletionUrl :: !Text + } + deriving (Show, Generic) + +instance FromJSON EmailUserOpts + +data ProviderOpts = ProviderOpts + { homeUrl :: !Text, + providerActivationUrl :: !Text, + approvalUrl :: !Text, + approvalTo :: !EmailAddress, + providerPwResetUrl :: !Text + } + deriving (Show, Generic) + +instance FromJSON ProviderOpts + +data EmailSettings = EmailSettings + { general :: !EmailSMSGeneralOpts, + user :: !EmailUserOpts, + provider :: !ProviderOpts, + team :: !TeamOpts + } + deriving (Show, Generic) + +instance FromJSON EmailSettings + +data LimitFailedLogins = LimitFailedLogins + { timeout :: !Timeout, + retryLimit :: !Int + } + deriving (Eq, Show, Generic) + +instance FromJSON LimitFailedLogins + +data SuspendInactiveUsers = SuspendInactiveUsers + { suspendTimeout :: !Timeout + } + deriving (Eq, Show, Generic) + +instance FromJSON SuspendInactiveUsers + +data ZAuthOpts = ZAuthOpts + { privateKeys :: !FilePath, + publicKeys :: !FilePath, + authSettings :: !ZAuthSettings + } + deriving (Show, Generic) + +instance FromJSON ZAuthOpts + +data TurnServersFiles = TurnServersFiles + { tsfServers :: !FilePath, + tsfServersV2 :: !FilePath + } + deriving (Show) + +instance FromJSON TurnServersFiles where + parseJSON = withObject "TurnServersFiles" $ \o -> + TurnServersFiles + <$> o .: "servers" + <*> o .: "serversV2" + +data TurnDnsOpts = TurnDnsOpts + { tdoBaseDomain :: DNS.Domain, + tdoDiscoveryIntervalSeconds :: !(Maybe DiffTime) + } + deriving (Show) + +instance FromJSON TurnDnsOpts where + parseJSON = withObject "TurnDnsOpts" $ \o -> + TurnDnsOpts + <$> (asciiOnly =<< o .: "baseDomain") + <*> o .:? "discoveryIntervalSeconds" + +data TurnServersSource + = TurnSourceDNS TurnDnsOpts + | TurnSourceFiles TurnServersFiles + deriving (Show) + +data TurnOpts = TurnOpts + { serversSource :: !TurnServersSource, + secret :: !FilePath, + tokenTTL :: !Word32, + configTTL :: !Word32 + } + deriving (Show) + +instance FromJSON TurnOpts where + parseJSON = withObject "TurnOpts" $ \o -> do + sourceName <- o .: "serversSource" + source <- + case sourceName of + "files" -> TurnSourceFiles <$> parseJSON (Object o) + "dns" -> TurnSourceDNS <$> parseJSON (Object o) + _ -> fail $ "TurnOpts: Invalid sourceType, expected one of [files, dns] but got: " <> Text.unpack sourceName + TurnOpts source + <$> o .: "secret" + <*> o .: "tokenTTL" + <*> o .: "configTTL" + +data SFTTokenOptions = SFTTokenOptions + { sttTTL :: !Word32, + sttSecret :: !FilePath + } + deriving (Show, Generic) + +instance FromJSON SFTTokenOptions where + parseJSON = withObject "SFTTokenOptions" $ \o -> + SFTTokenOptions + <$> o .: "ttl" + <*> o .: "secret" + +data SFTOptions = SFTOptions + { sftBaseDomain :: !DNS.Domain, + sftSRVServiceName :: !(Maybe ByteString), + sftDiscoveryIntervalSeconds :: !(Maybe DiffTime), + sftListLength :: !(Maybe (Range 1 100 Int)), + sftTokenOptions :: !(Maybe SFTTokenOptions) + } + deriving (Show, Generic) + +instance FromJSON SFTOptions where + parseJSON = withObject "SFTOptions" $ \o -> + SFTOptions + <$> (asciiOnly =<< o .: "sftBaseDomain") + <*> (mapM asciiOnly =<< o .:? "sftSRVServiceName") + <*> (fmap . fmap) secondsToDiffTime (o .:? "sftDiscoveryIntervalSeconds") + <*> o .:? "sftListLength" + <*> o .:? "sftToken" + +-- --------------------------------------------------------------------------- +-- TH splices — must come after all data declarations due to stage restrictions +-- --------------------------------------------------------------------------- + +deriveJSON defaultOptions {constructorTagModifier = map toLower} ''RedisConnectionMode + +deriveFromJSON toOptionFieldName ''RedisEndpoint + +defMaxKeyLen :: Int64 +defMaxKeyLen = 1024 + +defMaxValueLen :: Int64 +defMaxValueLen = 524288 + +defDeleteThrottleMillis :: Int +defDeleteThrottleMillis = 100 + +defSqsThrottleMillis :: Int +defSqsThrottleMillis = 500 + +defUserMaxPermClients :: Int +defUserMaxPermClients = 7 + +defSftServiceName :: ByteString +defSftServiceName = "_sft" + +defSrvDiscoveryIntervalSeconds :: DiffTime +defSrvDiscoveryIntervalSeconds = secondsToDiffTime 10 + +defSftListLength :: Range 1 100 Int +defSftListLength = toRange (Proxy @5) diff --git a/libs/wire-subsystems/src/Wire/Options/Galley.hs b/libs/wire-subsystems/src/Wire/Options/Galley.hs index 6b30fff0976..76d25b5bc64 100644 --- a/libs/wire-subsystems/src/Wire/Options/Galley.hs +++ b/libs/wire-subsystems/src/Wire/Options/Galley.hs @@ -221,7 +221,7 @@ data Opts = Opts _federator :: !(Maybe Endpoint), -- | RabbitMQ settings, required when federation is enabled. _rabbitmq :: !(Maybe AmqpEndpoint), - -- | Disco URL + -- | Disco URL. TODO: Remove _discoUrl :: !(Maybe Text), -- | Other settings _settings :: !Settings, diff --git a/libs/wire-subsystems/wire-subsystems.cabal b/libs/wire-subsystems/wire-subsystems.cabal index 0f4c739c004..c248ec180dc 100644 --- a/libs/wire-subsystems/wire-subsystems.cabal +++ b/libs/wire-subsystems/wire-subsystems.cabal @@ -116,6 +116,7 @@ common common-all , currency-codes , data-default , data-timeout + , dns , email-validate , errors , exceptions @@ -377,6 +378,7 @@ library Wire.MigrationLock Wire.NotificationSubsystem Wire.NotificationSubsystem.Interpreter + Wire.Options Wire.Options.Galley Wire.Options.Keys Wire.PaginationState diff --git a/services/brig/brig.cabal b/services/brig/brig.cabal index 70e95dcd393..ea130123aef 100644 --- a/services/brig/brig.cabal +++ b/services/brig/brig.cabal @@ -126,7 +126,6 @@ library Brig.IO.Journal Brig.IO.Logging Brig.Main - Brig.Options Brig.Provider.API Brig.Provider.DB Brig.Provider.Email @@ -257,7 +256,6 @@ library , imports , insert-ordered-containers , iproute >=1.5 - , iso639 >=0.1 , jose , jwt-tools , lens >=3.8 diff --git a/services/brig/src/Brig/API/Auth.hs b/services/brig/src/Brig/API/Auth.hs index b47366621fc..9b41894b42c 100644 --- a/services/brig/src/Brig/API/Auth.hs +++ b/services/brig/src/Brig/API/Auth.hs @@ -21,7 +21,7 @@ import Brig.API.Error import Brig.API.Handler import Brig.API.Types import Brig.App -import Brig.Options +import Wire.Options import Brig.User.Auth qualified as Auth import Control.Monad.Trans.Except import Data.CommaSeparatedList diff --git a/services/brig/src/Brig/API/Connection.hs b/services/brig/src/Brig/API/Connection.hs index 375a5300f93..bb11fcde9dd 100644 --- a/services/brig/src/Brig/API/Connection.hs +++ b/services/brig/src/Brig/API/Connection.hs @@ -42,7 +42,7 @@ import Brig.Data.Connection qualified as Data import Brig.Data.Types (resultHasMore, resultList) import Brig.IO.Intra qualified as Intra import Brig.IO.Logging -import Brig.Options +import Wire.Options import Control.Error import Control.Monad.Catch (throwM) import Data.Id as Id diff --git a/services/brig/src/Brig/API/Connection/Remote.hs b/services/brig/src/Brig/API/Connection/Remote.hs index 09d8e11e086..23613e04e82 100644 --- a/services/brig/src/Brig/API/Connection/Remote.hs +++ b/services/brig/src/Brig/API/Connection/Remote.hs @@ -28,7 +28,7 @@ import Brig.API.Types (ConnectionError (..)) import Brig.App import Brig.Data.Connection qualified as Data import Brig.IO.Intra qualified as Intra -import Brig.Options +import Wire.Options import Control.Comonad import Control.Error.Util ((??)) import Control.Monad.Trans.Except diff --git a/services/brig/src/Brig/API/Connection/Util.hs b/services/brig/src/Brig/API/Connection/Util.hs index ccbba0284c1..38cce87aa36 100644 --- a/services/brig/src/Brig/API/Connection/Util.hs +++ b/services/brig/src/Brig/API/Connection/Util.hs @@ -28,7 +28,7 @@ where import Brig.API.Types import Brig.App import Brig.Data.Connection qualified as Data -import Brig.Options (Settings (userMaxConnections)) +import Wire.Options (Settings (userMaxConnections)) import Control.Error (MaybeT, noteT) import Control.Monad.Trans.Except import Data.Id (UserId) diff --git a/services/brig/src/Brig/API/Federation.hs b/services/brig/src/Brig/API/Federation.hs index 3641348b490..6c87f4004a7 100644 --- a/services/brig/src/Brig/API/Federation.hs +++ b/services/brig/src/Brig/API/Federation.hs @@ -29,7 +29,7 @@ import Brig.API.User qualified as API import Brig.App import Brig.Data.Connection qualified as Data import Brig.IO.Intra (notify) -import Brig.Options +import Wire.Options import Brig.User.API.Handle import Brig.User.Search.SearchIndex qualified as Q import Control.Error.Util diff --git a/services/brig/src/Brig/API/Handler.hs b/services/brig/src/Brig/API/Handler.hs index 5eb9c2b4643..6c645577e93 100644 --- a/services/brig/src/Brig/API/Handler.hs +++ b/services/brig/src/Brig/API/Handler.hs @@ -33,7 +33,7 @@ import Brig.API.Error import Brig.AWS qualified as AWS import Brig.App import Brig.CanonicalInterpreter (BrigCanonicalEffects, runBrigToIO) -import Brig.Options (allowlistEmailDomains) +import Wire.Options (allowlistEmailDomains) import Control.Error import Control.Exception (throwIO) import Control.Monad.Catch (catches, throwM) diff --git a/services/brig/src/Brig/API/Internal.hs b/services/brig/src/Brig/API/Internal.hs index 6a6f17dbb80..5d5bb0bfc20 100644 --- a/services/brig/src/Brig/API/Internal.hs +++ b/services/brig/src/Brig/API/Internal.hs @@ -36,7 +36,7 @@ import Brig.Data.Activation import Brig.Data.Connection qualified as Data import Brig.Data.MLS.KeyPackage qualified as Data import Brig.Effects.UserPendingActivationStore (UserPendingActivationStore) -import Brig.Options hiding (internalEvents) +import Wire.Options hiding (internalEvents) import Brig.Provider.API qualified as Provider import Brig.Team.API qualified as Team import Brig.User.EJPD qualified diff --git a/services/brig/src/Brig/API/MLS/KeyPackages/Validation.hs b/services/brig/src/Brig/API/MLS/KeyPackages/Validation.hs index 45173c9eb66..9dea19ba8a6 100644 --- a/services/brig/src/Brig/API/MLS/KeyPackages/Validation.hs +++ b/services/brig/src/Brig/API/MLS/KeyPackages/Validation.hs @@ -26,7 +26,7 @@ where import Brig.API.Error import Brig.API.Handler import Brig.App -import Brig.Options +import Wire.Options import Control.Applicative import Data.ByteString qualified as LBS import Data.Qualified diff --git a/services/brig/src/Brig/API/MLS/Util.hs b/services/brig/src/Brig/API/MLS/Util.hs index d489be84592..129c52ae1c0 100644 --- a/services/brig/src/Brig/API/MLS/Util.hs +++ b/services/brig/src/Brig/API/MLS/Util.hs @@ -19,7 +19,7 @@ module Brig.API.MLS.Util where import Brig.API.Handler import Brig.App -import Brig.Options +import Wire.Options import Control.Error import Imports import Wire.ClientSubsystem.Error diff --git a/services/brig/src/Brig/API/OAuth.hs b/services/brig/src/Brig/API/OAuth.hs index 8738656b71b..37971c576cb 100644 --- a/services/brig/src/Brig/API/OAuth.hs +++ b/services/brig/src/Brig/API/OAuth.hs @@ -26,7 +26,7 @@ where import Brig.API.Error (throwStd) import Brig.API.Handler (Handler) import Brig.App -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Cassandra hiding (Set) import Cassandra qualified as C import Control.Error diff --git a/services/brig/src/Brig/API/Public.hs b/services/brig/src/Brig/API/Public.hs index b22f0ff6df6..b7c938a8535 100644 --- a/services/brig/src/Brig/API/Public.hs +++ b/services/brig/src/Brig/API/Public.hs @@ -47,7 +47,7 @@ import Brig.Effects.JwtTools (JwtTools) import Brig.Effects.PublicKeyBundle (PublicKeyBundle) import Brig.Effects.SFT import Brig.Effects.UserPendingActivationStore (UserPendingActivationStore) -import Brig.Options hiding (internalEvents) +import Wire.Options hiding (internalEvents) import Brig.Provider.API import Brig.Team.API qualified as Team import Brig.Template (InvitationUrlTemplates) diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index 4b6f1a4877c..0b13d96ac1b 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -74,7 +74,7 @@ import Brig.Effects.ConnectionStore import Brig.Effects.UserPendingActivationStore (UserPendingActivation (..), UserPendingActivationStore) import Brig.Effects.UserPendingActivationStore qualified as UserPendingActivationStore import Brig.IO.Intra qualified as Intra -import Brig.Options hiding (internalEvents) +import Wire.Options hiding (internalEvents) import Brig.User.Auth.Cookie qualified as Auth import Cassandra hiding (Set) import Control.Error diff --git a/services/brig/src/Brig/AWS.hs b/services/brig/src/Brig/AWS.hs index 5bcbf90ecd5..41cccd701b7 100644 --- a/services/brig/src/Brig/AWS.hs +++ b/services/brig/src/Brig/AWS.hs @@ -48,7 +48,6 @@ import Amazonka.DynamoDB qualified as DDB import Amazonka.SES qualified as SES import Amazonka.SQS qualified as SQS import Amazonka.SQS.Lens qualified as SQS -import Brig.Options qualified as Opt import Control.Exception.Lens import Control.Lens hiding ((.=)) import Control.Monad.Catch @@ -69,12 +68,13 @@ import UnliftIO.Async import UnliftIO.Exception import Util.Options import Wire.AWS (canRetry, sendCatch) +import Wire.Options qualified as Opt data Env = Env { _logger :: !Logger, _sesQueue :: !(Maybe Text), _userJournalQueue :: !(Maybe Text), - _prekeyTable :: !Text, + _prekeyTable :: !(Maybe Text), _amazonkaEnv :: !AWS.Env } @@ -99,20 +99,26 @@ newtype Amazon a = Amazon instance MonadLogger Amazon where log l m = view logger >>= \g -> Logger.log g l m -mkEnv :: Logger -> Opt.AWSOpts -> Maybe Opt.EmailAWSOpts -> Manager -> IO Env -mkEnv lgr opts emailOpts mgr = do +mkEnv :: + Logger -> + Opt.SqsOpts -> + Maybe Opt.DynamoDBPrekeySelectionOpts -> + Maybe Opt.EmailAWSOpts -> + Manager -> + IO Env +mkEnv lgr sqsOpts dynamoDBOpts emailOpts mgr = do let g = Logger.clone (Just "aws.brig") lgr - let pk = Opt.prekeyTable opts + let pk = Opt.tableName <$> dynamoDBOpts let sesEndpoint = mkEndpoint SES.defaultService . Opt.sesEndpoint <$> emailOpts - let dynamoEndpoint = mkEndpoint DDB.defaultService <$> Opt.dynamoDBEndpoint opts + let dynamoEndpoint = mkEndpoint DDB.defaultService . Opt.dynamoDBEndpoint <$> dynamoDBOpts e <- mkAwsEnv g sesEndpoint dynamoEndpoint - (mkEndpoint SQS.defaultService (Opt.sqsEndpoint opts)) + (mkEndpoint SQS.defaultService (Opt.sqsEndpoint sqsOpts)) sq <- maybe (pure Nothing) (fmap Just . getQueueUrl e . Opt.sesQueue) emailOpts - jq <- maybe (pure Nothing) (fmap Just . getQueueUrl e) (Opt.userJournalQueue opts) + jq <- maybe (pure Nothing) (fmap Just . getQueueUrl e) (Opt.userJournalQueue sqsOpts) pure (Env g sq jq pk e) where mkEndpoint svc e = AWS.setEndpoint (e ^. awsSecure) (e ^. awsHost) (e ^. awsPort) svc diff --git a/services/brig/src/Brig/App.hs b/services/brig/src/Brig/App.hs index 1e50c5179ed..5218a98b410 100644 --- a/services/brig/src/Brig/App.hs +++ b/services/brig/src/Brig/App.hs @@ -107,8 +107,8 @@ import Bilge.RPC (HasRequestId (..)) import Brig.AWS qualified as AWS import Brig.Calling qualified as Calling import Brig.DeleteQueue.Interpreter -import Brig.Options (ElasticSearchOpts, Opts, Settings (..)) -import Brig.Options qualified as Opt +import Wire.Options (ElasticSearchOpts, Opts, Settings (..)) +import Wire.Options qualified as Opt import Brig.Provider.Template import Brig.Queue.Stomp qualified as Stomp import Brig.Queue.Types diff --git a/services/brig/src/Brig/Calling.hs b/services/brig/src/Brig/Calling.hs index 8c1c2749d32..bc530073e62 100644 --- a/services/brig/src/Brig/Calling.hs +++ b/services/brig/src/Brig/Calling.hs @@ -48,8 +48,6 @@ module Brig.Calling ) where -import Brig.Options (SFTOptions (..), defSftListLength, defSftServiceName, defSrvDiscoveryIntervalSeconds) -import Brig.Options qualified as Opts import Control.Exception.Enclosed (handleAny) import Control.Lens import Control.Monad.Random.Class (MonadRandom) @@ -79,6 +77,8 @@ import UnliftIO.Async qualified as Async import Wire.API.Call.Config import Wire.Network.DNS.Effect import Wire.Network.DNS.SRV +import Wire.Options (SFTOptions (..), defSftListLength, defSftServiceName, defSrvDiscoveryIntervalSeconds) +import Wire.Options qualified as Opts import Wire.Sem.Delay import Wire.Sem.Logger.TinyLog diff --git a/services/brig/src/Brig/Calling/API.hs b/services/brig/src/Brig/Calling/API.hs index 78588a606d3..82d5a00411a 100644 --- a/services/brig/src/Brig/Calling/API.hs +++ b/services/brig/src/Brig/Calling/API.hs @@ -37,8 +37,8 @@ import Brig.Calling import Brig.Calling qualified as Calling import Brig.Calling.Internal import Brig.Effects.SFT -import Brig.Options (ListAllSFTServers (..)) -import Brig.Options qualified as Opt +import Wire.Options (ListAllSFTServers (..)) +import Wire.Options qualified as Opt import Control.Error (hush, throwE) import Control.Lens import Crypto.Hash qualified as Crypto diff --git a/services/brig/src/Brig/CanonicalInterpreter.hs b/services/brig/src/Brig/CanonicalInterpreter.hs index 83803ab3b1e..53190ebef92 100644 --- a/services/brig/src/Brig/CanonicalInterpreter.hs +++ b/services/brig/src/Brig/CanonicalInterpreter.hs @@ -28,8 +28,8 @@ import Brig.Effects.SFT (SFT, interpretSFT) import Brig.Effects.UserPendingActivationStore (UserPendingActivationStore) import Brig.Effects.UserPendingActivationStore.Cassandra (userPendingActivationStoreToCassandra) import Brig.IO.Intra (runEvents) -import Brig.Options (Settings (consumableNotifications), federationDomainConfigs, federationStrategy) -import Brig.Options qualified as Opt +import Wire.Options (Settings (consumableNotifications), federationDomainConfigs, federationStrategy) +import Wire.Options qualified as Opt import Brig.Template (InvitationUrlTemplates) import Brig.User.Search.Index (IndexEnv (..)) import Cassandra qualified as Cas diff --git a/services/brig/src/Brig/Data/MLS/KeyPackage.hs b/services/brig/src/Brig/Data/MLS/KeyPackage.hs index 20f95a40afa..138ccf8cc67 100644 --- a/services/brig/src/Brig/Data/MLS/KeyPackage.hs +++ b/services/brig/src/Brig/Data/MLS/KeyPackage.hs @@ -26,7 +26,7 @@ where import Brig.API.MLS.KeyPackages.Validation import Brig.App -import Brig.Options +import Wire.Options import Cassandra import Control.Arrow import Control.Error diff --git a/services/brig/src/Brig/Data/User.hs b/services/brig/src/Brig/Data/User.hs index fe176284211..39be9b8a7eb 100644 --- a/services/brig/src/Brig/Data/User.hs +++ b/services/brig/src/Brig/Data/User.hs @@ -26,7 +26,7 @@ module Brig.Data.User where import Brig.App -import Brig.Options +import Wire.Options import Control.Error import Data.Handle (Handle) import Data.Id diff --git a/services/brig/src/Brig/Index/Eval.hs b/services/brig/src/Brig/Index/Eval.hs index f2b1baa3d1f..d19b61389e9 100644 --- a/services/brig/src/Brig/Index/Eval.hs +++ b/services/brig/src/Brig/Index/Eval.hs @@ -23,7 +23,7 @@ where import Brig.App (initHttpManagerWithTLSConfig, mkIndexEnv) import Brig.Index.Options as IxOpts -import Brig.Options as Opt +import Wire.Options as Opt import Brig.User.Search.Index import Cassandra (ClientState) import Cassandra.Options diff --git a/services/brig/src/Brig/Index/Options.hs b/services/brig/src/Brig/Index/Options.hs index 8b0966d25f5..a0fecca5b9e 100644 --- a/services/brig/src/Brig/Index/Options.hs +++ b/services/brig/src/Brig/Index/Options.hs @@ -36,7 +36,7 @@ module Brig.Index.Options cKeyspace, PostgresSettings (..), localElasticSettings, - brigOptsToPostgresSettings, + wireConfigToPostgresSettings, localCassandraSettings, commandParser, mkCreateIndexSettings, @@ -49,7 +49,6 @@ module Brig.Index.Options where import Brig.Index.Types (CreateIndexSettings (..)) -import Brig.Options qualified as Opts import Cassandra qualified as C import Control.Lens import Data.Aeson as Aeson @@ -71,6 +70,7 @@ import Options.Applicative import URI.ByteString import URI.ByteString.QQ import Util.Options (CassandraOpts (..), Endpoint (..), FilePathSecrets) +import Wire.Options qualified as Opts data Command = Create ElasticSettings Endpoint @@ -168,12 +168,12 @@ localElasticSettings = _esDeleteTemplate = Nothing } -brigOptsToPostgresSettings :: Opts.Opts -> PostgresSettings -brigOptsToPostgresSettings opts = +wireConfigToPostgresSettings :: Opts.WireConfig -> PostgresSettings +wireConfigToPostgresSettings opts = PostgresSettings - { pool = opts.postgresqlPool, - passwordFile = opts.postgresqlPassword, - settings = opts.postgresql + { pool = opts.externalServices.postgresqlPool, + passwordFile = opts.externalServices.postgresqlPassword, + settings = opts.externalServices.postgresql } localCassandraSettings :: CassandraSettings diff --git a/services/brig/src/Brig/InternalEvent/Process.hs b/services/brig/src/Brig/InternalEvent/Process.hs index af018889184..41de186de40 100644 --- a/services/brig/src/Brig/InternalEvent/Process.hs +++ b/services/brig/src/Brig/InternalEvent/Process.hs @@ -22,7 +22,7 @@ import Brig.App import Brig.IO.Intra (rmClient) import Brig.IO.Intra qualified as Intra import Brig.InternalEvent.Types -import Brig.Options (defDeleteThrottleMillis, deleteThrottleMillis) +import Wire.Options (defDeleteThrottleMillis, deleteThrottleMillis) import Brig.Provider.API qualified as API import Control.Monad.Catch import Data.ByteString.Conversion diff --git a/services/brig/src/Brig/Main.hs b/services/brig/src/Brig/Main.hs index fc874c4ef2c..e96a2b0c8de 100644 --- a/services/brig/src/Brig/Main.hs +++ b/services/brig/src/Brig/Main.hs @@ -17,7 +17,7 @@ module Brig.Main where -import Brig.Options (Opts (postgresql)) +import Wire.Options (Opts (postgresql)) import Brig.Run import Data.Map qualified as Map import Data.Yaml qualified as Yaml diff --git a/services/brig/src/Brig/Options.hs b/services/brig/src/Brig/Options.hs deleted file mode 100644 index da75b90138a..00000000000 --- a/services/brig/src/Brig/Options.hs +++ /dev/null @@ -1,854 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE TemplateHaskell #-} -{-# OPTIONS_GHC -Wno-ambiguous-fields #-} --- Disabling to stop errors on Getters -{-# OPTIONS_GHC -Wno-redundant-constraints #-} - --- This file is part of the Wire Server implementation. --- --- Copyright (C) 2022 Wire Swiss GmbH --- --- 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 . - -module Brig.Options where - -import Brig.Queue.Types (QueueOpts (..)) -import Control.Applicative -import Control.Lens hiding (Level, element, enum) -import Data.Aeson -import Data.Aeson.Types qualified as A -import Data.Char qualified as Char -import Data.Code qualified as Code -import Data.Default -import Data.Domain (Domain (..)) -import Data.Id -import Data.LanguageCodes (ISO639_1 (EN)) -import Data.Misc (HttpsUrl) -import Data.Nonce -import Data.Range -import Data.Schema -import Data.Text qualified as Text -import Data.Text.Encoding qualified as Text -import Database.Bloodhound.Types qualified as ES -import Hasql.Pool.Extended -import Imports -import Network.AMQP.Extended -import Network.DNS qualified as DNS -import System.Logger.Extended (Level, LogFormat) -import Util.Options -import Util.SuffixNamer -import Util.Timeout -import Wire.API.Allowlists (AllowlistEmailDomains (..)) -import Wire.API.Routes.FederationDomainConfig -import Wire.API.Routes.Version -import Wire.API.Team.Feature -import Wire.API.User -import Wire.AuthenticationSubsystem.Config (ZAuthSettings) -import Wire.AuthenticationSubsystem.Cookie.Limit -import Wire.EmailSending.SMTP (SMTPConnType (..)) -import Wire.EmailSubsystem.Template (TeamOpts) -import Wire.PostgresMigrationOpts -import Wire.RateLimit.Interpreter - -data ElasticSearchOpts = ElasticSearchOpts - { -- | ElasticSearch URL - url :: !ES.Server, - -- | The name of the ElasticSearch user index - index :: !ES.IndexName, - -- | An additional index to write user data, useful while migrating to a new - -- index. - -- There is a bug hidden when using this option. Sometimes a user won't get - -- deleted from the index. Attempts at reproducing this issue in a simpler - -- environment have failed. As a workaround, there is a tool in - -- tools/db/find-undead which can be used to find the undead users right - -- after the migration, if they exist, we can run the reindexing to get data - -- in elasticsearch in a consistent state. - additionalWriteIndex :: !(Maybe ES.IndexName), - -- | An additional ES URL to write user data, useful while migrating to a - -- new instance of ES. It is necessary to provide 'additionalWriteIndex' for - -- this to be used. If this is 'Nothing' and 'additionalWriteIndex' is - -- configured, the 'url' field will be used. - additionalWriteIndexUrl :: !(Maybe ES.Server), - -- | Elasticsearch credentials - credentials :: !(Maybe FilePathSecrets), - -- | Credentials for additional ES index (maily used for migrations) - additionalCredentials :: !(Maybe FilePathSecrets), - insecureSkipVerifyTls :: Bool, - caCert :: Maybe FilePath, - additionalInsecureSkipVerifyTls :: Bool, - additionalCaCert :: Maybe FilePath - } - deriving (Show, Generic) - -instance FromJSON ElasticSearchOpts - -data AWSOpts = AWSOpts - { -- | Event journal queue for user events - -- (e.g. user deletion) - userJournalQueue :: !(Maybe Text), - -- | Dynamo table for storing prekey data - prekeyTable :: !Text, - -- | AWS SQS endpoint - sqsEndpoint :: !AWSEndpoint, - -- | DynamoDB endpoint - dynamoDBEndpoint :: !(Maybe AWSEndpoint) - } - deriving (Show, Generic) - -instance FromJSON AWSOpts - -data EmailAWSOpts = EmailAWSOpts - { -- | Event feedback queue for SES - -- (e.g. for email bounces and complaints) - sesQueue :: !Text, - -- | AWS SES endpoint - sesEndpoint :: !AWSEndpoint - } - deriving (Show, Generic) - -instance FromJSON EmailAWSOpts - -data EmailSMTPCredentials = EmailSMTPCredentials - { -- | Username to authenticate - -- against the SMTP server - smtpUsername :: !Text, - -- | File containing password to - -- authenticate against the SMTP server - smtpPassword :: !FilePathSecrets - } - deriving (Show, Generic) - -instance FromJSON EmailSMTPCredentials - -data EmailSMTPOpts = EmailSMTPOpts - { -- | Hostname of the SMTP server to connect to - smtpEndpoint :: !Endpoint, - smtpCredentials :: !(Maybe EmailSMTPCredentials), - -- | Which type of connection to use - -- against the SMTP server {tls,ssl,plain} - smtpConnType :: !SMTPConnType - } - deriving (Show, Generic) - -instance FromJSON EmailSMTPOpts - -data StompOpts = StompOpts - { host :: !Text, - port :: !Int, - tls :: !Bool - } - deriving (Show, Generic) - -data InternalEventsOpts = InternalEventsOpts - { internalEventsQueue :: !QueueOpts - } - deriving (Show) - -instance FromJSON InternalEventsOpts where - parseJSON = withObject "InternalEventsOpts" $ \o -> - InternalEventsOpts <$> parseJSON (Object o) - -data EmailSMSGeneralOpts = EmailSMSGeneralOpts - { -- | Email, SMS, ... template directory - templateDir :: !FilePath, - -- | Email sender address - emailSender :: !EmailAddress, - -- | Twilio sender identifier (sender phone number in E.104 format) - -- or twilio messaging sender ID - see - -- https://www.twilio.com/docs/sms/send-messages#use-an-alphanumeric-sender-id - smsSender :: !Text, - -- | Customizable branding text for - -- emails/sms/calls - templateBranding :: !BrandingOpts - } - deriving (Show, Generic) - -instance FromJSON EmailSMSGeneralOpts - -data BrandingOpts = BrandingOpts - { brand :: !Text, - brandUrl :: !Text, - brandLabelUrl :: !Text, - brandLogoUrl :: !Text, - brandService :: !Text, - copyright :: !Text, - misuse :: !Text, - legal :: !Text, - forgot :: !Text, - support :: !Text - } - deriving (Show, Generic) - -instance FromJSON BrandingOpts - -data EmailUserOpts = EmailUserOpts - { -- | Activation URL template - activationUrl :: !Text, - -- | SMS activation URL template - smsActivationUrl :: !Text, - -- | Password reset URL template - passwordResetUrl :: !Text, - -- | Deletion URL template - deletionUrl :: !Text - } - deriving (Show, Generic) - -instance FromJSON EmailUserOpts - --- | Provider settings -data ProviderOpts = ProviderOpts - { -- | Homepage URL - homeUrl :: !Text, - -- | Activation URL template - providerActivationUrl :: !Text, - -- | Approval URL template - approvalUrl :: !Text, - -- | Approval email recipient - approvalTo :: !EmailAddress, - -- | Password reset URL template - providerPwResetUrl :: !Text - } - deriving (Show, Generic) - -instance FromJSON ProviderOpts - -data EmailOpts - = EmailAWS EmailAWSOpts - | EmailSMTP EmailSMTPOpts - deriving (Show, Generic) - -instance FromJSON EmailOpts where - parseJSON o = - EmailAWS <$> parseJSON o - <|> EmailSMTP <$> parseJSON o - -data EmailSMSOpts = EmailSMSOpts - { email :: !EmailOpts, - general :: !EmailSMSGeneralOpts, - user :: !EmailUserOpts, - provider :: !ProviderOpts, - team :: !TeamOpts - } - deriving (Show, Generic) - -instance FromJSON EmailSMSOpts - --- | Login retry limit. In contrast to 'setUserCookieThrottle', this is not about mitigating --- DOS attacks, but about preventing dictionary attacks. This introduces the orthogonal risk --- of an attacker blocking legitimate login attempts of a user by constantly keeping the retry --- limit for that user exhausted with failed login attempts. --- --- If in doubt, do not ues retry options and worry about encouraging / enforcing a good --- password policy. -data LimitFailedLogins = LimitFailedLogins - { -- | Time the user is blocked when retry limit is reached (in - -- seconds mostly for making it easier to write a fast-ish - -- integration test.) - timeout :: !Timeout, - -- | Maximum number of failed login attempts for one user. - retryLimit :: !Int - } - deriving (Eq, Show, Generic) - -instance FromJSON LimitFailedLogins - -data SuspendInactiveUsers = SuspendInactiveUsers - { suspendTimeout :: !Timeout - } - deriving (Eq, Show, Generic) - -instance FromJSON SuspendInactiveUsers - --- | ZAuth options -data ZAuthOpts = ZAuthOpts - { -- | Private key file - privateKeys :: !FilePath, - -- | Public key file - publicKeys :: !FilePath, - -- | Other settings - authSettings :: !ZAuthSettings - } - deriving (Show, Generic) - -instance FromJSON ZAuthOpts - --- | TURN server options -data TurnOpts = TurnOpts - { -- | Where to get list of turn servers from - serversSource :: !TurnServersSource, - -- | TURN shared secret file path - secret :: !FilePath, - -- | For how long TURN credentials should be - -- valid, in seconds - tokenTTL :: !Word32, - -- | How long until a new TURN configuration - -- should be fetched, in seconds - configTTL :: !Word32 - } - deriving (Show) - -instance FromJSON TurnOpts where - parseJSON = withObject "TurnOpts" $ \o -> do - sourceName <- o .: "serversSource" - source <- - case sourceName of - "files" -> TurnSourceFiles <$> parseJSON (Object o) - "dns" -> TurnSourceDNS <$> parseJSON (Object o) - _ -> fail $ "TurnOpts: Invalid sourceType, expected one of [files, dns] but got: " <> Text.unpack sourceName - TurnOpts source - <$> o .: "secret" - <*> o .: "tokenTTL" - <*> o .: "configTTL" - -data TurnServersSource - = TurnSourceDNS TurnDnsOpts - | TurnSourceFiles TurnServersFiles - deriving (Show) - -data TurnServersFiles = TurnServersFiles - { tsfServers :: !FilePath, - tsfServersV2 :: !FilePath - } - deriving (Show) - -instance FromJSON TurnServersFiles where - parseJSON = withObject "TurnServersFiles" $ \o -> - TurnServersFiles - <$> o .: "servers" - <*> o .: "serversV2" - -data TurnDnsOpts = TurnDnsOpts - { tdoBaseDomain :: DNS.Domain, - tdoDiscoveryIntervalSeconds :: !(Maybe DiffTime) - } - deriving (Show) - -instance FromJSON TurnDnsOpts where - parseJSON = withObject "TurnDnsOpts" $ \o -> - TurnDnsOpts - <$> (asciiOnly =<< o .: "baseDomain") - <*> o .:? "discoveryIntervalSeconds" - -data ListAllSFTServers - = ListAllSFTServers - | HideAllSFTServers - deriving (Show, Eq, Ord) - deriving (FromJSON) via Schema ListAllSFTServers - -instance ToSchema ListAllSFTServers where - schema = - enum @Text $ - mconcat - [ element "enabled" ListAllSFTServers, - element "disabled" HideAllSFTServers - ] - --- | Options that are consumed on startup -data Opts = Opts - -- services - { -- | Host and port to bind to - brig :: !Endpoint, - -- | Cargohold address - cargohold :: !Endpoint, - -- | Galley address - galley :: !Endpoint, - -- | Spar address - spar :: !Endpoint, - -- | Gundeck address - gundeck :: !Endpoint, - -- | Federator address - federatorInternal :: !(Maybe Endpoint), - -- | Wire Server Enterprise address - wireServerEnterprise :: !(Maybe Endpoint), - -- external - - -- | Cassandra settings - cassandra :: !CassandraOpts, - -- | ElasticSearch settings - elasticsearch :: !ElasticSearchOpts, - -- | Postgresql settings, the key values must be in libpq format. - -- https://www.postgresql.org/docs/17/libpq-connect.html#LIBPQ-PARAMKEYWORDS - postgresql :: !(Map Text Text), - postgresqlPassword :: !(Maybe FilePathSecrets), - postgresqlPool :: !PoolConfig, - postgresMigration :: !PostgresMigrationOpts, - -- | SFT Federation - multiSFT :: !(Maybe Bool), - -- | RabbitMQ settings, required when federation is enabled. - rabbitmq :: !AmqpEndpoint, - -- | AWS settings - aws :: !AWSOpts, - -- | Enable Random Prekey Strategy - randomPrekeys :: !(Maybe Bool), - -- | STOMP broker settings - stompOptions :: !(Maybe StompOpts), - -- Email & SMS - - -- | Email and SMS settings - emailSMS :: !EmailSMSOpts, - -- ZAuth - - -- | ZAuth settings - zauth :: !ZAuthOpts, - -- Misc. - - -- | Disco URL - discoUrl :: !(Maybe Text), - -- | Event queue for - -- Brig-generated events (e.g. - -- user deletion) - internalEvents :: !InternalEventsOpts, - -- Logging - - -- | Log level (Debug, Info, etc) - logLevel :: !Level, - -- | Use netstrings encoding (see - -- ) - logNetStrings :: !(Maybe (Last Bool)), - -- | Logformat to use - -- TURN - logFormat :: !(Maybe (Last LogFormat)), - -- | TURN server settings - turn :: !TurnOpts, - -- | SFT Settings - sft :: !(Maybe SFTOptions), - -- | Runtime settings - settings :: !Settings - } - deriving (Show, Generic) - --- | Options that persist as runtime settings. -data Settings = Settings - { -- | Activation timeout, in seconds - activationTimeout :: !Timeout, - -- | Default verification code timeout, in seconds - -- use `verificationTimeout` as the getter function which always provides a default value - verificationCodeTimeoutInternal :: !(Maybe Code.Timeout), - -- | Team invitation timeout, in seconds - teamInvitationTimeout :: !Timeout, - -- | Check for expired users every so often, in seconds - expiredUserCleanupTimeout :: !(Maybe Timeout), - -- | STOMP broker credentials - stomp :: !(Maybe FilePathSecrets), - -- | Whitelist of allowed emails/phones - allowlistEmailDomains :: !(Maybe AllowlistEmailDomains), - -- | Max. number of sent/accepted - -- connections per user - userMaxConnections :: !Int64, - -- | Max. number of permanent clients per user - userMaxPermClients :: !(Maybe Int), - -- | Whether to allow plain HTTP transmission - -- of cookies (for testing purposes only) - cookieInsecure :: !Bool, - -- | Minimum age of a user cookie before - -- it is renewed during token refresh - userCookieRenewAge :: !Integer, - -- | Max. # of cookies per user and cookie type - userCookieLimit :: !Int, - -- | Throttling tings (not to be confused - -- with 'LoginRetryOpts') - userCookieThrottle :: !CookieThrottle, - -- | Block user from logging in - -- for m minutes after n failed - -- logins - limitFailedLogins :: !(Maybe LimitFailedLogins), - -- | If last cookie renewal is too long ago, - -- suspend the user. - suspendInactiveUsers :: !(Maybe SuspendInactiveUsers), - -- | Max size of rich info (number of chars in - -- field names and values), should be in sync - -- with Spar - richInfoLimit :: !Int, - -- | Default locale to use when selecting templates - -- use `defaultTemplateLocale` as the getter function which always provides a default value - defaultTemplateLocaleInternal :: !(Maybe Locale), - -- | Default locale to use for users - -- use `defaultUserLocale` as the getter function which always provides a default value - defaultUserLocaleInternal :: !(Maybe Locale), - -- | Max. # of members in a team. - -- NOTE: This must be in sync with galley - maxTeamSize :: !Word32, - -- | Max. # of members in a conversation. - -- NOTE: This must be in sync with galley - maxConvSize :: !Word16, - -- | Filter ONLY services with - -- the given provider id - providerSearchFilter :: !(Maybe ProviderId), - -- | Whether to expose user emails and to whom - emailVisibility :: !EmailVisibilityConfig, - propertyMaxKeyLen :: !(Maybe Int64), - propertyMaxValueLen :: !(Maybe Int64), - -- | How long, in milliseconds, to wait - -- in between processing delete events - -- from the internal delete queue - deleteThrottleMillis :: !(Maybe Int), - -- | When true, search only - -- returns users from the same team - searchSameTeamOnly :: !(Maybe Bool), - -- | FederationDomain is required, even when not wanting to federate with other backends - -- (in that case the 'federationStrategy' can be set to `allowNone` below, or to - -- `allowDynamic` while keeping the list of allowed domains empty, see - -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections) - -- Federation domain is used to qualify local IDs and handles, - -- e.g. 0c4d8944-70fa-480e-a8b7-9d929862d18c@wire.com and somehandle@wire.com. - -- It should also match the SRV DNS records under which other wire-server installations can find this backend: - -- >>> _wire-server-federator._tcp. - -- Once set, DO NOT change it: if you do, existing users may have a broken experience and/or stop working. - -- Remember to keep it the same in all services. - federationDomain :: !Domain, - -- | See https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections - -- default: AllowNone - federationStrategy :: !(Maybe FederationStrategy), - -- | 'federationDomainConfigs' is introduced in - -- https://github.com/wireapp/wire-server/pull/3260 for the sole purpose of transitioning - -- to dynamic federation remote configuration. See - -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections - -- for details. - -- default: [] - federationDomainConfigs :: !(Maybe [ImplicitNoFederationRestriction]), - -- | In seconds. Default: 10 seconds. Values <1 are silently replaced by 1. See - -- https://docs.wire.com/understand/federation/backend-communication.html#configuring-remote-connections - federationDomainConfigsUpdateFreq :: !(Maybe Int), - -- | The amount of time in milliseconds to wait after reading from an SQS queue - -- returns no message, before asking for messages from SQS again. - -- defaults to 'defSqsThrottleMillis'. - -- When using real SQS from AWS, throttling isn't needed as much, since using - -- >>> SQS.rmWaitTimeSeconds (Just 20) in Brig.AWS.listen - -- ensures that there is only one request every 20 seconds. - -- However, that parameter is not honoured when using fake-sqs - -- (where throttling can thus make sense) - sqsThrottleMillis :: !(Maybe Int), - -- | Do not allow certain user creation flows. - -- docs/reference/user/registration.md {#RefRestrictRegistration}. - restrictUserCreation :: !(Maybe Bool), - -- | The analog to `Galley.Options.featureFlags`. See 'AccountFeatureConfigs'. - featureFlags :: !(Maybe UserFeatureFlags), - -- | Customer extensions. Read 'CustomerExtensions' docs carefully! - customerExtensions :: !(Maybe CustomerExtensions), - -- | When set; instead of using SRV lookups to discover SFTs the calls - -- config will always return this entry. This is useful in Kubernetes - -- where SFTs are deployed behind a load-balancer. In the long-run the SRV - -- fetching logic can go away completely - sftStaticUrl :: !(Maybe HttpsUrl), - -- | When set the /calls/config/v2 endpoint will include all the - -- loadbalanced servers of `sftStaticUrl` under the @sft_servers_all@ - -- field. The default ting is to exclude and omit the field from the - -- response. - sftListAllServers :: Maybe ListAllSFTServers, - enableMLS :: Maybe Bool, - keyPackageMaximumLifetime :: Maybe NominalDiffTime, - -- | Disabled versions are not advertised and are completely disabled. - disabledAPIVersions :: !(Set VersionExp), - -- | Minimum delay in seconds between consecutive attempts to generate a new verification code. - -- use `2FACodeGenerationDelaySecs` as the getter function which always provides a default value - twoFACodeGenerationDelaySecsInternal :: !(Maybe Int), - -- | The time-to-live of a nonce in seconds. - -- use `nonceTtlSecs` as the getter function which always provides a default value - nonceTtlSecsInternal :: !(Maybe NonceTtlSecs), - -- | The maximum number of seconds of clock skew the implementation of generate_dpop_access_token in jwt-tools will allow - -- use `dpopMaxSkewSecs` as the getter function which always provides a default value - dpopMaxSkewSecsInternal :: !(Maybe Word16), - -- | The expiration time of a JWT DPoP token in seconds. - -- use `dpopTokenExpirationTimeSecs` as the getter function which always provides a default value - dpopTokenExpirationTimeSecsInternal :: !(Maybe Word64), - -- | Path to a .pem file containing the server's public key and private key - -- e.g. to sign JWT tokens - publicKeyBundle :: !(Maybe FilePath), - -- | Path to the public and private JSON web key pair used to sign OAuth access tokens - oAuthJwkKeyPair :: !(Maybe FilePath), - -- | The expiration time of an OAuth access token in seconds. - -- use `oAuthAccessTokenExpirationTimeSecs` as the getter function which always provides a default value - oAuthAccessTokenExpirationTimeSecsInternal :: !(Maybe Word64), - -- | The expiration time of an OAuth authorization code in seconds. - -- use `oAuthAuthorizationCodeExpirationTimeSecs` as the getter function which always provides a default value - oAuthAuthorizationCodeExpirationTimeSecsInternal :: !(Maybe Word64), - -- | En-/Disable OAuth - -- use `oAuthEnabled` as the getter function which always provides a default value - oAuthEnabledInternal :: !(Maybe Bool), - -- | The expiration time of an OAuth refresh token in seconds. - -- use `oAuthRefreshTokenExpirationTimeSecs` as the getter function which always provides a default value - oAuthRefreshTokenExpirationTimeSecsInternal :: !(Maybe Word64), - -- | The maximum number of active OAuth refresh tokens a user is allowed to have. - -- use `oAuthMaxActiveRefreshTokens` as the getter function which always provides a default value - oAuthMaxActiveRefreshTokensInternal :: !(Maybe Word32), - -- | Options to override the default Argon2id settings for specific operators. - passwordHashingOptions :: !(PasswordHashingOptions), - passwordHashingRateLimit :: !RateLimitConfig, - -- | Optional recipient email address for email domain registration audit logs - auditLogEmailRecipient :: !(Maybe EmailAddress), - -- | Time-to-live for new domain verification challenges, in seconds - challengeTTL :: !Timeout, - -- | Whether to allow ephemeral user creation - ephemeralUserCreationEnabled :: !Bool, - -- | Determines if this backend supports nomad profiles. - nomadProfiles :: !(Maybe Bool), - -- | Determines if consumable notifications are enabled - consumableNotifications :: !Bool - } - deriving (Show, Generic) - -newtype ImplicitNoFederationRestriction = ImplicitNoFederationRestriction - {federationDomainConfig :: FederationDomainConfig} - deriving (Show, Eq, Generic) - -instance FromJSON ImplicitNoFederationRestriction where - parseJSON = - withObject - "ImplicitNoFederationRestriction" - ( \obj -> do - domain <- obj .: "domain" - searchPolicy <- obj .: "search_policy" - pure . ImplicitNoFederationRestriction $ - FederationDomainConfig domain searchPolicy FederationRestrictionAllowAll - ) - -defaultLocale :: Locale -defaultLocale = Locale (Language EN) Nothing - -defaultUserLocale :: Settings -> Locale -defaultUserLocale = fromMaybe defaultLocale . defaultUserLocaleInternal - -defaultTemplateLocale :: Settings -> Locale -defaultTemplateLocale = fromMaybe defaultLocale . defaultTemplateLocaleInternal - -verificationTimeout :: Settings -> Code.Timeout -verificationTimeout = fromMaybe defVerificationTimeout . verificationCodeTimeoutInternal - where - defVerificationTimeout :: Code.Timeout - defVerificationTimeout = Code.Timeout (60 * 10) -- 10 minutes - -twoFACodeGenerationDelaySecs :: Settings -> Int -twoFACodeGenerationDelaySecs = fromMaybe def2FACodeGenerationDelaySecs . twoFACodeGenerationDelaySecsInternal - where - def2FACodeGenerationDelaySecs :: Int - def2FACodeGenerationDelaySecs = 5 * 60 -- 5 minutes - -nonceTtlSecs :: Settings -> NonceTtlSecs -nonceTtlSecs = fromMaybe defaultNonceTtlSecs . nonceTtlSecsInternal - where - defaultNonceTtlSecs :: NonceTtlSecs - defaultNonceTtlSecs = NonceTtlSecs $ 5 * 60 -- 5 minutes - -setDpopMaxSkewSecs :: Settings -> Word16 -setDpopMaxSkewSecs = fromMaybe defaultDpopMaxSkewSecs . dpopMaxSkewSecsInternal - where - defaultDpopMaxSkewSecs :: Word16 - defaultDpopMaxSkewSecs = 1 - -dpopTokenExpirationTimeSecs :: Settings -> Word64 -dpopTokenExpirationTimeSecs = fromMaybe defaultDpopTokenExpirationTimeSecs . dpopTokenExpirationTimeSecsInternal - where - defaultDpopTokenExpirationTimeSecs :: Word64 - defaultDpopTokenExpirationTimeSecs = 30 - -oAuthAccessTokenExpirationTimeSecs :: Settings -> Word64 -oAuthAccessTokenExpirationTimeSecs = fromMaybe defaultOAuthAccessTokenExpirationTimeSecs . oAuthAccessTokenExpirationTimeSecsInternal - where - defaultOAuthAccessTokenExpirationTimeSecs :: Word64 - defaultOAuthAccessTokenExpirationTimeSecs = 60 * 60 * 24 * 7 * 3 -- 3 weeks - -oAuthAuthorizationCodeExpirationTimeSecs :: Settings -> Word64 -oAuthAuthorizationCodeExpirationTimeSecs = fromMaybe defaultOAuthAuthorizationCodeExpirationTimeSecs . oAuthAuthorizationCodeExpirationTimeSecsInternal - where - defaultOAuthAuthorizationCodeExpirationTimeSecs :: Word64 - defaultOAuthAuthorizationCodeExpirationTimeSecs = 300 -- 5 minutes - -oAuthEnabled :: Settings -> Bool -oAuthEnabled = fromMaybe defaultOAuthEnabled . oAuthEnabledInternal - where - defaultOAuthEnabled :: Bool - defaultOAuthEnabled = False - -oAuthRefreshTokenExpirationTimeSecs :: Settings -> Word64 -oAuthRefreshTokenExpirationTimeSecs = fromMaybe defaultOAuthRefreshTokenExpirationTimeSecs . oAuthRefreshTokenExpirationTimeSecsInternal - where - defaultOAuthRefreshTokenExpirationTimeSecs :: Word64 - defaultOAuthRefreshTokenExpirationTimeSecs = 60 * 60 * 24 * 7 * 4 * 6 -- 24 weeks - -oAuthMaxActiveRefreshTokens :: Settings -> Word32 -oAuthMaxActiveRefreshTokens = fromMaybe defaultOAuthMaxActiveRefreshTokens . oAuthMaxActiveRefreshTokensInternal - where - defaultOAuthMaxActiveRefreshTokens :: Word32 - defaultOAuthMaxActiveRefreshTokens = 10 - --- | The analog to `FeatureFlags`. At the moment, only status flags for --- conferenceCalling are stored. -data UserFeatureFlags = UserFeatureFlags - { conferenceCalling :: UserFeature ConferenceCallingConfig - } - deriving (Eq, Ord, Show) - -instance FromJSON UserFeatureFlags where - parseJSON = withObject "UserFeatureFlags" $ \obj -> do - UserFeatureFlags - <$> obj .:? "conferenceCalling" .!= def - -data family UserFeature cfg - -data instance UserFeature ConferenceCallingConfig = ConferenceCallingUserStatus - { -- | This will be set as the status of the feature for newly created users. - forNew :: Maybe FeatureStatus, - -- | How an unset status for this feature should be interpreted. - forNull :: FeatureStatus - } - deriving (Eq, Ord, Show) - -instance Default (UserFeature ConferenceCallingConfig) where - def = ConferenceCallingUserStatus Nothing FeatureStatusEnabled - -instance FromJSON (UserFeature ConferenceCallingConfig) where - parseJSON = withObject "UserFeatureConferenceCalling" $ \obj -> do - ConferenceCallingUserStatus - <$> A.explicitParseFieldMaybe parseUserFeatureStatus obj "defaultForNew" - <*> A.explicitParseFieldMaybe parseUserFeatureStatus obj "defaultForNull" .!= forNull def - -parseUserFeatureStatus :: A.Value -> A.Parser FeatureStatus -parseUserFeatureStatus = withObject "UserFeatureStatus" $ \obj -> obj .: "status" - --- | Customer extensions naturally are covered by the AGPL like everything else, but use them --- at your own risk! If you use the default server config and do not set --- @customerExtensions@, none of this will have any effect. --- --- This is code implemented to comply with particular contracts. It may change or be removed --- at any point in the future without any further notice. -data CustomerExtensions = CustomerExtensions - { -- | When a `Domain` is blocked, users cannot use email addresses of this domain. - -- - -- This includes: - -- - Account activations - -- - Team invitations - -- - Changes of the account's email address - domainsBlockedForRegistration :: DomainsBlockedForRegistration - } - deriving (Show, FromJSON, Generic) - --- | See also: "Galley.API.CustomBackend", `galley.custom_backend`. -newtype DomainsBlockedForRegistration = DomainsBlockedForRegistration (HashSet Domain) - deriving newtype (Show, FromJSON) - -deriving stock instance Generic DomainsBlockedForRegistration - -data SFTOptions = SFTOptions - { sftBaseDomain :: !DNS.Domain, - sftSRVServiceName :: !(Maybe ByteString), -- defaults to defSftServiceName if unset - sftDiscoveryIntervalSeconds :: !(Maybe DiffTime), -- defaults to defSftDiscoveryIntervalSeconds - sftListLength :: !(Maybe (Range 1 100 Int)), -- defaults to defSftListLength - sftTokenOptions :: !(Maybe SFTTokenOptions) - } - deriving (Show, Generic) - -instance FromJSON SFTOptions where - parseJSON = withObject "SFTOptions" $ \o -> - SFTOptions - <$> (asciiOnly =<< o .: "sftBaseDomain") - <*> (mapM asciiOnly =<< o .:? "sftSRVServiceName") - <*> (secondsToDiffTime <$$> o .:? "sftDiscoveryIntervalSeconds") - <*> (o .:? "sftListLength") - <*> (o .:? "sftToken") - -data SFTTokenOptions = SFTTokenOptions - { sttTTL :: !Word32, - sttSecret :: !FilePath - } - deriving (Show, Generic) - -instance FromJSON SFTTokenOptions where - parseJSON = withObject "SFTTokenOptions" $ \o -> - SFTTokenOptions - <$> (o .: "ttl") - <*> (o .: "secret") - -asciiOnly :: Text -> A.Parser ByteString -asciiOnly t = - if Text.all Char.isAscii t - then pure $ Text.encodeUtf8 t - else fail $ "Expected ascii string only, found: " <> Text.unpack t - -defMaxKeyLen :: Int64 -defMaxKeyLen = 1024 - -defMaxValueLen :: Int64 -defMaxValueLen = 524288 - -defDeleteThrottleMillis :: Int -defDeleteThrottleMillis = 100 - -defSqsThrottleMillis :: Int -defSqsThrottleMillis = 500 - -defUserMaxPermClients :: Int -defUserMaxPermClients = 7 - -defSftServiceName :: ByteString -defSftServiceName = "_sft" - -defSrvDiscoveryIntervalSeconds :: DiffTime -defSrvDiscoveryIntervalSeconds = secondsToDiffTime 10 - -defSftListLength :: Range 1 100 Int -defSftListLength = unsafeRange 5 - --- | Convert a word to title case by capitalising the first letter -capitalise :: String -> String -capitalise [] = [] -capitalise (c : cs) = toUpper c : cs - -instance FromJSON Settings where - parseJSON = genericParseJSON customOptions - where - customOptions = - defaultOptions - { fieldLabelModifier = \case - "defaultUserLocaleInternal" -> "setDefaultUserLocale" - "defaultTemplateLocaleInternal" -> "setDefaultTemplateLocale" - "verificationCodeTimeoutInternal" -> "setVerificationTimeout" - "twoFACodeGenerationDelaySecsInternal" -> "set2FACodeGenerationDelaySecs" - "nonceTtlSecsInternal" -> "setNonceTtlSecs" - "dpopMaxSkewSecsInternal" -> "setDpopMaxSkewSecs" - "dpopTokenExpirationTimeSecsInternal" -> "setDpopTokenExpirationTimeSecs" - "oAuthAuthorizationCodeExpirationTimeSecsInternal" -> "setOAuthAuthorizationCodeExpirationTimeSecs" - "oAuthAccessTokenExpirationTimeSecsInternal" -> "setOAuthAccessTokenExpirationTimeSecs" - "oAuthEnabledInternal" -> "setOAuthEnabled" - "oAuthRefreshTokenExpirationTimeSecsInternal" -> "setOAuthRefreshTokenExpirationTimeSecs" - "oAuthMaxActiveRefreshTokensInternal" -> "setOAuthMaxActiveRefreshTokens" - other -> "set" <> capitalise other - } - -instance FromJSON Opts where - parseJSON = genericParseJSON customOptions - where - customOptions = - defaultOptions - { fieldLabelModifier = \case - "settings" -> "optSettings" - "stompOptions" -> "stomp" - other -> other - } - -instance FromJSON StompOpts where - parseJSON = genericParseJSON customOptions - where - customOptions = - defaultOptions - { fieldLabelModifier = \a -> "stom" <> capitalise a - } - -makeLensesWith (lensRules & lensField .~ suffixNamer) ''Opts - -makeLensesWith (lensRules & lensField .~ suffixNamer) ''Settings - -makeLensesWith (lensRules & lensField .~ suffixNamer) ''ElasticSearchOpts - -makeLensesWith (lensRules & lensField .~ suffixNamer) ''TurnOpts diff --git a/services/brig/src/Brig/Provider/API.hs b/services/brig/src/Brig/Provider/API.hs index 625cd27170a..cbf2c0f79ea 100644 --- a/services/brig/src/Brig/Provider/API.hs +++ b/services/brig/src/Brig/Provider/API.hs @@ -31,8 +31,8 @@ import Brig.API.Error import Brig.API.Handler import Brig.API.Types (PasswordResetError (..)) import Brig.App -import Brig.Options (Settings (..)) -import Brig.Options qualified as Opt +import Wire.Options (Settings (..)) +import Wire.Options qualified as Opt import Brig.Provider.DB (ServiceConn (..)) import Brig.Provider.DB qualified as DB import Brig.Provider.Email diff --git a/services/brig/src/Brig/Provider/Template.hs b/services/brig/src/Brig/Provider/Template.hs index 7de713abb89..f4a3a8fff51 100644 --- a/services/brig/src/Brig/Provider/Template.hs +++ b/services/brig/src/Brig/Provider/Template.hs @@ -25,7 +25,6 @@ module Brig.Provider.Template ) where -import Brig.Options import Data.ByteString.Conversion (fromByteString) import Data.Misc (HttpsUrl) import Data.Text.Encoding (encodeUtf8) @@ -34,6 +33,7 @@ import Imports import Wire.API.User.Identity import Wire.EmailSubsystem.Template hiding (readTemplate, readText) import Wire.EmailSubsystem.Templates.User +import Wire.Options data ProviderTemplates = ProviderTemplates { activationEmail :: !ActivationEmailTemplate, @@ -62,7 +62,7 @@ data ApprovalConfirmEmailTemplate = ApprovalConfirmEmailTemplate approvalConfirmEmailHomeUrl :: !HttpsUrl } -loadProviderTemplates :: Opts -> IO (Localised ProviderTemplates) +loadProviderTemplates :: WireConfig -> IO (Localised ProviderTemplates) loadProviderTemplates o = readLocalesDir defLocale (templateDir gOptions) "provider" $ \fp -> ProviderTemplates <$> ( ActivationEmailTemplate activationUrl' @@ -104,9 +104,9 @@ loadProviderTemplates o = readLocalesDir defLocale (templateDir gOptions) "provi ) where maybeUrl = fromByteString . encodeUtf8 $ pOptions.homeUrl - gOptions = o.emailSMS.general - pOptions = o.emailSMS.provider - defLocale = defaultTemplateLocale o.settings + gOptions = o.settings.email.general + pOptions = o.settings.email.provider + defLocale = defaultTemplateLocale o.settings.users readTemplate = readTemplateWithDefault gOptions.templateDir defLocale "provider" readText = readTextWithDefault gOptions.templateDir defLocale "provider" -- URL templates diff --git a/services/brig/src/Brig/Queue/Stomp.hs b/services/brig/src/Brig/Queue/Stomp.hs index 8fa9b04336a..d3237b37e4b 100644 --- a/services/brig/src/Brig/Queue/Stomp.hs +++ b/services/brig/src/Brig/Queue/Stomp.hs @@ -27,7 +27,7 @@ module Brig.Queue.Stomp where import BasePrelude hiding (Handler, throwIO) -import Brig.Options qualified as Opts +import Wire.Options qualified as Opts import Codec.MIME.Type qualified as MIME import Control.Monad.Catch (Handler (..), MonadMask) import Control.Retry hiding (retryPolicy) diff --git a/services/brig/src/Brig/Run.hs b/services/brig/src/Brig/Run.hs index 5b5d0f96818..831ca03ed77 100644 --- a/services/brig/src/Brig/Run.hs +++ b/services/brig/src/Brig/Run.hs @@ -31,7 +31,7 @@ import Brig.CanonicalInterpreter import Brig.Effects.UserPendingActivationStore (UserPendingActivation (UserPendingActivation), UserPendingActivationStore) import Brig.Effects.UserPendingActivationStore qualified as UsersPendingActivationStore import Brig.InternalEvent.Process qualified as Internal -import Brig.Options hiding (internalEvents, sesQueue) +import Wire.Options hiding (internalEvents, sesQueue) import Brig.Queue qualified as Queue import Brig.Version import Control.Concurrent.Async qualified as Async diff --git a/services/brig/src/Brig/Team/Template.hs b/services/brig/src/Brig/Team/Template.hs index caffd40e85d..8459967600b 100644 --- a/services/brig/src/Brig/Team/Template.hs +++ b/services/brig/src/Brig/Team/Template.hs @@ -23,17 +23,17 @@ module Brig.Team.Template ) where -import Brig.Options import Imports import Wire.EmailSubsystem.Template import Wire.EmailSubsystem.Templates.Team +import Wire.Options -- FUTUREWORK: This can be inlined once the `API.Template` have been migrated -- to wire-subsystem unit tests. -loadTeamTemplatesWithBrigOpts :: Opts -> IO (Localised TeamTemplates) +loadTeamTemplatesWithBrigOpts :: WireConfig -> IO (Localised TeamTemplates) loadTeamTemplatesWithBrigOpts o = loadTeamTemplates - o.emailSMS.team - o.emailSMS.general.templateDir - (defaultTemplateLocale o.settings) - (emailSender o.emailSMS.general) + o.settings.email.team + o.settings.email.general.templateDir + (defaultTemplateLocale o.settings.users) + o.settings.email.general.emailSender diff --git a/services/brig/src/Brig/Template.hs b/services/brig/src/Brig/Template.hs index 0b530261d05..d7b3a1706ab 100644 --- a/services/brig/src/Brig/Template.hs +++ b/services/brig/src/Brig/Template.hs @@ -25,7 +25,7 @@ module Brig.Template ) where -import Brig.Options +import Wire.Options import Data.Map.Strict qualified as Map import Data.Text.Template (Template) import Imports diff --git a/services/brig/src/Brig/User/API/Handle.hs b/services/brig/src/Brig/User/API/Handle.hs index 4ff8f0a2b11..aa79f0b152c 100644 --- a/services/brig/src/Brig/User/API/Handle.hs +++ b/services/brig/src/Brig/User/API/Handle.hs @@ -27,7 +27,7 @@ import Brig.API.Error (fedError) import Brig.API.Handler (Handler) import Brig.API.User qualified as API import Brig.App -import Brig.Options (searchSameTeamOnly) +import Wire.Options (searchSameTeamOnly) import Data.Handle (Handle, fromHandle) import Data.Id (UserId) import Data.Qualified diff --git a/services/brig/src/Brig/User/Auth.hs b/services/brig/src/Brig/User/Auth.hs index d00b6340196..444fa222577 100644 --- a/services/brig/src/Brig/User/Auth.hs +++ b/services/brig/src/Brig/User/Auth.hs @@ -37,7 +37,7 @@ import Brig.API.Types import Brig.API.User (changeSingleAccountStatus) import Brig.App import Brig.Budget -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Brig.User.Auth.Cookie import Cassandra import Control.Error hiding (bool) diff --git a/services/brig/src/Brig/User/Auth/Cookie.hs b/services/brig/src/Brig/User/Auth/Cookie.hs index 6288cf652e0..5a2b1a2da4b 100644 --- a/services/brig/src/Brig/User/Auth/Cookie.hs +++ b/services/brig/src/Brig/User/Auth/Cookie.hs @@ -39,7 +39,7 @@ module Brig.User.Auth.Cookie where import Brig.App -import Brig.Options hiding (user) +import Wire.Options hiding (user) import Cassandra import Control.Error import Control.Monad.Except diff --git a/services/brig/src/Brig/User/Client.hs b/services/brig/src/Brig/User/Client.hs index ed89f86db6d..233d77089f7 100644 --- a/services/brig/src/Brig/User/Client.hs +++ b/services/brig/src/Brig/User/Client.hs @@ -27,7 +27,7 @@ import Brig.Effects.JwtTools (JwtTools) import Brig.Effects.JwtTools qualified as JwtTools import Brig.Effects.PublicKeyBundle (PublicKeyBundle) import Brig.Effects.PublicKeyBundle qualified as PublicKeyBundle -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Error import Control.Monad.Trans.Except (except) import Data.ByteString (toStrict) diff --git a/services/brig/src/Brig/User/Template.hs b/services/brig/src/Brig/User/Template.hs index ea7193faadf..02dde2ffd63 100644 --- a/services/brig/src/Brig/User/Template.hs +++ b/services/brig/src/Brig/User/Template.hs @@ -17,13 +17,13 @@ module Brig.User.Template (loadUserTemplates) where -import Brig.Options qualified as Opt import Data.Text.Template import Imports import Wire.EmailSubsystem.Template hiding (readTemplate, readText) import Wire.EmailSubsystem.Templates.User +import Wire.Options qualified as Opt -loadUserTemplates :: Opt.Opts -> IO (Localised UserTemplates) +loadUserTemplates :: Opt.WireConfig -> IO (Localised UserTemplates) loadUserTemplates o = readLocalesDir defLocale templateDir "user" $ \fp -> UserTemplates <$> ( VerificationEmailTemplate activationUrl @@ -97,15 +97,15 @@ loadUserTemplates o = readLocalesDir defLocale templateDir "user" $ \fp -> <*> readText fp "email/sender.txt" ) where - gOptions = o.emailSMS.general - uOptions = o.emailSMS.user - tOptions = o.emailSMS.team + gOptions = o.settings.email.general + uOptions = o.settings.email.user + tOptions = o.settings.email.team emailSender = gOptions.emailSender activationUrl = template uOptions.activationUrl teamActivationUrl = template tOptions.tActivationUrl passwordResetUrl = template uOptions.passwordResetUrl deletionUserUrl = template uOptions.deletionUrl - defLocale = Opt.defaultTemplateLocale o.settings + defLocale = Opt.defaultTemplateLocale o.settings.users templateDir = gOptions.templateDir readTemplate = readTemplateWithDefault templateDir defLocale "user" readText = readTextWithDefault templateDir defLocale "user" diff --git a/services/brig/test/integration/API/Calling.hs b/services/brig/test/integration/API/Calling.hs index 1bbb92375a4..2a22aee6c65 100644 --- a/services/brig/test/integration/API/Calling.hs +++ b/services/brig/test/integration/API/Calling.hs @@ -22,7 +22,7 @@ module API.Calling where import Bilge import Bilge.Assert -import Brig.Options qualified as Opts +import Wire.Options qualified as Opts import Control.Lens (view, (.~), (?~), (^.)) import Control.Monad.Catch (MonadCatch) import Data.Bifunctor (Bifunctor (first)) diff --git a/services/brig/test/integration/API/Federation.hs b/services/brig/test/integration/API/Federation.hs index fe4173888eb..9d392670174 100644 --- a/services/brig/test/integration/API/Federation.hs +++ b/services/brig/test/integration/API/Federation.hs @@ -38,7 +38,7 @@ module API.Federation where import API.Search.Util (refreshIndex) import Bilge hiding (head) import Bilge.Assert -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Arrow (Arrow (first), (&&&)) import Control.Lens ((?~)) import Data.Aeson diff --git a/services/brig/test/integration/API/Metrics.hs b/services/brig/test/integration/API/Metrics.hs index cc93b37cca0..177a4864a8a 100644 --- a/services/brig/test/integration/API/Metrics.hs +++ b/services/brig/test/integration/API/Metrics.hs @@ -26,7 +26,7 @@ where import Bilge import Bilge.Assert -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Data.Attoparsec.Text import Data.ByteString.Conversion import Imports diff --git a/services/brig/test/integration/API/OAuth.hs b/services/brig/test/integration/API/OAuth.hs index 894a6a8f4be..88531fa80f5 100644 --- a/services/brig/test/integration/API/OAuth.hs +++ b/services/brig/test/integration/API/OAuth.hs @@ -39,8 +39,8 @@ import API.Team.Util qualified as Team import Bilge import Bilge.Assert import Brig.API.OAuth hiding (verifyRefreshToken) -import Brig.Options -import Brig.Options qualified as Opt +import Wire.Options +import Wire.Options qualified as Opt import Cassandra qualified as C import Control.Lens import Control.Monad.Catch (MonadCatch) diff --git a/services/brig/test/integration/API/Provider.hs b/services/brig/test/integration/API/Provider.hs index eb2753862d9..1e4d854c026 100644 --- a/services/brig/test/integration/API/Provider.hs +++ b/services/brig/test/integration/API/Provider.hs @@ -27,7 +27,7 @@ where import API.Team.Util qualified as Team import Bilge hiding (accept, head, timeout) import Bilge.Assert -import Brig.Options qualified as Opts +import Wire.Options qualified as Opts import Cassandra qualified as DB import Control.Arrow ((&&&)) import Control.Concurrent.Async qualified as Async diff --git a/services/brig/test/integration/API/Search.hs b/services/brig/test/integration/API/Search.hs index 966bec0f148..7a267f15124 100644 --- a/services/brig/test/integration/API/Search.hs +++ b/services/brig/test/integration/API/Search.hs @@ -40,9 +40,6 @@ import Brig.App (initHttpManagerWithTLSConfig) import Brig.Index.Eval (initIndex, runCommand) import Brig.Index.Options import Brig.Index.Options qualified as IndexOpts -import Brig.Options -import Brig.Options qualified as Opt -import Brig.Options qualified as Opts import Brig.User.Search.Index import Cassandra qualified as C import Cassandra.Options qualified as CassOpts @@ -89,6 +86,9 @@ import Wire.API.User.Search import Wire.API.User.Search qualified as Search import Wire.IndexedUserStore.ElasticSearch (mappingName) import Wire.IndexedUserStore.MigrationStore.ElasticSearch (defaultMigrationIndexName) +import Wire.Options +import Wire.Options qualified as Opt +import Wire.Options qualified as Opts tests :: Opt.Opts -> ES.Server -> Manager -> Galley -> Brig -> IO TestTree tests opts additionalElasticSearch mgr galley brig = do @@ -826,7 +826,7 @@ runReindexFromDatabase syncCommand logger opts newIndexName migrationIndexName = & IndexOpts.cPort .~ (opts.cassandra.endpoint.port) & IndexOpts.cKeyspace .~ (C.Keyspace opts.cassandra.keyspace) postgresSettings :: PostgresSettings = - brigOptsToPostgresSettings opts + wireConfigToPostgresSettings opts endpoint :: Endpoint = opts.galley in runCommand logger $ syncCommand elasticSettings cassandraSettings postgresSettings endpoint diff --git a/services/brig/test/integration/API/Settings.hs b/services/brig/test/integration/API/Settings.hs index 25a4cff565f..d1df786645d 100644 --- a/services/brig/test/integration/API/Settings.hs +++ b/services/brig/test/integration/API/Settings.hs @@ -20,8 +20,8 @@ module API.Settings (tests) where import API.Team.Util import Bilge hiding (accept, timeout) import Bilge.Assert -import Brig.Options (Opts) -import Brig.Options qualified as Opt +import Wire.Options (Opts) +import Wire.Options qualified as Opt import Control.Arrow ((&&&)) import Control.Lens import Data.Aeson diff --git a/services/brig/test/integration/API/Team.hs b/services/brig/test/integration/API/Team.hs index 66e1ead9e28..f787ab5724d 100644 --- a/services/brig/test/integration/API/Team.hs +++ b/services/brig/test/integration/API/Team.hs @@ -28,7 +28,7 @@ import API.User.Util as Util import Bilge hiding (accept, head, timeout) import Bilge qualified import Bilge.Assert -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Arrow ((&&&)) import Control.Lens hiding ((.=)) import Control.Monad.Catch (MonadCatch) diff --git a/services/brig/test/integration/API/TeamUserSearch.hs b/services/brig/test/integration/API/TeamUserSearch.hs index d6ddbecfbf9..9beb0665757 100644 --- a/services/brig/test/integration/API/TeamUserSearch.hs +++ b/services/brig/test/integration/API/TeamUserSearch.hs @@ -22,7 +22,7 @@ import API.Search.Util (executeTeamUserSearch, executeTeamUserSearchWithMaybeSta import API.Team.Util (createPopulatedBindingTeamWithNamesAndHandles) import API.User.Util (initiateEmailUpdateAutoActivate) import Bilge (Manager, MonadHttp) -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Monad.Catch (MonadCatch) import Control.Retry () import Data.ByteString.Conversion (toByteString) diff --git a/services/brig/test/integration/API/Template.hs b/services/brig/test/integration/API/Template.hs index 4c697b88ed5..d541943086f 100644 --- a/services/brig/test/integration/API/Template.hs +++ b/services/brig/test/integration/API/Template.hs @@ -1,7 +1,7 @@ module API.Template (tests) where import Bilge -import Brig.Options +import Wire.Options import Brig.Team.Template (loadTeamTemplatesWithBrigOpts) import Brig.Template import Brig.User.Template (loadUserTemplates) diff --git a/services/brig/test/integration/API/User.hs b/services/brig/test/integration/API/User.hs index 7c88c057abf..e8953c5f17f 100644 --- a/services/brig/test/integration/API/User.hs +++ b/services/brig/test/integration/API/User.hs @@ -31,7 +31,7 @@ import API.User.Util import Bilge hiding (accept, timeout) import Brig.AWS qualified as AWS import Brig.App (initZAuth) -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Cassandra qualified as DB import Data.Qualified import Imports diff --git a/services/brig/test/integration/API/User/Account.hs b/services/brig/test/integration/API/User/Account.hs index 796edb99a9e..2b65f7f8e41 100644 --- a/services/brig/test/integration/API/User/Account.hs +++ b/services/brig/test/integration/API/User/Account.hs @@ -29,7 +29,7 @@ import Bilge hiding (accept, timeout) import Bilge.Assert import Brig.AWS qualified as AWS import Brig.AWS.Types -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Arrow ((&&&)) import Control.Exception (throw) import Control.Lens (ix, preview, (^.), (^?)) diff --git a/services/brig/test/integration/API/User/Auth.hs b/services/brig/test/integration/API/User/Auth.hs index 9cd8047a67e..91b7231fb2a 100644 --- a/services/brig/test/integration/API/User/Auth.hs +++ b/services/brig/test/integration/API/User/Auth.hs @@ -28,7 +28,7 @@ import API.Team.Util import Bilge hiding (body) import Bilge qualified as Http import Bilge.Assert hiding (assert) -import Brig.Options qualified as Opts +import Wire.Options qualified as Opts import Cassandra hiding (Client, Value) import Cassandra qualified as DB import Control.Arrow ((&&&)) diff --git a/services/brig/test/integration/API/User/Client.hs b/services/brig/test/integration/API/User/Client.hs index 917c42cef19..9a32267bba8 100644 --- a/services/brig/test/integration/API/User/Client.hs +++ b/services/brig/test/integration/API/User/Client.hs @@ -30,7 +30,7 @@ import API.User.Util import API.User.Util qualified as Util import Bilge hiding (accept, head, timeout) import Bilge.Assert -import Brig.Options as Opt +import Wire.Options as Opt import Cassandra qualified as DB import Control.Lens hiding (Wrapped, (#)) import Crypto.JWT hiding (Ed25519, header, params) diff --git a/services/brig/test/integration/API/User/Handles.hs b/services/brig/test/integration/API/User/Handles.hs index 3ade2b74f90..7f07b14f182 100644 --- a/services/brig/test/integration/API/User/Handles.hs +++ b/services/brig/test/integration/API/User/Handles.hs @@ -43,7 +43,7 @@ import API.Team.Util import API.User.Util import Bilge hiding (accept, timeout) import Bilge.Assert -import Brig.Options qualified as Opt +import Wire.Options qualified as Opt import Control.Lens hiding (from, (#)) import Control.Monad.Catch (MonadCatch) import Data.Aeson diff --git a/services/brig/test/integration/API/User/RichInfo.hs b/services/brig/test/integration/API/User/RichInfo.hs index ace12126fd5..497011cb77b 100644 --- a/services/brig/test/integration/API/User/RichInfo.hs +++ b/services/brig/test/integration/API/User/RichInfo.hs @@ -25,8 +25,8 @@ import API.Team.Util (createTeamMember, createUserWithTeam) import API.User.Util import Bilge hiding (accept, timeout) import Bilge.Assert -import Brig.Options -import Brig.Options qualified as Opt +import Wire.Options +import Wire.Options qualified as Opt import Data.CaseInsensitive qualified as CI import Data.List.NonEmpty qualified as NonEmpty import Data.Text qualified as Text diff --git a/services/brig/test/integration/Federation/End2end.hs b/services/brig/test/integration/Federation/End2end.hs index b2b3f897c4b..d26a87cd96b 100644 --- a/services/brig/test/integration/Federation/End2end.hs +++ b/services/brig/test/integration/Federation/End2end.hs @@ -21,7 +21,7 @@ import API.MLS.Util import API.User.Util import Bilge import Bilge.Assert ((!!!), (