From 221210370f05fc9370a299bc4a157f814aaa5626 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 14:05:09 +0200 Subject: [PATCH 01/66] libs/wire-api: add optional URL field to Invitations. --- libs/wire-api/src/Wire/API/Team/Invitation.hs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Invitation.hs b/libs/wire-api/src/Wire/API/Team/Invitation.hs index 4476698097..203b229216 100644 --- a/libs/wire-api/src/Wire/API/Team/Invitation.hs +++ b/libs/wire-api/src/Wire/API/Team/Invitation.hs @@ -33,6 +33,7 @@ import Data.Aeson import Data.Id import Data.Json.Util import qualified Data.Swagger.Build.Api as Doc +import Data.Text import Imports import Wire.API.Team.Role (Role, defaultRole, typeRole) import Wire.API.User.Identity (Email, Phone) @@ -104,7 +105,8 @@ data Invitation = Invitation inCreatedBy :: Maybe UserId, inInviteeEmail :: Email, inInviteeName :: Maybe Name, - inInviteePhone :: Maybe Phone + inInviteePhone :: Maybe Phone, + inInviteeUrl :: Maybe Text } deriving stock (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform Invitation) @@ -134,6 +136,9 @@ modelTeamInvitation = Doc.defineModel "TeamInvitation" $ do Doc.property "phone" Doc.string' $ do Doc.description "Phone number of the invitee, in the E.164 format" Doc.optional + Doc.property "url" Doc.string' $ do + Doc.description "URL of the invitation link to be sent to the invitee" + Doc.optional instance ToJSON Invitation where toJSON i = @@ -145,7 +150,8 @@ instance ToJSON Invitation where "created_by" .= inCreatedBy i, "email" .= inInviteeEmail i, "name" .= inInviteeName i, - "phone" .= inInviteePhone i + "phone" .= inInviteePhone i, + "url" .= inInviteeUrl i ] instance FromJSON Invitation where @@ -160,6 +166,7 @@ instance FromJSON Invitation where <*> o .: "email" <*> o .:? "name" <*> o .:? "phone" + <*> o .:? "url" -------------------------------------------------------------------------------- -- InvitationList From 76e3882617034fa83723a810536482698bd72233 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 15:28:16 +0200 Subject: [PATCH 02/66] libs/wire-api: update golden tests. --- .../Golden/Generated/InvitationList_team.hs | 151 ++++++++++++------ .../API/Golden/Generated/Invitation_team.hs | 60 ++++--- 2 files changed, 141 insertions(+), 70 deletions(-) diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs index 5a3d0eee55..8e7b45b6ad 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs @@ -32,6 +32,7 @@ import Wire.API.Team.Invitation inInviteeEmail, inInviteeName, inInviteePhone, + inInviteeUrl, inRole, inTeam ), @@ -62,7 +63,8 @@ testObject_InvitationList_team_2 = "fuC9p\1098501A\163554\f\ENQ\SO\21027N\47326_?oCX.U\r\163744W\33096\58996\1038685\DC3\t[\37667\SYN/\8408A\145025\173325\DC4H\135001\STX\166880\EOT\165028o\DC3" } ), - inInviteePhone = Just (Phone {fromPhone = "+851333011"}) + inInviteePhone = Just (Phone {fromPhone = "+851333011"}), + inInviteeUrl = Nothing } ], ilHasMore = True @@ -89,7 +91,8 @@ testObject_InvitationList_team_4 = "R6\133444\134053VQ\187682\SUB\SOH\180538\&0C\1088909\ESCR\185800\125002@\38857Z?\STX\169387\1067878e}\SOH\ETB\EOTm\184898\US]\986782\189015\1059374\986508\b\DC1zfw-5\120662\CAN\1064450 \EMe\DC4|\14426Vo{\1076439\DC3#\USS\45051&zz\160719\&9\142411,\SI\f\SOHp\1025840\DLE\163178\1060369.&\997544kZ\50431u\b\50764\1109279n:\1103691D$.Q" } ), - inInviteePhone = Just (Phone {fromPhone = "+60506387292"}) + inInviteePhone = Just (Phone {fromPhone = "+60506387292"}), + inInviteeUrl = Nothing }, Invitation { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), @@ -105,7 +108,8 @@ testObject_InvitationList_team_4 = "\DC2}q\CAN=SA\ETXx\t\ETX\\\v[\b)(\ESC]\135875Y\v@p\41515l\45065\157388\NUL\t\1100066\SOH1\DC1\ENQ\1021763\"i\29460\EM\b\ACK\SI\DC2v\ACK" } ), - inInviteePhone = Just (Phone {fromPhone = "+913945015"}) + inInviteePhone = Just (Phone {fromPhone = "+913945015"}), + inInviteeUrl = Nothing }, Invitation { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), @@ -121,7 +125,8 @@ testObject_InvitationList_team_4 = "\58076&\1059325Ec\NUL\16147}k\1036184l\172911\USJ\EM0^.+F\DEL\NUL\f$'`!\ETB[p\1041609}>E0y\96440#4I\a\66593jc\ESCgt\22473\1093208P\DC4!\1095909E93'Y$YL\46886b\r:,\181790\SO\153247y\ETX;\1064633\1099478z4z-D\1096755a\139100\&6\164829r\1033640\987906J\DLE\48134" } ), - inInviteePhone = Just (Phone {fromPhone = "+17046334"}) + inInviteePhone = Just (Phone {fromPhone = "+17046334"}), + inInviteeUrl = Nothing }, Invitation { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), @@ -137,7 +142,8 @@ testObject_InvitationList_team_4 = "Ft*O1\b&\SO\CAN<\72219\1092619m\n\DC4\DC2; \ETX\988837\DC1\1059627\"k.T\1023249[[\FS\EOT{j`\GS\997342c\1066411{\SUB\GSQY\182805\t\NAKy\t\132339j\1036225W " } ), - inInviteePhone = Nothing + inInviteePhone = Nothing, + inInviteeUrl = Nothing }, Invitation { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), @@ -147,7 +153,8 @@ testObject_InvitationList_team_4 = inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, - inInviteePhone = Just (Phone {fromPhone = "+918848647685283"}) + inInviteePhone = Just (Phone {fromPhone = "+918848647685283"}), + inInviteeUrl = Nothing }, Invitation { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), @@ -163,7 +170,8 @@ testObject_InvitationList_team_4 = "Lo\r\1107113\1111565\1042998\1027480g\"\1055088\SUB\SUB\180703\43419\EOTv\188258,\171408(\GSQT\150160;\1063450\ENQ\ETBB\1106414H\170195\\\1040638,Y" } ), - inInviteePhone = Just (Phone {fromPhone = "+45207005641274"}) + inInviteePhone = Just (Phone {fromPhone = "+45207005641274"}), + inInviteeUrl = Nothing } testObject_Invitation_team_6 :: Invitation @@ -115,7 +120,8 @@ testObject_Invitation_team_6 = "O~\DC4U\RS?V3_\191280Slh\1072236Q1\1011443j|~M7\1092762\1097596\94632\DC1K\1078140Afs\178951lGV\1113159]`o\EMf\34020InvfDDy\\DI\163761\1091945\ETBB\159212F*X\SOH\SUB\50580\ETX\DLE<\ETX\SYNc\DEL\DLE,p\v*\1005720Vn\fI\70201xS\STXV\ESC$\EMu\1002390xl>\aZ\DC44e\DC4aZ" } ), - inInviteePhone = Just (Phone {fromPhone = "+75547625285"}) + inInviteePhone = Just (Phone {fromPhone = "+75547625285"}), + inInviteeUrl = Nothing } testObject_Invitation_team_7 :: Invitation @@ -134,7 +140,8 @@ testObject_Invitation_team_7 = "\CAN.\110967\1085214\DLE\f\DLE\CAN\150564o;Yay:yY $\ETX<\879%@\USre>5L'R\DC3\178035oy#]c4!\99741U\54858\26279\1042232\1062242p_>f\SO\DEL\175240\1077738\995735_Vm\US}\STXPz\r\ENQK\SO+>\991648\NUL\153467?pu?r\ESC\SUB!?\168405;\6533S\18757\a\1071148\b\1023581\996567\17385\120022\b\SUB\FS\SIF%<\125113\SIh\ESC\ETX\SI\994739\USO\NULg_\151272\47274\1026399\EOT\1058084\1089771z~%IA'R\b\1011572Hv^\1043633wrjb\t\166747\ETX" } ), - inInviteePhone = Just (Phone {fromPhone = "+518729615781"}) + inInviteePhone = Just (Phone {fromPhone = "+518729615781"}), + inInviteeUrl = Nothing } testObject_Invitation_team_12 :: Invitation @@ -211,7 +222,8 @@ testObject_Invitation_team_12 = "\DLEZ+wd^\67082\1073384\&1\STXYdXt>\1081020LSB7F9\\\135148\ENQ\n\987295\"\127009|\a\61724\157754\DEL'\ESCTygU\1106772R\52822\1071584O4\1035713E9\"\1016016\DC2Re\ENQD}\1051112\161959\1104733\bV\176894%98'\RS9\ACK4yP\83405\14400\345\aw\t\1098022\v\1078003xv/Yl\1005740\158703" } ), - inInviteePhone = Just (Phone {fromPhone = "+68945103783764"}) + inInviteePhone = Just (Phone {fromPhone = "+68945103783764"}), + inInviteeUrl = Nothing } testObject_Invitation_team_13 :: Invitation @@ -224,7 +236,8 @@ testObject_Invitation_team_13 = inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000100000002"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "\DELr"}, inInviteeName = Just (Name {fromName = "U"}), - inInviteePhone = Just (Phone {fromPhone = "+549940856897515"}) + inInviteePhone = Just (Phone {fromPhone = "+549940856897515"}), + inInviteeUrl = Nothing } testObject_Invitation_team_14 :: Invitation @@ -237,7 +250,8 @@ testObject_Invitation_team_14 = inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0002-0000-000200000000"))), inInviteeEmail = Email {emailLocal = "EI", emailDomain = "{"}, inInviteeName = Nothing, - inInviteePhone = Just (Phone {fromPhone = "+89058877371"}) + inInviteePhone = Just (Phone {fromPhone = "+89058877371"}), + inInviteeUrl = Nothing } testObject_Invitation_team_15 :: Invitation @@ -256,7 +270,8 @@ testObject_Invitation_team_15 = "\71448\US&KIL\DC3\1086159![\n6\1111661HEj4E\12136UL\US>2\1070931_\nJ\53410Pv\SO\SIR\30897\&8\bmS\45510mE\ag\SYN\ENQ%\14545\f!\v\US\119306\ENQ\184817\1044744\SO83!j\73854\GS\1071331,\RS\CANF\1062795\1110535U\EMJb\DC1j\EMY\92304O\1007855" } ), - inInviteePhone = Just (Phone {fromPhone = "+57741900390998"}) + inInviteePhone = Just (Phone {fromPhone = "+57741900390998"}), + inInviteeUrl = Nothing } testObject_Invitation_team_16 :: Invitation @@ -269,7 +284,8 @@ testObject_Invitation_team_16 = inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "\\", emailDomain = "\"\DEL{"}, inInviteeName = Just (Name {fromName = "\GS\DC4Q;6/_f*7\1093966\SI+\1092810\41698\&9"}), - inInviteePhone = Nothing + inInviteePhone = Nothing, + inInviteeUrl = Nothing } testObject_Invitation_team_17 :: Invitation @@ -288,7 +304,8 @@ testObject_Invitation_team_17 = "Z\ESC9E\DEL\NAK\37708\83413}(3m\97177\97764'\1072786.WY;\RS8?v-\1100720\DC2\1015859" } ), - inInviteePhone = Nothing + inInviteePhone = Nothing, + inInviteeUrl = Nothing } testObject_Invitation_team_19 :: Invitation @@ -326,7 +344,8 @@ testObject_Invitation_team_19 = "\38776r\111317\ETXQi\1000087\1097943\EM\170747\74323+\1067948Q?H=G-\RS;\1103719\SOq^K;a\1052250W\EM X\83384\1073320>M\980\26387jjbU-&\1040136v\NULy\181884\a|\SYNUfJCHjP\SO\1111555\27981DNA:~s" } ), - inInviteePhone = Just (Phone {fromPhone = "+05787228893"}) + inInviteePhone = Just (Phone {fromPhone = "+05787228893"}), + inInviteeUrl = Nothing } testObject_Invitation_team_20 :: Invitation @@ -339,5 +358,6 @@ testObject_Invitation_team_20 = inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "b", emailDomain = "u9T"}, inInviteeName = Nothing, - inInviteePhone = Just (Phone {fromPhone = "+27259486019"}) + inInviteePhone = Just (Phone {fromPhone = "+27259486019"}), + inInviteeUrl = Nothing } From e8d2fed10209f32e4bf4566e570fa0970d5bb9b0 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 14:09:30 +0200 Subject: [PATCH 03/66] libs/wire-api: add exposeInvitationURLsToTeamAdmin feature flag definition. --- libs/wire-api/src/Wire/API/Team/Feature.hs | 31 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 1e2a33f803..118dc4771f 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -67,6 +67,7 @@ module Wire.API.Team.Feature DigitalSignaturesConfig (..), ConferenceCallingConfig (..), GuestLinksConfig (..), + ExposeInvitationURLsToTeamAdminConfig (..), SndFactorPasswordChallengeConfig (..), SearchVisibilityInboundConfig (..), ClassifiedDomainsConfig (..), @@ -579,6 +580,7 @@ allFeatureModels = withStatusNoLockModel @SndFactorPasswordChallengeConfig, withStatusNoLockModel @SearchVisibilityInboundConfig, withStatusNoLockModel @MLSConfig, + withStatusNoLockModel @ExposeInvitationURLsToTeamAdminConfig, withStatusModel @LegalholdConfig, withStatusModel @SSOConfig, withStatusModel @SearchVisibilityAvailableConfig, @@ -592,7 +594,8 @@ allFeatureModels = withStatusModel @GuestLinksConfig, withStatusModel @SndFactorPasswordChallengeConfig, withStatusModel @SearchVisibilityInboundConfig, - withStatusModel @MLSConfig + withStatusModel @MLSConfig, + withStatusModel @ExposeInvitationURLsToTeamAdminConfig ] <> catMaybes [ configModel @LegalholdConfig, @@ -608,7 +611,8 @@ allFeatureModels = configModel @GuestLinksConfig, configModel @SndFactorPasswordChallengeConfig, configModel @SearchVisibilityInboundConfig, - configModel @MLSConfig + configModel @MLSConfig, + configModel @ExposeInvitationURLsToTeamAdminConfig ] -------------------------------------------------------------------------------- @@ -939,6 +943,24 @@ instance IsFeatureConfig MLSConfig where Doc.property "allowedCipherSuites" (Doc.array Doc.int32') $ Doc.description "cipher suite numbers, See https://messaginglayersecurity.rocks/mls-protocol/draft-ietf-mls-protocol.html#table-5" Doc.property "defaultCipherSuite" Doc.int32' $ Doc.description "cipher suite number. See https://messaginglayersecurity.rocks/mls-protocol/draft-ietf-mls-protocol.html#table-5" +---------------------------------------------------------------------- +-- ExposeInvitationURLsToTeamAdminConfig + +data ExposeInvitationURLsToTeamAdminConfig = ExposeInvitationURLsToTeamAdminConfig + deriving stock (Show, Eq, Generic) + deriving (Arbitrary) via (GenericUniform ExposeInvitationURLsToTeamAdminConfig) + +instance IsFeatureConfig ExposeInvitationURLsToTeamAdminConfig where + type FeatureSymbol ExposeInvitationURLsToTeamAdminConfig = "exposeInvitationURLsToTeamAdmins" + defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked ExposeInvitationURLsToTeamAdminConfig FeatureTTLUnlimited + objectSchema = pure ExposeInvitationURLsToTeamAdminConfig + +instance ToSchema ExposeInvitationURLsToTeamAdminConfig where + schema = object "ExposeInvitationURLsToTeamAdminConfig" objectSchema + +instance FeatureTrivialConfig ExposeInvitationURLsToTeamAdminConfig where + trivialConfig = ExposeInvitationURLsToTeamAdminConfig + ---------------------------------------------------------------------- -- FeatureStatus @@ -1007,7 +1029,8 @@ data AllFeatureConfigs = AllFeatureConfigs afcSelfDeletingMessages :: WithStatus SelfDeletingMessagesConfig, afcGuestLink :: WithStatus GuestLinksConfig, afcSndFactorPasswordChallenge :: WithStatus SndFactorPasswordChallengeConfig, - afcMLS :: WithStatus MLSConfig + afcMLS :: WithStatus MLSConfig, + afcExposeInvitationURLsToTeamAdmin :: WithStatus ExposeInvitationURLsToTeamAdminConfig } deriving stock (Eq, Show) deriving (FromJSON, ToJSON, S.ToSchema) via (Schema AllFeatureConfigs) @@ -1030,6 +1053,7 @@ instance ToSchema AllFeatureConfigs where <*> afcGuestLink .= featureField <*> afcSndFactorPasswordChallenge .= featureField <*> afcMLS .= featureField + <*> afcExposeInvitationURLsToTeamAdmin .= featureField where featureField :: forall cfg. @@ -1054,5 +1078,6 @@ instance Arbitrary AllFeatureConfigs where <*> arbitrary <*> arbitrary <*> arbitrary + <*> arbitrary makeLenses ''ImplicitLockStatus From 1cf1d5adef77d417624141f03653064358f85246 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 14:13:23 +0200 Subject: [PATCH 04/66] galley: add exposeInvitationURLsToTeamAdmin feature flag to Cassandra schema. --- .../src/V73_ExposeInvitationsToTeamAdmin.hs | 34 +++++++++++++++++++ services/galley/src/Galley/Cassandra.hs | 2 +- .../src/Galley/Cassandra/TeamFeatures.hs | 7 ++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs diff --git a/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs b/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs new file mode 100644 index 0000000000..d03e782456 --- /dev/null +++ b/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs @@ -0,0 +1,34 @@ +-- 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 V73_ExposeInvitationsToTeamAdmin + ( migration, + ) +where + +import Cassandra.Schema +import Imports +import Text.RawString.QQ + +migration :: Migration +migration = Migration 73 "Add feature config for team feature exposing invitation URLs to team admins" $ do + schema' + [r| ALTER TABLE team_features ADD ( + expose_invitation_urls_to_team_admin int, + expose_invitation_urls_to_team_admin_lock_status int + ) + |] diff --git a/services/galley/src/Galley/Cassandra.hs b/services/galley/src/Galley/Cassandra.hs index 9e6c24f7fb..6bc6719a2b 100644 --- a/services/galley/src/Galley/Cassandra.hs +++ b/services/galley/src/Galley/Cassandra.hs @@ -20,4 +20,4 @@ module Galley.Cassandra (schemaVersion) where import Imports schemaVersion :: Int32 -schemaVersion = 72 +schemaVersion = 73 diff --git a/services/galley/src/Galley/Cassandra/TeamFeatures.hs b/services/galley/src/Galley/Cassandra/TeamFeatures.hs index eacdde5653..fe370df226 100644 --- a/services/galley/src/Galley/Cassandra/TeamFeatures.hs +++ b/services/galley/src/Galley/Cassandra/TeamFeatures.hs @@ -313,3 +313,10 @@ instance FeatureStatusCassandra MLSConfig where insert = "insert into team_features (team_id, mls_status, mls_default_protocol, \ \mls_protocol_toggle_users, mls_allowed_ciphersuites, mls_default_ciphersuite) values (?, ?, ?, ?, ?, ?)" + +instance FeatureStatusCassandra ExposeInvitationURLsToTeamAdminConfig where + getFeatureConfig _ = getTrivialConfigC "expose_invitation_urls_to_team_admin" + setFeatureConfig _ tid statusNoLock = setFeatureStatusC "expose_invitation_urls_to_team_admin" tid (wssStatus statusNoLock) + + getFeatureLockStatus _ = getLockStatusC "expose_invitation_urls_to_team_admin_lock_status" + setFeatureLockStatus _ = setLockStatusC "expose_invitation_urls_to_team_admin_lock_status" From 57a6b6903c80f888a7920b55d0125a3600fe4119 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 14:52:27 +0200 Subject: [PATCH 05/66] libs/galley-types: add exposeInvitationURLsToTeamAdmin to galley feature types. --- libs/galley-types/src/Galley/Types/Teams.hs | 9 +++++++-- libs/galley-types/test/unit/Test/Galley/Types.hs | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index a06c1b0ea0..19026d391d 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -39,6 +39,7 @@ module Galley.Types.Teams flagTeamFeatureSndFactorPasswordChallengeStatus, flagTeamFeatureSearchVisibilityInbound, flagMLS, + flagTeamFeatureExposeInvitationURLsToTeamAdmin, Defaults (..), ImplicitLockStatus (..), unImplicitLockStatus, @@ -150,7 +151,8 @@ data FeatureFlags = FeatureFlags _flagsTeamFeatureValidateSAMLEmailsStatus :: !(Defaults (ImplicitLockStatus ValidateSAMLEmailsConfig)), _flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)), _flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)), - _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)) + _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)), + _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(Defaults (ImplicitLockStatus ExposeInvitationURLsToTeamAdminConfig)) } deriving (Eq, Show, Generic) @@ -200,6 +202,7 @@ instance FromJSON FeatureFlags where <*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge")) <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" + <*> withImplicitLockStatusOrDefault obj "exposeInvitationURLsToTeamAdmin" where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) withImplicitLockStatusOrDefault obj fieldName = fromMaybe (Defaults (ImplicitLockStatus (defFeatureStatus @cfg))) <$> obj .:? fieldName @@ -220,6 +223,7 @@ instance ToJSON FeatureFlags where sndFactorPasswordChallenge searchVisibilityInbound mls + exposeInvitationURLsToTeamAdmin ) = object [ "sso" .= sso, @@ -234,7 +238,8 @@ instance ToJSON FeatureFlags where "validateSAMLEmails" .= validateSAMLEmails, "sndFactorPasswordChallenge" .= sndFactorPasswordChallenge, "searchVisibilityInbound" .= searchVisibilityInbound, - "mls" .= mls + "mls" .= mls, + "exposeInvitationURLsToTeamAdmin" .= exposeInvitationURLsToTeamAdmin ] instance FromJSON FeatureSSO where diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 3e92614977..3ef1a07863 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -98,6 +98,7 @@ instance Arbitrary FeatureFlags where <*> arbitrary <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary + <*> fmap (fmap unlocked) arbitrary where unlocked :: ImplicitLockStatus a -> ImplicitLockStatus a unlocked = ImplicitLockStatus . Public.setLockStatus Public.LockStatusUnlocked . _unImplicitLockStatus From a069fec12b2c958ca9d6a4e0c253f19da3797761 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 15:23:54 +0200 Subject: [PATCH 06/66] galley: add feature flag toggle logic for exposeInvitationURLsToTeamAdmin --- services/galley/src/Galley/API/Teams/Features.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 260781cec4..415eea0c44 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -177,7 +177,8 @@ type FeaturePersistentAllFeatures db = FeaturePersistentConstraint db GuestLinksConfig, FeaturePersistentConstraint db SndFactorPasswordChallengeConfig, FeaturePersistentConstraint db MLSConfig, - FeaturePersistentConstraint db SearchVisibilityInboundConfig + FeaturePersistentConstraint db SearchVisibilityInboundConfig, + FeaturePersistentConstraint db ExposeInvitationURLsToTeamAdminConfig ) getFeatureStatus :: @@ -438,6 +439,7 @@ getAllFeatureConfigsForServer = <*> getConfigForServer @db @GuestLinksConfig <*> getConfigForServer @db @SndFactorPasswordChallengeConfig <*> getConfigForServer @db @MLSConfig + <*> getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig getAllFeatureConfigsUser :: forall db r. @@ -471,6 +473,7 @@ getAllFeatureConfigsUser uid = <*> getConfigForUser @db @GuestLinksConfig uid <*> getConfigForUser @db @SndFactorPasswordChallengeConfig uid <*> getConfigForUser @db @MLSConfig uid + <*> getConfigForUser @db @ExposeInvitationURLsToTeamAdminConfig uid getAllFeatureConfigsTeam :: forall db r. @@ -503,6 +506,7 @@ getAllFeatureConfigsTeam tid = <*> getConfigForTeam @db @GuestLinksConfig tid <*> getConfigForTeam @db @SndFactorPasswordChallengeConfig tid <*> getConfigForTeam @db @MLSConfig tid + <*> getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid -- | Note: this is an internal function which doesn't cover all features, e.g. LegalholdConfig genericGetConfigForTeam :: @@ -846,6 +850,14 @@ instance SetFeatureConfig db MLSConfig where setConfigForTeam tid wsnl = do persistAndPushEvent @db tid wsnl +instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where + getConfigForServer = + input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin . unDefaults . unImplicitLockStatus) + +instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where + setConfigForTeam tid wsnl = persistAndPushEvent @db tid wsnl + + -- -- | If second factor auth is enabled, make sure that end-points that don't support it, but should, are blocked completely. (This is a workaround until we have 2FA for those end-points as well.) -- -- -- This function exists to resolve a cyclic dependency. From 823da2451e52dda2aeb187d127617385325dd725 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 16:45:08 +0200 Subject: [PATCH 07/66] libs/wire-api: add galley routes for exposeInvitationURLsToTeamAdmin feature. --- libs/wire-api/src/Wire/API/Routes/Public/Galley.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs index f80ff25aa3..de1bd25b34 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs @@ -1136,6 +1136,8 @@ type FeatureAPI = :<|> FeatureStatusPut '() SndFactorPasswordChallengeConfig :<|> FeatureStatusGet MLSConfig :<|> FeatureStatusPut '() MLSConfig + :<|> FeatureStatusGet ExposeInvitationURLsToTeamAdminConfig + :<|> FeatureStatusPut '() ExposeInvitationURLsToTeamAdminConfig :<|> FeatureStatusGet SearchVisibilityInboundConfig :<|> FeatureStatusPut '() SearchVisibilityInboundConfig :<|> AllFeatureConfigsUserGet From 39160c576d66a051b34f6929f57e3385f3de0310 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 16:26:33 +0200 Subject: [PATCH 08/66] galley: implement feature config routes for exposeInvitationURLsToTeamAdmin. --- services/galley/src/Galley/API/Public/Servant.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/galley/src/Galley/API/Public/Servant.hs b/services/galley/src/Galley/API/Public/Servant.hs index 9977aaae1a..baaa9cc414 100644 --- a/services/galley/src/Galley/API/Public/Servant.hs +++ b/services/galley/src/Galley/API/Public/Servant.hs @@ -111,6 +111,7 @@ servantSitemap = <@> mkNamedAPI @"get-team" getTeamH <@> mkNamedAPI @"delete-team" deleteTeam + features :: API FeatureAPI GalleyEffects features = mkNamedAPI @'("get", SSOConfig) (getFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @'("get", LegalholdConfig) (getFeatureStatus @Cassandra . DoAuth) @@ -139,6 +140,8 @@ servantSitemap = <@> mkNamedAPI @'("put", SndFactorPasswordChallengeConfig) (setFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @'("get", MLSConfig) (getFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @'("put", MLSConfig) (setFeatureStatus @Cassandra . DoAuth) + <@> mkNamedAPI @'("get", ExposeInvitationURLsToTeamAdminConfig) (getFeatureStatus @Cassandra . DoAuth) + <@> mkNamedAPI @'("put", ExposeInvitationURLsToTeamAdminConfig) (setFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @'("get", SearchVisibilityInboundConfig) (getFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @'("put", SearchVisibilityInboundConfig) (setFeatureStatus @Cassandra . DoAuth) <@> mkNamedAPI @"get-all-feature-configs-for-user" (getAllFeatureConfigsForUser @Cassandra) From cf4d9b31701ff9c3dcba1f1750200c3733071161 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 16:56:58 +0200 Subject: [PATCH 09/66] galley: add internal endpoints for exposeInvitationURLsToTeamAdmin feature. --- services/galley/src/Galley/API/Internal.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/galley/src/Galley/API/Internal.hs b/services/galley/src/Galley/API/Internal.hs index 120845565a..7cd042bf08 100644 --- a/services/galley/src/Galley/API/Internal.hs +++ b/services/galley/src/Galley/API/Internal.hs @@ -192,6 +192,10 @@ type IFeatureAPI = :<|> IFeatureStatusGet MLSConfig :<|> IFeatureStatusPut '() MLSConfig :<|> IFeatureStatusPatch '() MLSConfig + -- ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusGet ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusPut '() ExposeInvitationURLsToTeamAdminConfig + :<|> IFeatureStatusPatch '() ExposeInvitationURLsToTeamAdminConfig -- SearchVisibilityInboundConfig :<|> IFeatureStatusGet SearchVisibilityInboundConfig :<|> IFeatureStatusPut '() SearchVisibilityInboundConfig @@ -530,6 +534,9 @@ featureAPI = <@> mkNamedAPI @'("iget", MLSConfig) (getFeatureStatus @Cassandra DontDoAuth) <@> mkNamedAPI @'("iput", MLSConfig) (setFeatureStatusInternal @Cassandra) <@> mkNamedAPI @'("ipatch", MLSConfig) (patchFeatureStatusInternal @Cassandra) + <@> mkNamedAPI @'("iget", ExposeInvitationURLsToTeamAdminConfig) (getFeatureStatus @Cassandra DontDoAuth) + <@> mkNamedAPI @'("iput", ExposeInvitationURLsToTeamAdminConfig) (setFeatureStatusInternal @Cassandra) + <@> mkNamedAPI @'("ipatch", ExposeInvitationURLsToTeamAdminConfig) (patchFeatureStatusInternal @Cassandra) <@> mkNamedAPI @'("iget", SearchVisibilityInboundConfig) (getFeatureStatus @Cassandra DontDoAuth) <@> mkNamedAPI @'("iput", SearchVisibilityInboundConfig) (setFeatureStatusInternal @Cassandra) <@> mkNamedAPI @'("ipatch", SearchVisibilityInboundConfig) (patchFeatureStatusInternal @Cassandra) From 8dd780f3e4614101c9468058458e435939b202b3 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 17:04:09 +0200 Subject: [PATCH 10/66] galley: add option for specifying teams with admin-visible invitation URLs --- services/galley/src/Galley/Options.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/galley/src/Galley/Options.hs b/services/galley/src/Galley/Options.hs index 2d82241913..0ca95f1970 100644 --- a/services/galley/src/Galley/Options.hs +++ b/services/galley/src/Galley/Options.hs @@ -30,6 +30,7 @@ module Galley.Options setFederationDomain, setEnableIndexedBillingTeamMembers, setMlsPrivateKeyPaths, + setAllowExposingInvitationURLsInTeams, setFeatureFlags, defConcurrentDeletionEvents, defDeleteConvThrottleMillis, @@ -56,6 +57,7 @@ where import Control.Lens hiding (Level, (.=)) import Data.Aeson.TH (deriveFromJSON) import Data.Domain (Domain) +import Data.Id (TeamId) import Data.Misc import Data.Range import Galley.Keys @@ -106,6 +108,7 @@ data Settings = Settings -- Defaults to false. _setEnableIndexedBillingTeamMembers :: !(Maybe Bool), _setMlsPrivateKeyPaths :: !(Maybe MLSPrivateKeyPaths), + _setAllowExposingInvitationURLsInTeams :: !(Maybe [TeamId]), -- | FUTUREWORK: 'setFeatureFlags' should be renamed to 'setFeatureConfigs' in all types. _setFeatureFlags :: !FeatureFlags } From 88130c9acff9a78610df14f38e6124e53979c2d3 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Thu, 8 Sep 2022 17:36:21 +0200 Subject: [PATCH 11/66] galley: only allow enabling feature for explicitly configured teams --- services/galley/src/Galley/API/Teams/Features.hs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 415eea0c44..92d417c756 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -142,7 +142,7 @@ class GetFeatureConfig (db :: *) cfg where -- | Don't export methods of this typeclass class GetFeatureConfig (db :: *) cfg => SetFeatureConfig (db :: *) cfg where type SetConfigForTeamConstraints db cfg (r :: EffectRow) :: Constraint - type SetConfigForTeamConstraints db cfg (r :: EffectRow) = () + type SetConfigForTeamConstraints db cfg (r :: EffectRow) = (Member (ErrorS OperationDenied) r) -- | This method should generate the side-effects of changing the feature and -- also (depending on the feature) persist the new setting to the database and @@ -855,8 +855,15 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin . unDefaults . unImplicitLockStatus) instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where - setConfigForTeam tid wsnl = persistAndPushEvent @db tid wsnl - + setConfigForTeam tid wsnl = + -- Only allow enabling this feature for teams which are in the admin-configured allowlist. + case wssStatus wsnl of + FeatureStatusDisabled -> persistAndPushEvent @db tid wsnl + FeatureStatusEnabled -> do + allowedTeams <- input <&> view (optSettings . setAllowExposingInvitationURLsInTeams) + if maybe False (elem tid) allowedTeams + then persistAndPushEvent @db tid wsnl + else throwS @OperationDenied -- -- | If second factor auth is enabled, make sure that end-points that don't support it, but should, are blocked completely. (This is a workaround until we have 2FA for those end-points as well.) -- -- From eeac0bfde4fa6a284dbaa4f089da807e427a39c4 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 11:43:38 +0200 Subject: [PATCH 12/66] libs/wire-api: add separate feature flag for team allowlist. --- libs/wire-api/src/Wire/API/Team/Feature.hs | 47 ++++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 118dc4771f..147ec2db79 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -68,6 +68,7 @@ module Wire.API.Team.Feature ConferenceCallingConfig (..), GuestLinksConfig (..), ExposeInvitationURLsToTeamAdminConfig (..), + ExposeInvitationURLsTeamAllowlistConfig (..), SndFactorPasswordChallengeConfig (..), SearchVisibilityInboundConfig (..), ClassifiedDomainsConfig (..), @@ -581,6 +582,7 @@ allFeatureModels = withStatusNoLockModel @SearchVisibilityInboundConfig, withStatusNoLockModel @MLSConfig, withStatusNoLockModel @ExposeInvitationURLsToTeamAdminConfig, + withStatusNoLockModel @ExposeInvitationURLsTeamAllowlistConfig, withStatusModel @LegalholdConfig, withStatusModel @SSOConfig, withStatusModel @SearchVisibilityAvailableConfig, @@ -595,7 +597,8 @@ allFeatureModels = withStatusModel @SndFactorPasswordChallengeConfig, withStatusModel @SearchVisibilityInboundConfig, withStatusModel @MLSConfig, - withStatusModel @ExposeInvitationURLsToTeamAdminConfig + withStatusModel @ExposeInvitationURLsToTeamAdminConfig, + withStatusModel @ExposeInvitationURLsTeamAllowlistConfig ] <> catMaybes [ configModel @LegalholdConfig, @@ -612,7 +615,8 @@ allFeatureModels = configModel @SndFactorPasswordChallengeConfig, configModel @SearchVisibilityInboundConfig, configModel @MLSConfig, - configModel @ExposeInvitationURLsToTeamAdminConfig + configModel @ExposeInvitationURLsToTeamAdminConfig, + configModel @ExposeInvitationURLsTeamAllowlistConfig ] -------------------------------------------------------------------------------- @@ -951,7 +955,8 @@ data ExposeInvitationURLsToTeamAdminConfig = ExposeInvitationURLsToTeamAdminConf deriving (Arbitrary) via (GenericUniform ExposeInvitationURLsToTeamAdminConfig) instance IsFeatureConfig ExposeInvitationURLsToTeamAdminConfig where - type FeatureSymbol ExposeInvitationURLsToTeamAdminConfig = "exposeInvitationURLsToTeamAdmins" + type FeatureSymbol ExposeInvitationURLsToTeamAdminConfig = "exposeInvitationURLsToTeamAdmin" + -- TODO(sysvinit): lock by default? defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked ExposeInvitationURLsToTeamAdminConfig FeatureTTLUnlimited objectSchema = pure ExposeInvitationURLsToTeamAdminConfig @@ -961,6 +966,37 @@ instance ToSchema ExposeInvitationURLsToTeamAdminConfig where instance FeatureTrivialConfig ExposeInvitationURLsToTeamAdminConfig where trivialConfig = ExposeInvitationURLsToTeamAdminConfig +---------------------------------------------------------------------- +-- ExposeInvitationURLsTeamAllowlistConfig + +data ExposeInvitationURLsTeamAllowlistConfig = ExposeInvitationURLsTeamAllowlistConfig + { exposeInvitationURLsTeamAllowlist :: [TeamId] + } + deriving stock (Show, Eq, Generic) + deriving (ToJSON, FromJSON, S.ToSchema) via (Schema ExposeInvitationURLsTeamAllowlistConfig) + +deriving via (GenericUniform ExposeInvitationURLsTeamAllowlistConfig) instance Arbitrary ExposeInvitationURLsTeamAllowlistConfig + +instance ToSchema ExposeInvitationURLsTeamAllowlistConfig where + schema = + object "ExposeInvitationURLsTeamAllowlistConfig" $ + ExposeInvitationURLsTeamAllowlistConfig + <$> exposeInvitationURLsTeamAllowlist .= field "teams" (array schema) + +instance IsFeatureConfig ExposeInvitationURLsTeamAllowlistConfig where + type FeatureSymbol ExposeInvitationURLsTeamAllowlistConfig = "exposeInvitationURLsTeamAllowlist" + + defFeatureStatus = + withStatus + FeatureStatusDisabled + LockStatusUnlocked + (ExposeInvitationURLsTeamAllowlistConfig []) + FeatureTTLUnlimited + configModel = Just $ + Doc.defineModel "ExposeInvitationURLsTeamAllowlistConfig" $ do + Doc.property "teams" (Doc.array Doc.string') $ Doc.description "teams" + objectSchema = field "config" schema + ---------------------------------------------------------------------- -- FeatureStatus @@ -1030,7 +1066,8 @@ data AllFeatureConfigs = AllFeatureConfigs afcGuestLink :: WithStatus GuestLinksConfig, afcSndFactorPasswordChallenge :: WithStatus SndFactorPasswordChallengeConfig, afcMLS :: WithStatus MLSConfig, - afcExposeInvitationURLsToTeamAdmin :: WithStatus ExposeInvitationURLsToTeamAdminConfig + afcExposeInvitationURLsToTeamAdmin :: WithStatus ExposeInvitationURLsToTeamAdminConfig, + afcExposeInvitationURLsTeamAllowlist :: WithStatus ExposeInvitationURLsTeamAllowlistConfig } deriving stock (Eq, Show) deriving (FromJSON, ToJSON, S.ToSchema) via (Schema AllFeatureConfigs) @@ -1054,6 +1091,7 @@ instance ToSchema AllFeatureConfigs where <*> afcSndFactorPasswordChallenge .= featureField <*> afcMLS .= featureField <*> afcExposeInvitationURLsToTeamAdmin .= featureField + <*> afcExposeInvitationURLsTeamAllowlist .= featureField where featureField :: forall cfg. @@ -1079,5 +1117,6 @@ instance Arbitrary AllFeatureConfigs where <*> arbitrary <*> arbitrary <*> arbitrary + <*> arbitrary makeLenses ''ImplicitLockStatus From 6c557ff316dc6f293f57086ce891490ea0fc201b Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 11:47:09 +0200 Subject: [PATCH 13/66] galley: remove lock status for exposeInvitationURLsToTeamAdmin. --- services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs | 3 +-- services/galley/src/Galley/Cassandra/TeamFeatures.hs | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs b/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs index d03e782456..460d32f500 100644 --- a/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs +++ b/services/galley/schema/src/V73_ExposeInvitationsToTeamAdmin.hs @@ -28,7 +28,6 @@ migration :: Migration migration = Migration 73 "Add feature config for team feature exposing invitation URLs to team admins" $ do schema' [r| ALTER TABLE team_features ADD ( - expose_invitation_urls_to_team_admin int, - expose_invitation_urls_to_team_admin_lock_status int + expose_invitation_urls_to_team_admin int ) |] diff --git a/services/galley/src/Galley/Cassandra/TeamFeatures.hs b/services/galley/src/Galley/Cassandra/TeamFeatures.hs index fe370df226..169280c51d 100644 --- a/services/galley/src/Galley/Cassandra/TeamFeatures.hs +++ b/services/galley/src/Galley/Cassandra/TeamFeatures.hs @@ -317,6 +317,3 @@ instance FeatureStatusCassandra MLSConfig where instance FeatureStatusCassandra ExposeInvitationURLsToTeamAdminConfig where getFeatureConfig _ = getTrivialConfigC "expose_invitation_urls_to_team_admin" setFeatureConfig _ tid statusNoLock = setFeatureStatusC "expose_invitation_urls_to_team_admin" tid (wssStatus statusNoLock) - - getFeatureLockStatus _ = getLockStatusC "expose_invitation_urls_to_team_admin_lock_status" - setFeatureLockStatus _ = setLockStatusC "expose_invitation_urls_to_team_admin_lock_status" From ba7e15376027618ea0b210f5c2682c6cfddf4499 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 12:11:20 +0200 Subject: [PATCH 14/66] libs/galley-types: add feature flag configuration for invite url allowlist. --- libs/galley-types/src/Galley/Types/Teams.hs | 10 ++++++++-- libs/galley-types/test/unit/Test/Galley/Types.hs | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index 19026d391d..ca1ee38bc2 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -40,6 +40,7 @@ module Galley.Types.Teams flagTeamFeatureSearchVisibilityInbound, flagMLS, flagTeamFeatureExposeInvitationURLsToTeamAdmin, + flagTeamFeatureExposeInvitationURLsTeamAllowlist, Defaults (..), ImplicitLockStatus (..), unImplicitLockStatus, @@ -152,7 +153,9 @@ data FeatureFlags = FeatureFlags _flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)), _flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)), _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)), - _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(Defaults (ImplicitLockStatus ExposeInvitationURLsToTeamAdminConfig)) + -- TODO(sysvinit): v-- default locked status? + _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(Defaults (ImplicitLockStatus ExposeInvitationURLsToTeamAdminConfig)), + _flagTeamFeatureExposeInvitationURLsTeamAllowlist :: !(ImplicitLockStatus ExposeInvitationURLsTeamAllowlistConfig) } deriving (Eq, Show, Generic) @@ -203,6 +206,7 @@ instance FromJSON FeatureFlags where <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" <*> withImplicitLockStatusOrDefault obj "exposeInvitationURLsToTeamAdmin" + <*> (fromMaybe (ImplicitLockStatus (defFeatureStatus @ExposeInvitationURLsTeamAllowlistConfig)) <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) withImplicitLockStatusOrDefault obj fieldName = fromMaybe (Defaults (ImplicitLockStatus (defFeatureStatus @cfg))) <$> obj .:? fieldName @@ -224,6 +228,7 @@ instance ToJSON FeatureFlags where searchVisibilityInbound mls exposeInvitationURLsToTeamAdmin + exposeInvitationURLsTeamAllowlist ) = object [ "sso" .= sso, @@ -239,7 +244,8 @@ instance ToJSON FeatureFlags where "sndFactorPasswordChallenge" .= sndFactorPasswordChallenge, "searchVisibilityInbound" .= searchVisibilityInbound, "mls" .= mls, - "exposeInvitationURLsToTeamAdmin" .= exposeInvitationURLsToTeamAdmin + "exposeInvitationURLsToTeamAdmin" .= exposeInvitationURLsToTeamAdmin, + "exposeInvitationURLsTeamAllowlist" .= exposeInvitationURLsTeamAllowlist ] instance FromJSON FeatureSSO where diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 3ef1a07863..31e26a11ce 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -99,6 +99,7 @@ instance Arbitrary FeatureFlags where <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary + <*> fmap unlocked arbitrary where unlocked :: ImplicitLockStatus a -> ImplicitLockStatus a unlocked = ImplicitLockStatus . Public.setLockStatus Public.LockStatusUnlocked . _unImplicitLockStatus From 507d60984f2f517a041b45733912b8d7386deb7c Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 12:20:54 +0200 Subject: [PATCH 15/66] galley: re-scope effect row constraint for exposeInvitationURLsToTeamAdmin. --- services/galley/src/Galley/API/Teams/Features.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 92d417c756..5b3707379a 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -142,7 +142,7 @@ class GetFeatureConfig (db :: *) cfg where -- | Don't export methods of this typeclass class GetFeatureConfig (db :: *) cfg => SetFeatureConfig (db :: *) cfg where type SetConfigForTeamConstraints db cfg (r :: EffectRow) :: Constraint - type SetConfigForTeamConstraints db cfg (r :: EffectRow) = (Member (ErrorS OperationDenied) r) + type SetConfigForTeamConstraints db cfg (r :: EffectRow) = () -- | This method should generate the side-effects of changing the feature and -- also (depending on the feature) persist the new setting to the database and @@ -855,6 +855,7 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin . unDefaults . unImplicitLockStatus) instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where + type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = -- Only allow enabling this feature for teams which are in the admin-configured allowlist. case wssStatus wsnl of From c20b900fc372c24bd94bb5b8c103ce0f48e8dcac Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 12:26:45 +0200 Subject: [PATCH 16/66] galley: add feature status internals for allowlist config. --- services/galley/src/Galley/API/Teams/Features.hs | 10 +++++++++- services/galley/src/Galley/Cassandra/TeamFeatures.hs | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 5b3707379a..d970e6b375 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -178,7 +178,8 @@ type FeaturePersistentAllFeatures db = FeaturePersistentConstraint db SndFactorPasswordChallengeConfig, FeaturePersistentConstraint db MLSConfig, FeaturePersistentConstraint db SearchVisibilityInboundConfig, - FeaturePersistentConstraint db ExposeInvitationURLsToTeamAdminConfig + FeaturePersistentConstraint db ExposeInvitationURLsToTeamAdminConfig, + FeaturePersistentConstraint db ExposeInvitationURLsTeamAllowlistConfig ) getFeatureStatus :: @@ -440,6 +441,7 @@ getAllFeatureConfigsForServer = <*> getConfigForServer @db @SndFactorPasswordChallengeConfig <*> getConfigForServer @db @MLSConfig <*> getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig + <*> getConfigForServer @db @ExposeInvitationURLsTeamAllowlistConfig getAllFeatureConfigsUser :: forall db r. @@ -474,6 +476,7 @@ getAllFeatureConfigsUser uid = <*> getConfigForUser @db @SndFactorPasswordChallengeConfig uid <*> getConfigForUser @db @MLSConfig uid <*> getConfigForUser @db @ExposeInvitationURLsToTeamAdminConfig uid + <*> getConfigForUser @db @ExposeInvitationURLsTeamAllowlistConfig uid getAllFeatureConfigsTeam :: forall db r. @@ -507,6 +510,7 @@ getAllFeatureConfigsTeam tid = <*> getConfigForTeam @db @SndFactorPasswordChallengeConfig tid <*> getConfigForTeam @db @MLSConfig tid <*> getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid + <*> getConfigForTeam @db @ExposeInvitationURLsTeamAllowlistConfig tid -- | Note: this is an internal function which doesn't cover all features, e.g. LegalholdConfig genericGetConfigForTeam :: @@ -866,6 +870,10 @@ instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where then persistAndPushEvent @db tid wsnl else throwS @OperationDenied +instance GetFeatureConfig db ExposeInvitationURLsTeamAllowlistConfig where + getConfigForServer = + input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist . unImplicitLockStatus) + -- -- | If second factor auth is enabled, make sure that end-points that don't support it, but should, are blocked completely. (This is a workaround until we have 2FA for those end-points as well.) -- -- -- This function exists to resolve a cyclic dependency. diff --git a/services/galley/src/Galley/Cassandra/TeamFeatures.hs b/services/galley/src/Galley/Cassandra/TeamFeatures.hs index 169280c51d..f03f3f4b56 100644 --- a/services/galley/src/Galley/Cassandra/TeamFeatures.hs +++ b/services/galley/src/Galley/Cassandra/TeamFeatures.hs @@ -317,3 +317,7 @@ instance FeatureStatusCassandra MLSConfig where instance FeatureStatusCassandra ExposeInvitationURLsToTeamAdminConfig where getFeatureConfig _ = getTrivialConfigC "expose_invitation_urls_to_team_admin" setFeatureConfig _ tid statusNoLock = setFeatureStatusC "expose_invitation_urls_to_team_admin" tid (wssStatus statusNoLock) + +instance FeatureStatusCassandra ExposeInvitationURLsTeamAllowlistConfig where + getFeatureConfig _ _tid = pure Nothing + setFeatureConfig _ _tid _statusNoLock = pure () From 574d36213928237f132da3037b2c31db67f5d0ff Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 12:36:12 +0200 Subject: [PATCH 17/66] galley: use allowlist feature config for setting exposeInvitationURLsToTeamAdmin --- services/galley/src/Galley/API/Teams/Features.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index d970e6b375..12b71cda21 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -865,8 +865,8 @@ instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where case wssStatus wsnl of FeatureStatusDisabled -> persistAndPushEvent @db tid wsnl FeatureStatusEnabled -> do - allowedTeams <- input <&> view (optSettings . setAllowExposingInvitationURLsInTeams) - if maybe False (elem tid) allowedTeams + allowedTeams <- exposeInvitationURLsTeamAllowlist <$> wsConfig <$> getConfigForServer @db + if elem tid allowedTeams then persistAndPushEvent @db tid wsnl else throwS @OperationDenied From 6d713304be4311bd30849251b7870dfbb5c727d5 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 12:37:30 +0200 Subject: [PATCH 18/66] galley: remove top-level configuration option for team allowlist --- services/galley/src/Galley/Options.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/galley/src/Galley/Options.hs b/services/galley/src/Galley/Options.hs index 0ca95f1970..2d82241913 100644 --- a/services/galley/src/Galley/Options.hs +++ b/services/galley/src/Galley/Options.hs @@ -30,7 +30,6 @@ module Galley.Options setFederationDomain, setEnableIndexedBillingTeamMembers, setMlsPrivateKeyPaths, - setAllowExposingInvitationURLsInTeams, setFeatureFlags, defConcurrentDeletionEvents, defDeleteConvThrottleMillis, @@ -57,7 +56,6 @@ where import Control.Lens hiding (Level, (.=)) import Data.Aeson.TH (deriveFromJSON) import Data.Domain (Domain) -import Data.Id (TeamId) import Data.Misc import Data.Range import Galley.Keys @@ -108,7 +106,6 @@ data Settings = Settings -- Defaults to false. _setEnableIndexedBillingTeamMembers :: !(Maybe Bool), _setMlsPrivateKeyPaths :: !(Maybe MLSPrivateKeyPaths), - _setAllowExposingInvitationURLsInTeams :: !(Maybe [TeamId]), -- | FUTUREWORK: 'setFeatureFlags' should be renamed to 'setFeatureConfigs' in all types. _setFeatureFlags :: !FeatureFlags } From 107671d2711700c7e6982145c97b94dd1d7fb5aa Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 13:30:31 +0200 Subject: [PATCH 19/66] libs/{wire-api,galley-types}: lock exposeInvitationURLsToTeamAdmin by default. --- libs/galley-types/src/Galley/Types/Teams.hs | 5 ++--- libs/galley-types/test/unit/Test/Galley/Types.hs | 2 +- libs/wire-api/src/Wire/API/Team/Feature.hs | 3 +-- services/galley/src/Galley/API/Teams/Features.hs | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index ca1ee38bc2..eb78ef9870 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -153,8 +153,7 @@ data FeatureFlags = FeatureFlags _flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)), _flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)), _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)), - -- TODO(sysvinit): v-- default locked status? - _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(Defaults (ImplicitLockStatus ExposeInvitationURLsToTeamAdminConfig)), + _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(WithStatus ExposeInvitationURLsToTeamAdminConfig), _flagTeamFeatureExposeInvitationURLsTeamAllowlist :: !(ImplicitLockStatus ExposeInvitationURLsTeamAllowlistConfig) } deriving (Eq, Show, Generic) @@ -205,7 +204,7 @@ instance FromJSON FeatureFlags where <*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge")) <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" - <*> withImplicitLockStatusOrDefault obj "exposeInvitationURLsToTeamAdmin" + <*> (fromMaybe (defFeatureStatus @ExposeInvitationURLsToTeamAdminConfig) <$> (obj .:? "exposeInvitationURLsToTeamAdmin")) <*> (fromMaybe (ImplicitLockStatus (defFeatureStatus @ExposeInvitationURLsTeamAllowlistConfig)) <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 31e26a11ce..683d1a2bcc 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -98,7 +98,7 @@ instance Arbitrary FeatureFlags where <*> arbitrary <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary - <*> fmap (fmap unlocked) arbitrary + <*> arbitrary <*> fmap unlocked arbitrary where unlocked :: ImplicitLockStatus a -> ImplicitLockStatus a diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 147ec2db79..88f32a1128 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -956,8 +956,7 @@ data ExposeInvitationURLsToTeamAdminConfig = ExposeInvitationURLsToTeamAdminConf instance IsFeatureConfig ExposeInvitationURLsToTeamAdminConfig where type FeatureSymbol ExposeInvitationURLsToTeamAdminConfig = "exposeInvitationURLsToTeamAdmin" - -- TODO(sysvinit): lock by default? - defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked ExposeInvitationURLsToTeamAdminConfig FeatureTTLUnlimited + defFeatureStatus = withStatus FeatureStatusDisabled LockStatusLocked ExposeInvitationURLsToTeamAdminConfig FeatureTTLUnlimited objectSchema = pure ExposeInvitationURLsToTeamAdminConfig instance ToSchema ExposeInvitationURLsToTeamAdminConfig where diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 12b71cda21..23c9655180 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -856,7 +856,7 @@ instance SetFeatureConfig db MLSConfig where instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where getConfigForServer = - input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin . unDefaults . unImplicitLockStatus) + input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin) instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) From cbcbe8198f242af66a1eaabfb1a9dd8db23cfdb9 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 13:43:11 +0200 Subject: [PATCH 20/66] galley: validate based on state transition instead of target state. --- .../galley/src/Galley/API/Teams/Features.hs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 23c9655180..da1930472d 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -860,15 +860,17 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) - setConfigForTeam tid wsnl = - -- Only allow enabling this feature for teams which are in the admin-configured allowlist. - case wssStatus wsnl of - FeatureStatusDisabled -> persistAndPushEvent @db tid wsnl - FeatureStatusEnabled -> do - allowedTeams <- exposeInvitationURLsTeamAllowlist <$> wsConfig <$> getConfigForServer @db - if elem tid allowedTeams - then persistAndPushEvent @db tid wsnl - else throwS @OperationDenied + setConfigForTeam tid wsnl = do + -- Only allow enabling this feature for teams which are in the + -- admin-configured allowlist. If a team has the feature enabled, but is not + -- currently in the allowlist, permit them to disable the feature. + teamAllowed <- getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid + oldState <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsStatus + let newState = wssStatus wsnl + case (teamAllowed, oldState, newState) of + (True, _ , _) -> persistAndPushEvent @db tid wsnl + (False, FeatureStatusEnabled, FeatureStatusDisabled) -> persistAndPushEvent @db tid wsnl + (_, _ , _) -> throwS @OperationDenied instance GetFeatureConfig db ExposeInvitationURLsTeamAllowlistConfig where getConfigForServer = From 3c3f2acb98b74f8f86feb38d2175843645b3e18a Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 14:11:02 +0200 Subject: [PATCH 21/66] galley: add missing imports to galley-schema --- services/galley/galley.cabal | 1 + services/galley/schema/src/Main.hs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/services/galley/galley.cabal b/services/galley/galley.cabal index 9ed8d77715..96a9c6b83c 100644 --- a/services/galley/galley.cabal +++ b/services/galley/galley.cabal @@ -662,6 +662,7 @@ executable galley-schema V70_MLSCipherSuite V71_MemberClientKeypackage V72_DropManagedConversations + V73_ExposeInvitationsToTeamAdmin hs-source-dirs: schema/src default-extensions: diff --git a/services/galley/schema/src/Main.hs b/services/galley/schema/src/Main.hs index 13e3401d5f..b1c8d67ef3 100644 --- a/services/galley/schema/src/Main.hs +++ b/services/galley/schema/src/Main.hs @@ -75,6 +75,7 @@ import qualified V69_MLSProposal import qualified V70_MLSCipherSuite import qualified V71_MemberClientKeypackage import qualified V72_DropManagedConversations +import qualified V73_ExposeInvitationsToTeamAdmin main :: IO () main = do @@ -135,7 +136,8 @@ main = do V69_MLSProposal.migration, V70_MLSCipherSuite.migration, V71_MemberClientKeypackage.migration, - V72_DropManagedConversations.migration + V72_DropManagedConversations.migration, + V73_ExposeInvitationsToTeamAdmin.migration -- When adding migrations here, don't forget to update -- 'schemaVersion' in Galley.Cassandra -- (see also docs/developer/cassandra-interaction.md) From 655db6074008a4af659792e8f5d7d49d8a5c7971 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 17:10:22 +0200 Subject: [PATCH 22/66] galley: note to self. --- services/galley/src/Galley/API/Teams/Features.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index da1930472d..7681c68571 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -858,6 +858,10 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where getConfigForServer = input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin) + -- TODO(sysvinit): override getConfigForTeam here to prevent teams not in the + -- allowlist from using this feature? or do we just trust that what's in the db + -- is validated? + instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = do From a88e872698bcbcd2e09b4b73933e7e0bc46d036c Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 17:10:54 +0200 Subject: [PATCH 23/66] brig: add function for checking invite link visibility in galley --- services/brig/src/Brig/IO/Intra.hs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/services/brig/src/Brig/IO/Intra.hs b/services/brig/src/Brig/IO/Intra.hs index 51695d0a0f..937e0456c4 100644 --- a/services/brig/src/Brig/IO/Intra.hs +++ b/services/brig/src/Brig/IO/Intra.hs @@ -59,6 +59,7 @@ module Brig.IO.Intra getTeamSearchVisibility, getAllFeatureConfigsForUser, getVerificationCodeEnabled, + getTeamExposeInvitationURLsToTeamAdmin, -- * Legalhold guardLegalhold, @@ -1367,6 +1368,28 @@ getTeamSearchVisibility tid = paths ["i", "teams", toByteString' tid, "search-visibility"] . expect2xx +getTeamExposeInvitationURLsToTeamAdmin :: + ( MonadLogger m, + MonadReader Env m, + MonadIO m, + MonadMask m, + MonadHttp m, + HasRequestId m + ) => + TeamId -> + m Bool +getTeamExposeInvitationURLsToTeamAdmin tid = do + debug $ remote "galley" . msg (val "Get expose invitation URLs to team admin settings") + response <- galleyRequest GET req + status <- wsStatus <$> decodeBody @(WithStatus ExposeInvitationURLsToTeamAdminConfig) "galley" response + case status of + FeatureStatusEnabled -> pure True + FeatureStatusDisabled -> pure False + where + req = + paths ["i", "teams", toByteString' tid, "features", featureNameBS @ExposeInvitationURLsToTeamAdminConfig] + . expect2xx + getVerificationCodeEnabled :: ( MonadReader Env m, MonadIO m, From b453550528e37580ae3b41a6da26e610551493a5 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 17:11:39 +0200 Subject: [PATCH 24/66] brig: add checks for exposeInvitationUrlsToTeamAdmin in team invite endpoints --- services/brig/src/Brig/Team/DB.hs | 78 ++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 0f6063c4d6..1a8e39037f 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -37,16 +37,25 @@ module Brig.Team.DB ) where +import Bilge.IO +import Bilge.RPC import Brig.Data.Instances () import Brig.Data.Types as T +import Brig.App as App import Brig.Options +import Brig.IO.Intra +import Brig.Team.Template +import Brig.Template (renderTextWithBranding) import Cassandra as C +import Control.Lens (view) +import Control.Monad.Catch (MonadMask) import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C import Data.Id import Data.Json.Util (UTCTimeMillis, toUTCTimeMillis) import Data.Range -import Data.Text.Ascii (encodeBase64Url) +import Data.Text.Ascii (encodeBase64Url, toText) +import Data.Text.Lazy (toStrict) import Data.Time.Clock import Data.UUID.V4 import Imports @@ -76,7 +85,7 @@ data InvitationByEmail | InvitationByEmailMoreThanOne insertInvitation :: - MonadClient m => + (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => InvitationId -> TeamId -> Role -> @@ -90,7 +99,9 @@ insertInvitation :: m (Invitation, InvitationCode) insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout = do code <- liftIO mkInvitationCode - let inv = Invitation t role iid now minviter email inviteeName phone + showUrl <- getTeamExposeInvitationURLsToTeamAdmin t + url <- if showUrl then Just <$> mkInviteUrl t code else pure Nothing + let inv = Invitation t role iid now minviter email inviteeName phone url retry x5 . batch $ do setType BatchLogged setConsistency LocalQuorum @@ -107,15 +118,17 @@ insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName cqlInvitationByEmail :: PrepQuery W (Email, TeamId, InvitationId, InvitationCode, Int32) () cqlInvitationByEmail = "INSERT INTO team_invitation_email (email, team, invitation, code) VALUES (?, ?, ?, ?) USING TTL ?" -lookupInvitation :: MonadClient m => TeamId -> InvitationId -> m (Maybe Invitation) -lookupInvitation t r = - fmap toInvitation - <$> retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) +lookupInvitation :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => TeamId -> InvitationId -> m (Maybe Invitation) +lookupInvitation t r = do + showUrl <- getTeamExposeInvitationURLsToTeamAdmin t + inv <- retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) + inv' <- traverse (toInvitation showUrl) inv + pure inv' where - cqlInvitation :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone) - cqlInvitation = "SELECT team, role, id, created_at, created_by, email, name, phone FROM team_invitation WHERE team = ? AND id = ?" + cqlInvitation :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) + cqlInvitation = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? AND id = ?" -lookupInvitationByCode :: MonadClient m => InvitationCode -> m (Maybe Invitation) +lookupInvitationByCode :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => InvitationCode -> m (Maybe Invitation) lookupInvitationByCode i = lookupInvitationInfo i >>= \case Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId @@ -135,12 +148,13 @@ lookupInvitationCodeEmail t r = retry x1 (query1 cqlInvitationCodeEmail (params cqlInvitationCodeEmail :: PrepQuery R (TeamId, InvitationId) (InvitationCode, Email) cqlInvitationCodeEmail = "SELECT code, email FROM team_invitation WHERE team = ? AND id = ?" -lookupInvitations :: MonadClient m => TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> m (ResultPage Invitation) +lookupInvitations :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> m (ResultPage Invitation) lookupInvitations team start (fromRange -> size) = do page <- case start of Just ref -> retry x1 $ paginate cqlSelectFrom (paramsP LocalQuorum (team, ref) (size + 1)) Nothing -> retry x1 $ paginate cqlSelect (paramsP LocalQuorum (Identity team) (size + 1)) - pure $ toResult (hasMore page) $ map toInvitation (trim page) + showUrl <- getTeamExposeInvitationURLsToTeamAdmin team + toResult (hasMore page) <$> traverse (toInvitation showUrl) (trim page) where trim p = take (fromIntegral size) (result p) toResult more invs = @@ -149,10 +163,10 @@ lookupInvitations team start (fromRange -> size) = do { result = invs, hasMore = more } - cqlSelect :: PrepQuery R (Identity TeamId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone) - cqlSelect = "SELECT team, role, id, created_at, created_by, email, name, phone FROM team_invitation WHERE team = ? ORDER BY id ASC" - cqlSelectFrom :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone) - cqlSelectFrom = "SELECT team, role, id, created_at, created_by, email, name, phone FROM team_invitation WHERE team = ? AND id > ? ORDER BY id ASC" + cqlSelect :: PrepQuery R (Identity TeamId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) + cqlSelect = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? ORDER BY id ASC" + cqlSelectFrom :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) + cqlSelectFrom = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? AND id > ? ORDER BY id ASC" deleteInvitation :: MonadClient m => TeamId -> InvitationId -> m () deleteInvitation t i = do @@ -195,7 +209,7 @@ lookupInvitationInfo ic@(InvitationCode c) cqlInvitationInfo :: PrepQuery R (Identity InvitationCode) (TeamId, InvitationId) cqlInvitationInfo = "SELECT team, id FROM team_invitation_info WHERE code = ?" -lookupInvitationByEmail :: (Log.MonadLogger m, MonadClient m) => Email -> m (Maybe Invitation) +lookupInvitationByEmail :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => Email -> m (Maybe Invitation) lookupInvitationByEmail e = lookupInvitationInfoByEmail e >>= \case InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId @@ -230,6 +244,9 @@ countInvitations t = -- | brig used to not store the role, so for migration we allow this to be empty and fill in the -- default here. toInvitation :: + ( MonadReader Env m + ) => + Bool -> ( TeamId, Maybe Role, InvitationId, @@ -237,8 +254,27 @@ toInvitation :: Maybe UserId, Email, Maybe Name, - Maybe Phone + Maybe Phone, + InvitationCode ) -> - Invitation -toInvitation (t, r, i, tm, minviter, e, inviteeName, p) = - Invitation t (fromMaybe defaultRole r) i tm minviter e inviteeName p + m Invitation +toInvitation showUrl (t, r, i, tm, minviter, e, inviteeName, p, code) = do + url <- if showUrl then Just <$> mkInviteUrl t code else pure Nothing + pure $ Invitation t (fromMaybe defaultRole r) i tm minviter e inviteeName p url + +mkInviteUrl :: + ( MonadReader Env m + ) => + TeamId -> + InvitationCode -> + m Text +mkInviteUrl team (InvitationCode c) = do + template <- invitationEmailUrl . invitationEmail . snd <$> teamTemplates Nothing + branding <- view App.templateBranding + let url = toStrict $ renderTextWithBranding template replace branding + pure url + where + replace "team" = idToText team + replace "code" = toText c + replace x = x + From b16c032305a745130583f1f0f9bd00bb40ad1c60 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 12 Sep 2022 17:13:23 +0200 Subject: [PATCH 25/66] brig: swap wrapClient for wrapHttp to handle extra typeclass constraints in invites. --- services/brig/src/Brig/API/Internal.hs | 2 +- services/brig/src/Brig/API/User.hs | 2 +- services/brig/src/Brig/Team/API.hs | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/services/brig/src/Brig/API/Internal.hs b/services/brig/src/Brig/API/Internal.hs index 4a548f766d..db8ab4b248 100644 --- a/services/brig/src/Brig/API/Internal.hs +++ b/services/brig/src/Brig/API/Internal.hs @@ -556,7 +556,7 @@ listActivatedAccounts elh includePendingInvitations = do case (accountStatus account, includePendingInvitations, emailIdentity ident) of (PendingInvitation, False, _) -> pure False (PendingInvitation, True, Just email) -> do - hasInvitation <- isJust <$> wrapClient (lookupInvitationByEmail email) + hasInvitation <- isJust <$> wrapHttp (lookupInvitationByEmail email) unless hasInvitation $ do -- user invited via scim should expire together with its invitation API.deleteUserNoVerify (userId . accountUser $ account) diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index ce19c9bdde..b16ba7e470 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -413,7 +413,7 @@ createUser new = do findTeamInvitation (Just e) c = lift (wrapClient $ Team.lookupInvitationInfo c) >>= \case Just ii -> do - inv <- lift . wrapClient $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) + inv <- lift . wrapHttp $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) case (inv, Team.inInviteeEmail <$> inv) of (Just invite, Just em) | e == userEmailKey em -> do diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index 74bda2dead..65980ed54e 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -383,7 +383,7 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do now <- liftIO =<< view currentTime timeout <- setTeamInvitationTimeout <$> view settings (newInv, code) <- - wrapClient $ + wrapHttp $ DB.insertInvitation iid tid @@ -412,7 +412,7 @@ listInvitationsH (_ ::: uid ::: tid ::: start ::: size) = do listInvitations :: UserId -> TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> (Handler r) Public.InvitationList listInvitations uid tid start size = do ensurePermissions uid tid [AddTeamMember] - rs <- lift $ wrapClient $ DB.lookupInvitations tid start size + rs <- lift $ wrapHttp $ DB.lookupInvitations tid start size pure $! Public.InvitationList (DB.resultList rs) (DB.resultHasMore rs) getInvitationH :: JSON ::: UserId ::: TeamId ::: InvitationId -> (Handler r) Response @@ -425,7 +425,7 @@ getInvitationH (_ ::: uid ::: tid ::: iid) = do getInvitation :: UserId -> TeamId -> InvitationId -> (Handler r) (Maybe Public.Invitation) getInvitation uid tid iid = do ensurePermissions uid tid [AddTeamMember] - lift $ wrapClient $ DB.lookupInvitation tid iid + lift $ wrapHttp $ DB.lookupInvitation tid iid getInvitationByCodeH :: JSON ::: Public.InvitationCode -> (Handler r) Response getInvitationByCodeH (_ ::: c) = do @@ -433,7 +433,7 @@ getInvitationByCodeH (_ ::: c) = do getInvitationByCode :: Public.InvitationCode -> (Handler r) Public.Invitation getInvitationByCode c = do - inv <- lift . wrapClient $ DB.lookupInvitationByCode c + inv <- lift . wrapHttp $ DB.lookupInvitationByCode c maybe (throwStd $ errorToWai @'E.InvalidInvitationCode) pure inv headInvitationByEmailH :: JSON ::: Email -> (Handler r) Response @@ -453,7 +453,7 @@ getInvitationByEmailH (_ ::: email) = getInvitationByEmail :: Email -> (Handler r) Public.Invitation getInvitationByEmail email = do - inv <- lift $ wrapClient $ DB.lookupInvitationByEmail email + inv <- lift $ wrapHttp $ DB.lookupInvitationByEmail email maybe (throwStd (notFound "Invitation not found")) pure inv suspendTeamH :: JSON ::: TeamId -> (Handler r) Response From eabaf06ea3dcc1e1b87bc4efe3391dcae624d6a8 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Tue, 13 Sep 2022 09:31:03 +0200 Subject: [PATCH 26/66] brig: formatting and style. --- services/brig/src/Brig/Team/DB.hs | 63 ++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 1a8e39037f..cf8eaa9c2b 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -39,11 +39,11 @@ where import Bilge.IO import Bilge.RPC +import Brig.App as App import Brig.Data.Instances () import Brig.Data.Types as T -import Brig.App as App -import Brig.Options import Brig.IO.Intra +import Brig.Options import Brig.Team.Template import Brig.Template (renderTextWithBranding) import Cassandra as C @@ -85,7 +85,13 @@ data InvitationByEmail | InvitationByEmailMoreThanOne insertInvitation :: - (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => + ( Log.MonadLogger m, + MonadReader Env m, + MonadMask m, + MonadHttp m, + HasRequestId m, + MonadClient m + ) => InvitationId -> TeamId -> Role -> @@ -118,17 +124,35 @@ insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName cqlInvitationByEmail :: PrepQuery W (Email, TeamId, InvitationId, InvitationCode, Int32) () cqlInvitationByEmail = "INSERT INTO team_invitation_email (email, team, invitation, code) VALUES (?, ?, ?, ?) USING TTL ?" -lookupInvitation :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => TeamId -> InvitationId -> m (Maybe Invitation) +lookupInvitation :: + ( Log.MonadLogger m, + MonadReader Env m, + MonadMask m, + MonadHttp m, + HasRequestId m, + MonadClient m + ) => + TeamId -> + InvitationId -> + m (Maybe Invitation) lookupInvitation t r = do showUrl <- getTeamExposeInvitationURLsToTeamAdmin t inv <- retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) - inv' <- traverse (toInvitation showUrl) inv - pure inv' + traverse (toInvitation showUrl) inv where cqlInvitation :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) cqlInvitation = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? AND id = ?" -lookupInvitationByCode :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => InvitationCode -> m (Maybe Invitation) +lookupInvitationByCode :: + ( Log.MonadLogger m, + MonadReader Env m, + MonadMask m, + MonadHttp m, + HasRequestId m, + MonadClient m + ) => + InvitationCode -> + m (Maybe Invitation) lookupInvitationByCode i = lookupInvitationInfo i >>= \case Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId @@ -148,7 +172,18 @@ lookupInvitationCodeEmail t r = retry x1 (query1 cqlInvitationCodeEmail (params cqlInvitationCodeEmail :: PrepQuery R (TeamId, InvitationId) (InvitationCode, Email) cqlInvitationCodeEmail = "SELECT code, email FROM team_invitation WHERE team = ? AND id = ?" -lookupInvitations :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> m (ResultPage Invitation) +lookupInvitations :: + ( Log.MonadLogger m, + MonadReader Env m, + MonadMask m, + MonadHttp m, + HasRequestId m, + MonadClient m + ) => + TeamId -> + Maybe InvitationId -> + Range 1 500 Int32 -> + m (ResultPage Invitation) lookupInvitations team start (fromRange -> size) = do page <- case start of Just ref -> retry x1 $ paginate cqlSelectFrom (paramsP LocalQuorum (team, ref) (size + 1)) @@ -209,7 +244,16 @@ lookupInvitationInfo ic@(InvitationCode c) cqlInvitationInfo :: PrepQuery R (Identity InvitationCode) (TeamId, InvitationId) cqlInvitationInfo = "SELECT team, id FROM team_invitation_info WHERE code = ?" -lookupInvitationByEmail :: (Log.MonadLogger m, MonadReader Env m, MonadMask m, MonadHttp m, HasRequestId m, MonadClient m) => Email -> m (Maybe Invitation) +lookupInvitationByEmail :: + ( Log.MonadLogger m, + MonadReader Env m, + MonadMask m, + MonadHttp m, + HasRequestId m, + MonadClient m + ) => + Email -> + m (Maybe Invitation) lookupInvitationByEmail e = lookupInvitationInfoByEmail e >>= \case InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId @@ -277,4 +321,3 @@ mkInviteUrl team (InvitationCode c) = do replace "team" = idToText team replace "code" = toText c replace x = x - From 73d01356174c4950582e49b6b6bcb53246f6e41b Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Tue, 13 Sep 2022 10:39:58 +0200 Subject: [PATCH 27/66] galley: override getConfigForTeam for exposeInvitationURLsToTeamAdmin The logic for determining whether this feature is enabled and/or locked for a given team is more nuanced than the default implementation. --- .../galley/src/Galley/API/Teams/Features.hs | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 7681c68571..20aa8b6588 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -858,16 +858,44 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where getConfigForServer = input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin) - -- TODO(sysvinit): override getConfigForTeam here to prevent teams not in the - -- allowlist from using this feature? or do we just trust that what's in the db - -- is validated? + getConfigForTeam tid = do + serverConfig <- getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig + teamAllowed <- getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid + teamDbStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus + pure $ computeConfigForTeam teamAllowed teamDbStatus serverConfig + where + computeConfigForTeam teamAllowed teamDbStatus defConfig = + case wsLockStatus defConfig of + LockStatusLocked -> + makeConfig LockStatusLocked (if teamAllowed then wsStatus defConfig else FeatureStatusDisabled) + LockStatusUnlocked -> + makeConfig + (computeTeamLockStatus teamAllowed teamDbStatus) + (computeTeamStatus teamAllowed teamDbStatus $ wsStatus defConfig) + + makeConfig lockStatus status = + withStatus + status + lockStatus + ExposeInvitationURLsToTeamAdminConfig + FeatureTTLUnlimited + + computeTeamLockStatus True _ = LockStatusUnlocked + computeTeamLockStatus False (Just FeatureStatusEnabled) = LockStatusUnlocked + computeTeamLockStatus _ _ = LockStatusLocked + + computeTeamStatus True Nothing defaultStatus = defaultStatus + computeTeamStatus True (Just status) _ = status + computeTeamStatus False (Just FeatureStatusEnabled) _ = FeatureStatusEnabled + computeTeamStatus False _ _ = FeatureStatusDisabled + instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = do - -- Only allow enabling this feature for teams which are in the - -- admin-configured allowlist. If a team has the feature enabled, but is not - -- currently in the allowlist, permit them to disable the feature. + -- Only allow enabling this feature for teams which are in the admin-configured allowlist. If + -- a team has the feature enabled, but is not currently in the allowlist, permit them to + -- disable the feature. teamAllowed <- getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid oldState <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsStatus let newState = wssStatus wsnl From 74e5fe92f1261af1db4895eb2d2513c6ae22c449 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Tue, 13 Sep 2022 11:55:39 +0200 Subject: [PATCH 28/66] galley: formatting. --- services/galley/src/Galley/API/Teams/Features.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 20aa8b6588..f2caee0ea5 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -889,7 +889,6 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where computeTeamStatus False (Just FeatureStatusEnabled) _ = FeatureStatusEnabled computeTeamStatus False _ _ = FeatureStatusDisabled - instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = do @@ -900,9 +899,9 @@ instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where oldState <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsStatus let newState = wssStatus wsnl case (teamAllowed, oldState, newState) of - (True, _ , _) -> persistAndPushEvent @db tid wsnl + (True, _, _) -> persistAndPushEvent @db tid wsnl (False, FeatureStatusEnabled, FeatureStatusDisabled) -> persistAndPushEvent @db tid wsnl - (_, _ , _) -> throwS @OperationDenied + (_, _, _) -> throwS @OperationDenied instance GetFeatureConfig db ExposeInvitationURLsTeamAllowlistConfig where getConfigForServer = From b1bb6ef7aa4de2d7c9db6c4f55f1577b57352257 Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Tue, 13 Sep 2022 14:31:46 +0200 Subject: [PATCH 29/66] galley: style --- services/galley/src/Galley/API/Teams/Features.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index f2caee0ea5..b6a88802d7 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -859,10 +859,10 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin) getConfigForTeam tid = do - serverConfig <- getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig - teamAllowed <- getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid - teamDbStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus - pure $ computeConfigForTeam teamAllowed teamDbStatus serverConfig + computeConfigForTeam + <$> (getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid) + <*> (TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus) + <*> getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig where computeConfigForTeam teamAllowed teamDbStatus defConfig = case wsLockStatus defConfig of From f51f5103f0ae4fad2e07bed67fe0a573e6c78142 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Tue, 13 Sep 2022 17:19:07 +0200 Subject: [PATCH 30/66] Fix type checking issue --- services/galley/test/integration/API/Teams/Feature.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index 0660b3f836..59b9cc560f 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -991,7 +991,9 @@ testAllFeatures = do Public.afcGuestLink = Public.withStatus FeatureStatusEnabled Public.LockStatusUnlocked Public.GuestLinksConfig Public.FeatureTTLUnlimited, Public.afcSndFactorPasswordChallenge = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked Public.SndFactorPasswordChallengeConfig Public.FeatureTTLUnlimited, Public.afcMLS = Public.withStatus FeatureStatusDisabled Public.LockStatusUnlocked (Public.MLSConfig [] ProtocolProteusTag [MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519] MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519) Public.FeatureTTLUnlimited, - Public.afcSearchVisibilityInboundConfig = Public.withStatus FeatureStatusDisabled Public.LockStatusUnlocked Public.SearchVisibilityInboundConfig Public.FeatureTTLUnlimited + Public.afcSearchVisibilityInboundConfig = Public.withStatus FeatureStatusDisabled Public.LockStatusUnlocked Public.SearchVisibilityInboundConfig Public.FeatureTTLUnlimited, + Public.afcExposeInvitationURLsToTeamAdmin = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited, + Public.afcExposeInvitationURLsTeamAllowlist = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked (Public.ExposeInvitationURLsTeamAllowlistConfig []) Public.FeatureTTLUnlimited } testFeatureConfigConsistency :: TestM () From 658b160a75164d379370fdc726bd37d2bcc58eb0 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 14 Sep 2022 08:41:07 +0200 Subject: [PATCH 31/66] Fix golden tests --- .../testObject_InvitationList_team_10.json | 3 +- .../testObject_InvitationList_team_11.json | 3 +- .../testObject_InvitationList_team_13.json | 21 ++++++--- .../testObject_InvitationList_team_15.json | 15 ++++--- .../testObject_InvitationList_team_16.json | 3 +- .../testObject_InvitationList_team_17.json | 3 +- .../testObject_InvitationList_team_2.json | 3 +- .../testObject_InvitationList_team_20.json | 6 ++- .../testObject_InvitationList_team_4.json | 24 ++++++---- .../testObject_InvitationList_team_6.json | 45 ++++++++++++------- .../testObject_InvitationList_team_7.json | 9 ++-- .../testObject_InvitationList_team_8.json | 6 ++- .../testObject_InvitationList_team_9.json | 9 ++-- .../golden/testObject_Invitation_team_1.json | 3 +- .../golden/testObject_Invitation_team_10.json | 3 +- .../golden/testObject_Invitation_team_11.json | 3 +- .../golden/testObject_Invitation_team_12.json | 3 +- .../golden/testObject_Invitation_team_13.json | 3 +- .../golden/testObject_Invitation_team_14.json | 3 +- .../golden/testObject_Invitation_team_15.json | 3 +- .../golden/testObject_Invitation_team_16.json | 3 +- .../golden/testObject_Invitation_team_17.json | 3 +- .../golden/testObject_Invitation_team_18.json | 3 +- .../golden/testObject_Invitation_team_19.json | 3 +- .../golden/testObject_Invitation_team_2.json | 3 +- .../golden/testObject_Invitation_team_20.json | 3 +- .../golden/testObject_Invitation_team_3.json | 3 +- .../golden/testObject_Invitation_team_4.json | 3 +- .../golden/testObject_Invitation_team_5.json | 3 +- .../golden/testObject_Invitation_team_6.json | 3 +- .../golden/testObject_Invitation_team_7.json | 3 +- .../golden/testObject_Invitation_team_8.json | 3 +- .../golden/testObject_Invitation_team_9.json | 3 +- 33 files changed, 140 insertions(+), 70 deletions(-) diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_10.json b/libs/wire-api/test/golden/testObject_InvitationList_team_10.json index f5a607418e..c06f56c3cf 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_10.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_10.json @@ -9,7 +9,8 @@ "name": "P𥖧\u0006'e\u0010\u001d\"\u0011K󽗨Fcvm[\"Sc}U𑊒􂌨󿔟~!E􀖇\u000bV", "phone": null, "role": "member", - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_11.json b/libs/wire-api/test/golden/testObject_InvitationList_team_11.json index 621efe7906..3b5e6bce7f 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_11.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_11.json @@ -9,7 +9,8 @@ "name": "G\\,\u0000=ෝI-w󠀹}𠉭抳-92\u0013@\u0006\u001f\\F\u001a\"-r꒫6\u000fඬ\u001f*}c󼘹\u001f\u0007T8m@旅M\u0012#MIq\r4nW􍦐y\u0005Ud룫#𫶒5\n\u0002V]𨡀\"󶂃𩫘0:ﲼ𮭩+\u0001\u000bP󹎷X镟􅔧.\u0019N\"𬋻", "phone": "+872574694", "role": "admin", - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "url" :null }, { "created_at": "1864-05-09T23:06:13.648Z", @@ -19,7 +20,8 @@ "name": "叕5q}B\u0001𦌜`イw\\X@󼶝𢼈7Mw,*z{𠚷&~", "phone": "+143031479742", "role": "partner", - "team": "00000000-0000-0001-0000-000000000001" + "team": "00000000-0000-0001-0000-000000000001", + "url" :null }, { "created_at": "1864-05-09T10:37:03.809Z", @@ -29,7 +31,8 @@ "name": "V􈫮\u0010qYヒCU\u000e􄕀fQJ\u0005ਓq+\u0007\u0016󱊸\u0011@𤠼`坟qh+𬾬A7𦄡Y \u0011Tㅎ1_􈩇#B<􂡁;a6o=", "phone": "+236346166386230", "role": "partner", - "team": "00000001-0000-0000-0000-000000000000" + "team": "00000001-0000-0000-0000-000000000000", + "url" :null }, { "created_at": "1864-05-09T04:46:03.504Z", @@ -39,7 +42,8 @@ "name": ",􃠾{ս\u000c𬕻Uh죙\t\u001b\u0004\u0001O@\u001a_\u0002D􎰥𦀛\u0016g}", "phone": "+80162248", "role": "admin", - "team": "00000001-0000-0001-0000-000100000001" + "team": "00000001-0000-0001-0000-000100000001", + "url" :null }, { "created_at": "1864-05-09T12:53:52.047Z", @@ -49,7 +53,8 @@ "name": null, "phone": null, "role": "owner", - "team": "00000000-0000-0001-0000-000100000001" + "team": "00000000-0000-0001-0000-000100000001", + "url" :null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_16.json b/libs/wire-api/test/golden/testObject_InvitationList_team_16.json index fc14ac96bf..535fe0678e 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_16.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_16.json @@ -9,7 +9,8 @@ "name": "E𝘆YM<󾪤j􆢆\r􇳗O󴟴MCU\u001eI󳊃m𔒷hG\u0012|:P􅛽Vj\u001c\u0000ffgG)K{􁇏7x5󱟰𪔘\n\u000clT􆊞", "phone": "+36515555", "role": "owner", - "team": "00000001-0000-0001-0000-000100000001" + "team": "00000001-0000-0001-0000-000100000001", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_17.json b/libs/wire-api/test/golden/testObject_InvitationList_team_17.json index c2c9ba044a..eba7991502 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_17.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_17.json @@ -9,7 +9,8 @@ "name": null, "phone": null, "role": "partner", - "team": "00000001-0000-0000-0000-000100000000" + "team": "00000001-0000-0000-0000-000100000000", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_2.json b/libs/wire-api/test/golden/testObject_InvitationList_team_2.json index e2f2601fb1..6a9c45aac6 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_2.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_2.json @@ -9,7 +9,8 @@ "name": "fuC9p􌌅A𧻢\u000c\u0005\u000e刣N룞_?oCX.U\r𧾠W腈󽥝\u0013\t[錣\u0016/⃘A𣚁𪔍\u0014H𠽙\u0002𨯠\u0004𨒤o\u0013", "phone": "+851333011", "role": "owner", - "team": "00000000-0000-0000-0000-000000000001" + "team": "00000000-0000-0000-0000-000000000001", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_20.json b/libs/wire-api/test/golden/testObject_InvitationList_team_20.json index 1b50ca8071..26a5ab0134 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_20.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_20.json @@ -9,7 +9,8 @@ "name": null, "phone": "+745177056001783", "role": "partner", - "team": "00000001-0000-0001-0000-000000000000" + "team": "00000001-0000-0001-0000-000000000000", + "url": null }, { "created_at": "1864-05-09T18:56:29.712Z", @@ -19,7 +20,8 @@ "name": "YPf╞:\u0005Ỉ&\u0018\u0011󽧛%ꦡk𪯋􅥏:Q\u0005F+\u0008b8Jh􌎓K\u0007\u001dY\u0004􃏡\u000f󽝰\u0016 􁗠6>I󾉩B$z?𤢾wECB\u001e𥼬덄\"W𗤞󲴂@\u001eg)\u0001m!-U􇧦󵜰o\u0006a\u0004𭂢;R􂪧kgT􍆈f\u0004\u001e\rp𓎎󿉊X/􄂲)\u00025.Ym󵳬n싟N\u0013𫅄]?'𠴺a4\"󳟾!i5\u001e\u001dC14", "phone": null, "role": "owner", - "team": "00000001-0000-0000-0000-000100000000" + "team": "00000001-0000-0000-0000-000100000000", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_4.json b/libs/wire-api/test/golden/testObject_InvitationList_team_4.json index e41e76da52..3063b4fdeb 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_4.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_4.json @@ -9,7 +9,8 @@ "name": "R6𠥄𠮥VQ𭴢\u001a\u0001𬄺0C􉶍\u001bR𭗈𞡊@韉Z?\u0002𩖫􄭦e}\u0001\u0017\u0004m𭉂\u001f]󰺞𮉗􂨮󰶌\u0008\u0011zfw-5𝝖\u0018􃸂 \u0019e\u0014|㡚Vo{􆳗\u0013#\u001fS꿻&zz𧏏9𢱋,\u000f\u000c\u0001p󺜰\u0010𧵪􂸑.&󳢨kZ쓿u\u0008왌􎴟n:􍝋D$.Q", "phone": "+60506387292", "role": "admin", - "team": "00000000-0000-0001-0000-000000000000" + "team": "00000000-0000-0001-0000-000000000000", + "url": null }, { "created_at": "1864-05-09T09:00:02.901Z", @@ -19,7 +20,8 @@ "name": "\u0012}q\u0018=SA\u0003x\t\u0003\\\u000b[\u0008)(\u001b]𡋃Y\u000b@pꈫl뀉𦛌\u0000\t􌤢\u00011\u0011\u0005󹝃\"i猔\u0019\u0008\u0006\u000f\u0012v\u0006", "phone": "+913945015", "role": "admin", - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "url": null }, { "created_at": "1864-05-09T11:10:31.203Z", @@ -29,7 +31,8 @@ "name": "&􂧽Ec\u0000㼓}k󼾘l𪍯\u001fJ\u00190^.+F\u0000\u000c$'`!\u0017[p󾓉}>E0y𗢸#4I\u0007𐐡jc\u001bgt埉􊹘P\u0014!􋣥E93'Y$YL뜦b\r:,𬘞\u000e𥚟y\u0003;􃺹􌛖z4z-D􋰳a𡽜6𨏝r󼖨󱌂J\u0010밆", "phone": "+17046334", "role": "member", - "team": "00000001-0000-0000-0000-000000000001" + "team": "00000001-0000-0000-0000-000000000001", + "url": null }, { "created_at": "1864-05-09T23:41:34.529Z", @@ -39,7 +42,8 @@ "name": "Ft*O1\u0008&\u000e\u0018<𑨛􊰋m\n\u0014\u0012; \u0003󱚥\u0011􂬫\"k.T󹴑[[\u001c\u0004{j`\u001d󳟞c􄖫{\u001a\u001dQY𬨕\t\u0015y\t𠓳j󼿁W ", "phone": null, "role": "owner", - "team": "00000000-0000-0000-0000-000000000000" + "team": "00000000-0000-0000-0000-000000000000", + "url": null }, { "created_at": "1864-05-09T00:29:17.658Z", @@ -49,7 +53,8 @@ "name": null, "phone": "+918848647685283", "role": "admin", - "team": "00000001-0000-0000-0000-000100000000" + "team": "00000001-0000-0000-0000-000100000000", + "url": null }, { "created_at": "1864-05-09T13:34:37.117Z", @@ -59,7 +64,8 @@ "name": "Lo\r􎒩B𗚰_v󰔢􆍶󻀬􊽦9\u0002vyQ🖰&W󻟑𠸘􇹬'􁔫:𤟗𡶘􏹠}-o󿜊le8Zp󺩐􋾙)nK\u00140⛟0DE\u0015K$io\u001e|Ip2ClnU𬖍", "phone": "+2239859474784", "role": "owner", - "team": "00000001-0000-0001-0000-000100000000" + "team": "00000001-0000-0001-0000-000100000000", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_6.json b/libs/wire-api/test/golden/testObject_InvitationList_team_6.json index 2285c8dc7a..03aa3d0485 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_6.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_6.json @@ -9,7 +9,8 @@ "name": null, "phone": null, "role": "admin", - "team": "00000001-0000-0001-0000-000100000000" + "team": "00000001-0000-0001-0000-000100000000", + "url": null }, { "created_at": "1864-05-09T11:26:36.672Z", @@ -19,7 +20,8 @@ "name": null, "phone": "+85999765", "role": "admin", - "team": "00000000-0000-0000-0000-000100000000" + "team": "00000000-0000-0000-0000-000100000000", + "url": null }, { "created_at": "1864-05-09T00:31:56.241Z", @@ -29,7 +31,8 @@ "name": null, "phone": "+150835819626453", "role": "owner", - "team": "00000001-0000-0000-0000-000100000000" + "team": "00000001-0000-0000-0000-000100000000", + "url": null }, { "created_at": "1864-05-09T21:10:47.237Z", @@ -39,7 +42,8 @@ "name": "YBc\r웶8{\\\n􋸓+\u0008\u0016'<\u0004􈄿Z\u0007nOb􋨴􌸖𩮤}2o@v/", "phone": "+787465997389", "role": "member", - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "url": null } ] } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_1.json b/libs/wire-api/test/golden/testObject_Invitation_team_1.json index 9d611aa22e..ee4489e209 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_1.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_1.json @@ -6,5 +6,6 @@ "name": null, "phone": "+54687000371", "role": "admin", - "team": "00000002-0000-0001-0000-000200000002" + "team": "00000002-0000-0001-0000-000200000002", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_10.json b/libs/wire-api/test/golden/testObject_Invitation_team_10.json index 447daf009c..9c189f7c13 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_10.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_10.json @@ -6,5 +6,6 @@ "name": null, "phone": "+957591063736", "role": "partner", - "team": "00000002-0000-0001-0000-000100000001" + "team": "00000002-0000-0001-0000-000100000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_11.json b/libs/wire-api/test/golden/testObject_Invitation_team_11.json index 09e6c67ff7..a1d4b2e572 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_11.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_11.json @@ -6,5 +6,6 @@ "name": "􄘬,􍁨緌sC\nD\u001e󱫂*\u0011𧲍\u0011󲾁a󽌳𗿸{.熿𭒪빝𡨶9/ಇ<;$𭣘𠪹Z\u0005'󺠞!F􎉼󼪟n\"\n8\u001dH󼯢9𐪜z:d\u0010F𧕰y_w\ri轭!>󳓗䏩𝓖\u0008\u001a\u001c\u000fF%<𞢹\u000fh\u001b\u0003\u000f󲶳\u001fO\u0000g_𤻨뢪󺥟\u0004􂔤􊃫z~%IA'R\u0008󶽴Hv^󾲱wrjb\t𨭛\u0003", "phone": "+518729615781", "role": "admin", - "team": "00000001-0000-0001-0000-000100000000" + "team": "00000001-0000-0001-0000-000100000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_12.json b/libs/wire-api/test/golden/testObject_Invitation_team_12.json index 866b59a789..ece82b4d17 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_12.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_12.json @@ -6,5 +6,6 @@ "name": "\u0010Z+wd^𐘊􆃨1\u0002YdXt>􇺼LSB7F9\\𠿬\u0005\n󱂟\"🀡|\u0007𦠺'\u001bTygU􎍔R칖􅧠O4󼷁E9\"󸃐\u0012Re\u0005D}􀧨𧢧􍭝\u0008V𫋾%98'\u001e9\u00064yP𔗍㡀ř\u0007w\t􌄦\u000b􇋳xv/Yl󵢬𦯯", "phone": "+68945103783764", "role": "admin", - "team": "00000000-0000-0000-0000-000000000002" + "team": "00000000-0000-0000-0000-000000000002", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_13.json b/libs/wire-api/test/golden/testObject_Invitation_team_13.json index 789b4a3297..f12163f667 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_13.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_13.json @@ -6,5 +6,6 @@ "name": "U", "phone": "+549940856897515", "role": "member", - "team": "00000002-0000-0001-0000-000000000001" + "team": "00000002-0000-0001-0000-000000000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_14.json b/libs/wire-api/test/golden/testObject_Invitation_team_14.json index 1f1ac31cc4..7b5764a687 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_14.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_14.json @@ -6,5 +6,6 @@ "name": null, "phone": "+89058877371", "role": "owner", - "team": "00000002-0000-0002-0000-000100000000" + "team": "00000002-0000-0002-0000-000100000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_15.json b/libs/wire-api/test/golden/testObject_Invitation_team_15.json index 8ec11e965f..7d5215c782 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_15.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_15.json @@ -6,5 +6,6 @@ "name": "𑜘\u001f&KIL\u0013􉋏![\n6􏙭HEj4E⽨UL\u001f>2􅝓_\nJ킢Pv\u000e\u000fR碱8\u0008mS뇆mE\u0007g\u0016\u0005%㣑\u000c!\u000b\u001f𝈊\u0005𭇱󿄈\u000e83!j𒁾\u001d􅣣,\u001e\u0018F􃞋􏈇U\u0019Jb\u0011j\u0019Y𖢐O󶃯", "phone": "+57741900390998", "role": "owner", - "team": "00000000-0000-0002-0000-000100000001" + "team": "00000000-0000-0002-0000-000100000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_16.json b/libs/wire-api/test/golden/testObject_Invitation_team_16.json index 1ade470dd6..853aab3be7 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_16.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_16.json @@ -6,5 +6,6 @@ "name": "\u001d\u0014Q;6/_f*7􋅎\u000f+􊳊ꋢ9", "phone": null, "role": "partner", - "team": "00000001-0000-0001-0000-000100000002" + "team": "00000001-0000-0001-0000-000100000002", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_17.json b/libs/wire-api/test/golden/testObject_Invitation_team_17.json index ffaa39fdfc..d7ae310a54 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_17.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_17.json @@ -6,5 +6,6 @@ "name": "Z\u001b9E\u0015鍌𔗕}(3m𗮙𗷤'􅺒.WY;\u001e8?v-􌮰\u0012󸀳", "phone": null, "role": "admin", - "team": "00000000-0000-0001-0000-000100000000" + "team": "00000000-0000-0001-0000-000100000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_19.json b/libs/wire-api/test/golden/testObject_Invitation_team_19.json index 4f087c6be4..aaa9b35ce0 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_19.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_19.json @@ -6,5 +6,6 @@ "name": "靸r𛋕\u0003Qi󴊗􌃗\u0019𩫻𒉓+􄮬Q?H=G-\u001e;􍝧\u000eq^K;a􀹚W\u0019 X𔖸􆂨>Mϔ朓jjbU-&󽼈v\u0000y𬙼\u0007|\u0016UfJCHjP\u000e􏘃浍DNA:~s", "phone": "+05787228893", "role": "member", - "team": "00000000-0000-0000-0000-000200000000" + "team": "00000000-0000-0000-0000-000200000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_2.json b/libs/wire-api/test/golden/testObject_Invitation_team_2.json index c5227405c9..393eaccd4f 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_2.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_2.json @@ -6,5 +6,6 @@ "name": "􄭇} 2pGEW+\rT𩹙p𪨳𦘢&𣫡v0\u0008", "phone": null, "role": "partner", - "team": "00000000-0000-0001-0000-000000000000" + "team": "00000000-0000-0001-0000-000000000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_20.json b/libs/wire-api/test/golden/testObject_Invitation_team_20.json index 8a036b8aff..653fafc89e 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_20.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_20.json @@ -6,5 +6,6 @@ "name": null, "phone": "+27259486019", "role": "partner", - "team": "00000001-0000-0000-0000-000000000000" + "team": "00000001-0000-0000-0000-000000000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_3.json b/libs/wire-api/test/golden/testObject_Invitation_team_3.json index 8111542049..6222659d12 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_3.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_3.json @@ -6,5 +6,6 @@ "name": null, "phone": null, "role": "partner", - "team": "00000002-0000-0001-0000-000100000001" + "team": "00000002-0000-0001-0000-000100000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_4.json b/libs/wire-api/test/golden/testObject_Invitation_team_4.json index 76282c4654..8e8dedc4a4 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_4.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_4.json @@ -6,5 +6,6 @@ "name": null, "phone": null, "role": "admin", - "team": "00000000-0000-0000-0000-000100000000" + "team": "00000000-0000-0000-0000-000100000000", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_5.json b/libs/wire-api/test/golden/testObject_Invitation_team_5.json index 44a5cd464d..ce4196efbb 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_5.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_5.json @@ -6,5 +6,6 @@ "name": "}G_𤃊`X󻋗𠆝󷲞L\"󿶗e6:E쨕󲟇f-$𠬒Z!s2p?#\tF 8𭿰𨕿󹵇\u0004􉢘*󸚄\u0016\u0010%Y𩀄>􏘍󾨶󺶘g\"􁥰\u001a\u001a𬇟ꦛ\u0004v𭽢,𩶐(\u001dQT𤪐;􃨚\u0005\u0017B􎇮H𩣓\\󾃾,Y", "phone": "+45207005641274", "role": "owner", - "team": "00000002-0000-0000-0000-000000000001" + "team": "00000002-0000-0000-0000-000000000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_6.json b/libs/wire-api/test/golden/testObject_Invitation_team_6.json index c847dcf4a2..37e3f45bdc 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_6.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_6.json @@ -6,5 +6,6 @@ "name": "O~\u0014U\u001e?V3_𮬰Slh􅱬Q1󶻳j|~M7􊲚􋽼𗆨\u0011K􇍼Afs𫬇lGV􏱇]`o\u0019f蓤InvfDDy\\DI𧾱􊥩\u0017B𦷬F*X\u0001\u001a얔\u0003\u0010<\u0003\u0016c\u0010,p\u000b*󵢘Vn\u000cI𑈹xS\u0002V\u001b$\u0019u󴮖xl>\u0007Z\u00144e\u0014aZ", "phone": "+75547625285", "role": "admin", - "team": "00000001-0000-0000-0000-000000000001" + "team": "00000001-0000-0000-0000-000000000001", + "url": null } diff --git a/libs/wire-api/test/golden/testObject_Invitation_team_7.json b/libs/wire-api/test/golden/testObject_Invitation_team_7.json index d4699fe74d..844522e716 100644 --- a/libs/wire-api/test/golden/testObject_Invitation_team_7.json +++ b/libs/wire-api/test/golden/testObject_Invitation_team_7.json @@ -6,5 +6,6 @@ "name": "\u0018.𛅷􈼞\u0010\u000c\u0010\u0018𤰤o;Yay:yY $\u0003<ͯ%@\u001fre>5L'R\u0013𫝳oy#]c4!𘖝U홊暧󾜸􃕢p_>f\u000e𪲈􇇪󳆗_Vm\u001f}\u0002Pz\r\u0005K\u000e+>󲆠\u0000𥝻?pu?r\u001b\u001a!?𩇕;ᦅS䥅\u0007􅠬\u0008󹹝 Date: Wed, 14 Sep 2022 10:54:36 +0200 Subject: [PATCH 32/66] Ensure the url for invitation QR code is a HttpsUrl --- libs/wire-api/src/Wire/API/Team/Invitation.hs | 4 +-- services/brig/src/Brig/Team/DB.hs | 36 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Invitation.hs b/libs/wire-api/src/Wire/API/Team/Invitation.hs index 203b229216..4f61f82157 100644 --- a/libs/wire-api/src/Wire/API/Team/Invitation.hs +++ b/libs/wire-api/src/Wire/API/Team/Invitation.hs @@ -32,8 +32,8 @@ where import Data.Aeson import Data.Id import Data.Json.Util +import Data.Misc (HttpsUrl) import qualified Data.Swagger.Build.Api as Doc -import Data.Text import Imports import Wire.API.Team.Role (Role, defaultRole, typeRole) import Wire.API.User.Identity (Email, Phone) @@ -106,7 +106,7 @@ data Invitation = Invitation inInviteeEmail :: Email, inInviteeName :: Maybe Name, inInviteePhone :: Maybe Phone, - inInviteeUrl :: Maybe Text + inInviteeUrl :: Maybe HttpsUrl } deriving stock (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform Invitation) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index cf8eaa9c2b..ff1a190655 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -53,14 +53,17 @@ import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C import Data.Id import Data.Json.Util (UTCTimeMillis, toUTCTimeMillis) +import Data.Misc import Data.Range import Data.Text.Ascii (encodeBase64Url, toText) +import Data.Text.Encoding import Data.Text.Lazy (toStrict) import Data.Time.Clock import Data.UUID.V4 import Imports import OpenSSL.Random (randBytes) import qualified System.Logger.Class as Log +import URI.ByteString import UnliftIO.Async (pooledMapConcurrentlyN_) import Wire.API.Team.Invitation import Wire.API.Team.Role @@ -106,7 +109,7 @@ insertInvitation :: insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout = do code <- liftIO mkInvitationCode showUrl <- getTeamExposeInvitationURLsToTeamAdmin t - url <- if showUrl then Just <$> mkInviteUrl t code else pure Nothing + url <- if showUrl then mkInviteUrl t code else pure Nothing let inv = Invitation t role iid now minviter email inviteeName phone url retry x5 . batch $ do setType BatchLogged @@ -288,7 +291,8 @@ countInvitations t = -- | brig used to not store the role, so for migration we allow this to be empty and fill in the -- default here. toInvitation :: - ( MonadReader Env m + ( MonadReader Env m, + Log.MonadLogger m ) => Bool -> ( TeamId, @@ -303,21 +307,41 @@ toInvitation :: ) -> m Invitation toInvitation showUrl (t, r, i, tm, minviter, e, inviteeName, p, code) = do - url <- if showUrl then Just <$> mkInviteUrl t code else pure Nothing + url <- if showUrl then mkInviteUrl t code else pure Nothing pure $ Invitation t (fromMaybe defaultRole r) i tm minviter e inviteeName p url mkInviteUrl :: - ( MonadReader Env m + ( MonadReader Env m, + Log.MonadLogger m ) => TeamId -> InvitationCode -> - m Text + m (Maybe HttpsUrl) mkInviteUrl team (InvitationCode c) = do template <- invitationEmailUrl . invitationEmail . snd <$> teamTemplates Nothing branding <- view App.templateBranding let url = toStrict $ renderTextWithBranding template replace branding - pure url + parseHttpsUrl url where replace "team" = idToText team replace "code" = toText c replace x = x + + parseHttpsUrl :: Log.MonadLogger m => Text -> m (Maybe HttpsUrl) + parseHttpsUrl url = case parseURI laxURIParserOptions (encodeUtf8 url) of + (Left e) -> do + logError url e + pure Nothing + (Right s) -> case mkHttpsUrl s of + Left e -> do + logError url e + pure Nothing + (Right s') -> pure $ Just s' + + logError :: (Log.MonadLogger m, Show e) => Text -> e -> m () + logError url e = + Log.err $ + Log.msg + (Log.val "Unable to create invitation url. Please check configuration.") + . Log.field "url" url + . Log.field "error" (show e) From 6b5d079d4480990a26c0a9b22c485555c396c054 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Wed, 14 Sep 2022 11:38:58 +0200 Subject: [PATCH 33/66] Less naive approach to HttpsUrl parsing / error handling --- services/brig/src/Brig/Team/DB.hs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index ff1a190655..a30b7674d0 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -49,6 +49,7 @@ import Brig.Template (renderTextWithBranding) import Cassandra as C import Control.Lens (view) import Control.Monad.Catch (MonadMask) +import Data.Bifunctor import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C import Data.Id @@ -328,15 +329,9 @@ mkInviteUrl team (InvitationCode c) = do replace x = x parseHttpsUrl :: Log.MonadLogger m => Text -> m (Maybe HttpsUrl) - parseHttpsUrl url = case parseURI laxURIParserOptions (encodeUtf8 url) of - (Left e) -> do - logError url e - pure Nothing - (Right s) -> case mkHttpsUrl s of - Left e -> do - logError url e - pure Nothing - (Right s') -> pure $ Just s' + parseHttpsUrl url = either (\e -> logError url e >> pure Nothing) (\s' -> pure $ Just s') $ do + uri <- first show $ parseURI laxURIParserOptions (encodeUtf8 url) + mkHttpsUrl uri logError :: (Log.MonadLogger m, Show e) => Text -> e -> m () logError url e = From f3ce88acbd0c1ae582a264a5bb007934019b007b Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Wed, 14 Sep 2022 14:28:40 +0200 Subject: [PATCH 34/66] Add more interesting golden test. --- .../Test/Wire/API/Golden/Generated/InvitationList_team.hs | 3 ++- .../wire-api/test/golden/testObject_InvitationList_team_2.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs index 8e7b45b6ad..25779c8133 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs @@ -19,6 +19,7 @@ module Test.Wire.API.Golden.Generated.InvitationList_team where +import Data.ByteString.Conversion (fromByteString') import Data.Id (Id (Id)) import Data.Json.Util (readUTCTimeMillis) import qualified Data.UUID as UUID (fromString) @@ -64,7 +65,7 @@ testObject_InvitationList_team_2 = } ), inInviteePhone = Just (Phone {fromPhone = "+851333011"}), - inInviteeUrl = Nothing + inInviteeUrl = fromByteString' "https://example.com/inv14" } ], ilHasMore = True diff --git a/libs/wire-api/test/golden/testObject_InvitationList_team_2.json b/libs/wire-api/test/golden/testObject_InvitationList_team_2.json index 6a9c45aac6..076b78a0d4 100644 --- a/libs/wire-api/test/golden/testObject_InvitationList_team_2.json +++ b/libs/wire-api/test/golden/testObject_InvitationList_team_2.json @@ -10,7 +10,7 @@ "phone": "+851333011", "role": "owner", "team": "00000000-0000-0000-0000-000000000001", - "url": null + "url": "https://example.com/inv14" } ] } From b53d7705500249203ab29570aa1f39305bceacd0 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Wed, 14 Sep 2022 14:43:20 +0200 Subject: [PATCH 35/66] make git-add-cassandra-schema --- cassandra-schema.cql | 1 + 1 file changed, 1 insertion(+) diff --git a/cassandra-schema.cql b/cassandra-schema.cql index 0474d89325..bde9fa797a 100644 --- a/cassandra-schema.cql +++ b/cassandra-schema.cql @@ -128,6 +128,7 @@ CREATE TABLE galley_test.team_features ( app_lock_status int, conference_calling int, digital_signatures int, + expose_invitation_urls_to_team_admin int, file_sharing int, file_sharing_lock_status int, guest_links_lock_status int, From 1985013e7cbeba9f950ea23c5cac0726d15ba7bd Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 13:09:41 +0200 Subject: [PATCH 36/66] The list of allowed TeamIds doesn't need to be a feature config An ordinary galley config file setting is sufficient. --- libs/galley-types/src/Galley/Types/Teams.hs | 9 +-- .../test/unit/Test/Galley/Types.hs | 1 - libs/wire-api/src/Wire/API/Team/Feature.hs | 26 +-------- services/galley/schema/src/Main.hs | 24 ++++---- .../galley/src/Galley/API/Teams/Features.hs | 57 ++++++++----------- .../test/integration/API/Teams/Feature.hs | 3 +- 6 files changed, 42 insertions(+), 78 deletions(-) diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index eb78ef9870..90152c9ebb 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -39,7 +39,6 @@ module Galley.Types.Teams flagTeamFeatureSndFactorPasswordChallengeStatus, flagTeamFeatureSearchVisibilityInbound, flagMLS, - flagTeamFeatureExposeInvitationURLsToTeamAdmin, flagTeamFeatureExposeInvitationURLsTeamAllowlist, Defaults (..), ImplicitLockStatus (..), @@ -153,8 +152,7 @@ data FeatureFlags = FeatureFlags _flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)), _flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)), _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)), - _flagTeamFeatureExposeInvitationURLsToTeamAdmin :: !(WithStatus ExposeInvitationURLsToTeamAdminConfig), - _flagTeamFeatureExposeInvitationURLsTeamAllowlist :: !(ImplicitLockStatus ExposeInvitationURLsTeamAllowlistConfig) + _flagTeamFeatureExposeInvitationURLsTeamAllowlist :: !ExposeInvitationURLsTeamAllowlistConfig } deriving (Eq, Show, Generic) @@ -204,8 +202,7 @@ instance FromJSON FeatureFlags where <*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge")) <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" - <*> (fromMaybe (defFeatureStatus @ExposeInvitationURLsToTeamAdminConfig) <$> (obj .:? "exposeInvitationURLsToTeamAdmin")) - <*> (fromMaybe (ImplicitLockStatus (defFeatureStatus @ExposeInvitationURLsTeamAllowlistConfig)) <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) + <*> (fromMaybe (ExposeInvitationURLsTeamAllowlistConfig []) <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) withImplicitLockStatusOrDefault obj fieldName = fromMaybe (Defaults (ImplicitLockStatus (defFeatureStatus @cfg))) <$> obj .:? fieldName @@ -226,7 +223,6 @@ instance ToJSON FeatureFlags where sndFactorPasswordChallenge searchVisibilityInbound mls - exposeInvitationURLsToTeamAdmin exposeInvitationURLsTeamAllowlist ) = object @@ -243,7 +239,6 @@ instance ToJSON FeatureFlags where "sndFactorPasswordChallenge" .= sndFactorPasswordChallenge, "searchVisibilityInbound" .= searchVisibilityInbound, "mls" .= mls, - "exposeInvitationURLsToTeamAdmin" .= exposeInvitationURLsToTeamAdmin, "exposeInvitationURLsTeamAllowlist" .= exposeInvitationURLsTeamAllowlist ] diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 683d1a2bcc..367f0413f1 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -99,7 +99,6 @@ instance Arbitrary FeatureFlags where <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary <*> arbitrary - <*> fmap unlocked arbitrary where unlocked :: ImplicitLockStatus a -> ImplicitLockStatus a unlocked = ImplicitLockStatus . Public.setLockStatus Public.LockStatusUnlocked . _unImplicitLockStatus diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 88f32a1128..27e42ea5e2 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -582,7 +582,6 @@ allFeatureModels = withStatusNoLockModel @SearchVisibilityInboundConfig, withStatusNoLockModel @MLSConfig, withStatusNoLockModel @ExposeInvitationURLsToTeamAdminConfig, - withStatusNoLockModel @ExposeInvitationURLsTeamAllowlistConfig, withStatusModel @LegalholdConfig, withStatusModel @SSOConfig, withStatusModel @SearchVisibilityAvailableConfig, @@ -597,8 +596,7 @@ allFeatureModels = withStatusModel @SndFactorPasswordChallengeConfig, withStatusModel @SearchVisibilityInboundConfig, withStatusModel @MLSConfig, - withStatusModel @ExposeInvitationURLsToTeamAdminConfig, - withStatusModel @ExposeInvitationURLsTeamAllowlistConfig + withStatusModel @ExposeInvitationURLsToTeamAdminConfig ] <> catMaybes [ configModel @LegalholdConfig, @@ -615,8 +613,7 @@ allFeatureModels = configModel @SndFactorPasswordChallengeConfig, configModel @SearchVisibilityInboundConfig, configModel @MLSConfig, - configModel @ExposeInvitationURLsToTeamAdminConfig, - configModel @ExposeInvitationURLsTeamAllowlistConfig + configModel @ExposeInvitationURLsToTeamAdminConfig ] -------------------------------------------------------------------------------- @@ -982,20 +979,6 @@ instance ToSchema ExposeInvitationURLsTeamAllowlistConfig where ExposeInvitationURLsTeamAllowlistConfig <$> exposeInvitationURLsTeamAllowlist .= field "teams" (array schema) -instance IsFeatureConfig ExposeInvitationURLsTeamAllowlistConfig where - type FeatureSymbol ExposeInvitationURLsTeamAllowlistConfig = "exposeInvitationURLsTeamAllowlist" - - defFeatureStatus = - withStatus - FeatureStatusDisabled - LockStatusUnlocked - (ExposeInvitationURLsTeamAllowlistConfig []) - FeatureTTLUnlimited - configModel = Just $ - Doc.defineModel "ExposeInvitationURLsTeamAllowlistConfig" $ do - Doc.property "teams" (Doc.array Doc.string') $ Doc.description "teams" - objectSchema = field "config" schema - ---------------------------------------------------------------------- -- FeatureStatus @@ -1065,8 +1048,7 @@ data AllFeatureConfigs = AllFeatureConfigs afcGuestLink :: WithStatus GuestLinksConfig, afcSndFactorPasswordChallenge :: WithStatus SndFactorPasswordChallengeConfig, afcMLS :: WithStatus MLSConfig, - afcExposeInvitationURLsToTeamAdmin :: WithStatus ExposeInvitationURLsToTeamAdminConfig, - afcExposeInvitationURLsTeamAllowlist :: WithStatus ExposeInvitationURLsTeamAllowlistConfig + afcExposeInvitationURLsToTeamAdmin :: WithStatus ExposeInvitationURLsToTeamAdminConfig } deriving stock (Eq, Show) deriving (FromJSON, ToJSON, S.ToSchema) via (Schema AllFeatureConfigs) @@ -1090,7 +1072,6 @@ instance ToSchema AllFeatureConfigs where <*> afcSndFactorPasswordChallenge .= featureField <*> afcMLS .= featureField <*> afcExposeInvitationURLsToTeamAdmin .= featureField - <*> afcExposeInvitationURLsTeamAllowlist .= featureField where featureField :: forall cfg. @@ -1116,6 +1097,5 @@ instance Arbitrary AllFeatureConfigs where <*> arbitrary <*> arbitrary <*> arbitrary - <*> arbitrary makeLenses ''ImplicitLockStatus diff --git a/services/galley/schema/src/Main.hs b/services/galley/schema/src/Main.hs index e493ad914f..5887891143 100644 --- a/services/galley/schema/src/Main.hs +++ b/services/galley/schema/src/Main.hs @@ -138,18 +138,18 @@ main = do V70_MLSCipherSuite.migration, V71_MemberClientKeypackage.migration, V72_DropManagedConversations.migration, - V73_MemberClientTable.migration - V74_ExposeInvitationsToTeamAdmin.migration - -- When adding migrations here, don't forget to update - -- 'schemaVersion' in Galley.Cassandra - -- (see also docs/developer/cassandra-interaction.md) - -- - -- 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 - -- for an explanation - -- FUTUREWORK: once #1751 has made its way to master/production, - -- the 'otr_muted' field in the member table can be dropped. + V73_MemberClientTable.migration, + V74_ExposeInvitationsToTeamAdmin.migration + -- When adding migrations here, don't forget to update + -- 'schemaVersion' in Galley.Cassandra + -- (see also docs/developer/cassandra-interaction.md) + -- + -- 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 + -- for an explanation + -- FUTUREWORK: once #1751 has made its way to master/production, + -- the 'otr_muted' field in the member table can be dropped. ] `finally` Log.close l where diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 6f6574a921..120dd98317 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -443,7 +443,6 @@ getAllFeatureConfigsForServer = <*> getConfigForServer @db @SndFactorPasswordChallengeConfig <*> getConfigForServer @db @MLSConfig <*> getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig - <*> getConfigForServer @db @ExposeInvitationURLsTeamAllowlistConfig getAllFeatureConfigsUser :: forall db r. @@ -478,7 +477,6 @@ getAllFeatureConfigsUser uid = <*> getConfigForUser @db @SndFactorPasswordChallengeConfig uid <*> getConfigForUser @db @MLSConfig uid <*> getConfigForUser @db @ExposeInvitationURLsToTeamAdminConfig uid - <*> getConfigForUser @db @ExposeInvitationURLsTeamAllowlistConfig uid getAllFeatureConfigsTeam :: forall db r. @@ -512,7 +510,6 @@ getAllFeatureConfigsTeam tid = <*> getConfigForTeam @db @SndFactorPasswordChallengeConfig tid <*> getConfigForTeam @db @MLSConfig tid <*> getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid - <*> getConfigForTeam @db @ExposeInvitationURLsTeamAllowlistConfig tid -- | Note: this is an internal function which doesn't cover all features, e.g. LegalholdConfig genericGetConfigForTeam :: @@ -859,24 +856,33 @@ instance SetFeatureConfig db MLSConfig where persistAndPushEvent @db tid wsnl instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where + -- TODO: Is this the right outcome? Speaking of "Generally, for the whole + -- server" the feature is always disabled. getConfigForServer = - input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsToTeamAdmin) + pure $ + withStatus + FeatureStatusDisabled + LockStatusLocked + ExposeInvitationURLsToTeamAdminConfig + FeatureTTLUnlimited getConfigForTeam tid = do - computeConfigForTeam - <$> (getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid) - <*> (TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus) - <*> getConfigForServer @db @ExposeInvitationURLsToTeamAdminConfig + allowList <- input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist) + mbOldStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus + let teamAllowed = tid `elem` exposeInvitationURLsTeamAllowlist allowList + pure $ case mbOldStatus of + Nothing -> computeConfigForTeam teamAllowed FeatureStatusDisabled + -- TODO: This is not what's expected. The galley config should be the + -- leader. I just don't know how to test with it. + Just s -> makeConfig LockStatusUnlocked s -- computeConfigForTeam teamAllowed s where - computeConfigForTeam teamAllowed teamDbStatus defConfig = - case wsLockStatus defConfig of - LockStatusLocked -> - makeConfig LockStatusLocked (if teamAllowed then wsStatus defConfig else FeatureStatusDisabled) - LockStatusUnlocked -> - makeConfig - (computeTeamLockStatus teamAllowed teamDbStatus) - (computeTeamStatus teamAllowed teamDbStatus $ wsStatus defConfig) + computeConfigForTeam :: Bool -> FeatureStatus -> WithStatus ExposeInvitationURLsToTeamAdminConfig + computeConfigForTeam teamAllowed teamDbStatus = + if teamAllowed + then makeConfig LockStatusUnlocked teamDbStatus + else makeConfig LockStatusLocked FeatureStatusDisabled + makeConfig :: LockStatus -> FeatureStatus -> WithStatus ExposeInvitationURLsToTeamAdminConfig makeConfig lockStatus status = withStatus status @@ -884,22 +890,11 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where ExposeInvitationURLsToTeamAdminConfig FeatureTTLUnlimited - computeTeamLockStatus True _ = LockStatusUnlocked - computeTeamLockStatus False (Just FeatureStatusEnabled) = LockStatusUnlocked - computeTeamLockStatus _ _ = LockStatusLocked - - computeTeamStatus True Nothing defaultStatus = defaultStatus - computeTeamStatus True (Just status) _ = status - computeTeamStatus False (Just FeatureStatusEnabled) _ = FeatureStatusEnabled - computeTeamStatus False _ _ = FeatureStatusDisabled - instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = do - -- Only allow enabling this feature for teams which are in the admin-configured allowlist. If - -- a team has the feature enabled, but is not currently in the allowlist, permit them to - -- disable the feature. - teamAllowed <- getConfigForServer @db <&> wsConfig <&> exposeInvitationURLsTeamAllowlist <&> elem tid + allowList <- input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist) + let teamAllowed = tid `elem` exposeInvitationURLsTeamAllowlist allowList oldState <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsStatus let newState = wssStatus wsnl case (teamAllowed, oldState, newState) of @@ -907,10 +902,6 @@ instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where (False, FeatureStatusEnabled, FeatureStatusDisabled) -> persistAndPushEvent @db tid wsnl (_, _, _) -> throwS @OperationDenied -instance GetFeatureConfig db ExposeInvitationURLsTeamAllowlistConfig where - getConfigForServer = - input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist . unImplicitLockStatus) - -- -- | If second factor auth is enabled, make sure that end-points that don't support it, but should, are blocked completely. (This is a workaround until we have 2FA for those end-points as well.) -- -- -- This function exists to resolve a cyclic dependency. diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index 59b9cc560f..c27cd2e929 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -992,8 +992,7 @@ testAllFeatures = do Public.afcSndFactorPasswordChallenge = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked Public.SndFactorPasswordChallengeConfig Public.FeatureTTLUnlimited, Public.afcMLS = Public.withStatus FeatureStatusDisabled Public.LockStatusUnlocked (Public.MLSConfig [] ProtocolProteusTag [MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519] MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519) Public.FeatureTTLUnlimited, Public.afcSearchVisibilityInboundConfig = Public.withStatus FeatureStatusDisabled Public.LockStatusUnlocked Public.SearchVisibilityInboundConfig Public.FeatureTTLUnlimited, - Public.afcExposeInvitationURLsToTeamAdmin = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited, - Public.afcExposeInvitationURLsTeamAllowlist = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked (Public.ExposeInvitationURLsTeamAllowlistConfig []) Public.FeatureTTLUnlimited + Public.afcExposeInvitationURLsToTeamAdmin = Public.withStatus FeatureStatusDisabled Public.LockStatusLocked Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited } testFeatureConfigConsistency :: TestM () From 37e46ce9727010f84004f401a7a0abd11200214b Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 13:37:42 +0200 Subject: [PATCH 37/66] Invitation URLs may not always be HTTPS On local test environments they are only HTTP. --- libs/wire-api/src/Wire/API/Team/Invitation.hs | 4 ++-- .../Wire/API/Golden/Generated/InvitationList_team.hs | 5 +++-- libs/wire-api/wire-api.cabal | 1 + services/brig/src/Brig/Team/DB.hs | 11 +++++------ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libs/wire-api/src/Wire/API/Team/Invitation.hs b/libs/wire-api/src/Wire/API/Team/Invitation.hs index 4f61f82157..efcc60de35 100644 --- a/libs/wire-api/src/Wire/API/Team/Invitation.hs +++ b/libs/wire-api/src/Wire/API/Team/Invitation.hs @@ -32,9 +32,9 @@ where import Data.Aeson import Data.Id import Data.Json.Util -import Data.Misc (HttpsUrl) import qualified Data.Swagger.Build.Api as Doc import Imports +import URI.ByteString import Wire.API.Team.Role (Role, defaultRole, typeRole) import Wire.API.User.Identity (Email, Phone) import Wire.API.User.Profile (Locale, Name) @@ -106,7 +106,7 @@ data Invitation = Invitation inInviteeEmail :: Email, inInviteeName :: Maybe Name, inInviteePhone :: Maybe Phone, - inInviteeUrl :: Maybe HttpsUrl + inInviteeUrl :: Maybe (URIRef Absolute) } deriving stock (Eq, Show, Generic) deriving (Arbitrary) via (GenericUniform Invitation) diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs index 25779c8133..ee7623fa21 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs @@ -19,11 +19,12 @@ module Test.Wire.API.Golden.Generated.InvitationList_team where -import Data.ByteString.Conversion (fromByteString') +import Data.Either.Combinators import Data.Id (Id (Id)) import Data.Json.Util (readUTCTimeMillis) import qualified Data.UUID as UUID (fromString) import Imports (Bool (False, True), Maybe (Just, Nothing), fromJust) +import URI.ByteString (parseURI, strictURIParserOptions) import Wire.API.Team.Invitation ( Invitation ( Invitation, @@ -65,7 +66,7 @@ testObject_InvitationList_team_2 = } ), inInviteePhone = Just (Phone {fromPhone = "+851333011"}), - inInviteeUrl = fromByteString' "https://example.com/inv14" + inInviteeUrl = Just (fromRight' (parseURI strictURIParserOptions "https://example.com/inv14")) } ], ilHasMore = True diff --git a/libs/wire-api/wire-api.cabal b/libs/wire-api/wire-api.cabal index 622041a252..b7dab8ae13 100644 --- a/libs/wire-api/wire-api.cabal +++ b/libs/wire-api/wire-api.cabal @@ -566,6 +566,7 @@ test-suite wire-api-golden-tests , containers >=0.5 , currency-codes , directory + , either , filepath , hscim , imports diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index a30b7674d0..fa561b91fe 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -54,7 +54,6 @@ import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C import Data.Id import Data.Json.Util (UTCTimeMillis, toUTCTimeMillis) -import Data.Misc import Data.Range import Data.Text.Ascii (encodeBase64Url, toText) import Data.Text.Encoding @@ -317,7 +316,7 @@ mkInviteUrl :: ) => TeamId -> InvitationCode -> - m (Maybe HttpsUrl) + m (Maybe (URIRef Absolute)) mkInviteUrl team (InvitationCode c) = do template <- invitationEmailUrl . invitationEmail . snd <$> teamTemplates Nothing branding <- view App.templateBranding @@ -328,10 +327,10 @@ mkInviteUrl team (InvitationCode c) = do replace "code" = toText c replace x = x - parseHttpsUrl :: Log.MonadLogger m => Text -> m (Maybe HttpsUrl) - parseHttpsUrl url = either (\e -> logError url e >> pure Nothing) (\s' -> pure $ Just s') $ do - uri <- first show $ parseURI laxURIParserOptions (encodeUtf8 url) - mkHttpsUrl uri + parseHttpsUrl :: Log.MonadLogger m => Text -> m (Maybe (URIRef Absolute)) + parseHttpsUrl url = + either (\e -> logError url e >> pure Nothing) (\s' -> pure $ Just s') $ + first show $ parseURI laxURIParserOptions (encodeUtf8 url) logError :: (Log.MonadLogger m, Show e) => Text -> e -> m () logError url e = From ccf99e4f41bbda23a26ee704ca8ded64aaa20d5e Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 15:01:40 +0200 Subject: [PATCH 38/66] Test the basic feature behavior --- .../test/integration/API/Teams/Feature.hs | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index c27cd2e929..cdaf28c5e0 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -18,15 +18,17 @@ module API.Teams.Feature (tests) where +import API.SQS (assertQueue, tActivate) import API.Util (HasGalley, getFeatureStatusMulti, withSettingsOverrides) import qualified API.Util as Util -import API.Util.TeamFeature (patchFeatureStatusInternal) +import API.Util.TeamFeature (patchFeatureStatusInternal, putTeamFeatureFlagWithGalley) import qualified API.Util.TeamFeature as Util import Bilge import Bilge.Assert import Brig.Types.Test.Arbitrary (Arbitrary (arbitrary)) import Cassandra as Cql -import Control.Lens (over, to, view) +import Control.Lens (over, to, view, (.~)) +import Control.Lens.Operators () import Control.Monad.Catch (MonadCatch) import Data.Aeson (FromJSON, ToJSON) import qualified Data.Aeson as Aeson @@ -56,7 +58,7 @@ import qualified Wire.API.Event.FeatureConfig as FeatureConfig import Wire.API.Internal.Notification (Notification) import Wire.API.MLS.CipherSuite import Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti as Multi -import Wire.API.Team.Feature (FeatureStatus (..), FeatureTTL, FeatureTTL' (..), LockStatus (LockStatusUnlocked), MLSConfig (MLSConfig)) +import Wire.API.Team.Feature (ExposeInvitationURLsTeamAllowlistConfig (..), ExposeInvitationURLsToTeamAdminConfig (..), FeatureStatus (..), FeatureTTL, FeatureTTL' (..), LockStatus (LockStatusUnlocked), MLSConfig (MLSConfig)) import qualified Wire.API.Team.Feature as Public tests :: IO TestSetup -> TestTree @@ -135,9 +137,51 @@ tests s = testPatch AssertLockStatusChange Public.FeatureStatusDisabled Public.SndFactorPasswordChallengeConfig, test s (unpack $ Public.featureNameBS @Public.SelfDeletingMessagesConfig) $ testPatch AssertLockStatusChange Public.FeatureStatusEnabled (Public.SelfDeletingMessagesConfig 0) + ], + testGroup + "ExposeInvitationURLsToTeamAdmin" + [ test s "can be set when TeamId is in allow list" testExposeInvitationURLsToTeamAdminTeamIdInAllowList, + test s "can not be set when allow list is empty" testExposeInvitationURLsToTeamAdminEmptyAllowList ] ] +testExposeInvitationURLsToTeamAdminTeamIdInAllowList :: TestM () +testExposeInvitationURLsToTeamAdminTeamIdInAllowList = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 200 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked + +assertExposeInvitationURLsToTeamAdminConfigStatus :: UserId -> TeamId -> FeatureStatus -> LockStatus -> TestM () +assertExposeInvitationURLsToTeamAdminConfigStatus owner tid fStatus lStatus = do + g <- view tsGalley + Util.getTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid !!! do + const 200 === statusCode + const (Right (Public.withStatus fStatus lStatus Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited)) === responseJsonEither + +testExposeInvitationURLsToTeamAdminEmptyAllowList :: TestM () +testExposeInvitationURLsToTeamAdminEmptyAllowList = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 409 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + -- | Provides a `Gen` with test objects that are realistic and can easily be asserted validMLSConfigGen :: Gen (Public.WithStatusPatch MLSConfig) validMLSConfigGen = From 55f93cf367a63606b0b9c054284fd816d887155c Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 15:20:19 +0200 Subject: [PATCH 39/66] Server config takes precedence over team feature config --- .../galley/src/Galley/API/Teams/Features.hs | 18 ++++--------- .../test/integration/API/Teams/Feature.hs | 27 ++++++++++++++++++- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 120dd98317..cdbac21dbd 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -856,8 +856,6 @@ instance SetFeatureConfig db MLSConfig where persistAndPushEvent @db tid wsnl instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where - -- TODO: Is this the right outcome? Speaking of "Generally, for the whole - -- server" the feature is always disabled. getConfigForServer = pure $ withStatus @@ -872,9 +870,7 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where let teamAllowed = tid `elem` exposeInvitationURLsTeamAllowlist allowList pure $ case mbOldStatus of Nothing -> computeConfigForTeam teamAllowed FeatureStatusDisabled - -- TODO: This is not what's expected. The galley config should be the - -- leader. I just don't know how to test with it. - Just s -> makeConfig LockStatusUnlocked s -- computeConfigForTeam teamAllowed s + Just s -> computeConfigForTeam teamAllowed s where computeConfigForTeam :: Bool -> FeatureStatus -> WithStatus ExposeInvitationURLsToTeamAdminConfig computeConfigForTeam teamAllowed teamDbStatus = @@ -893,14 +889,10 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where instance SetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where type SetConfigForTeamConstraints db ExposeInvitationURLsToTeamAdminConfig (r :: EffectRow) = (Member (ErrorS OperationDenied) r) setConfigForTeam tid wsnl = do - allowList <- input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist) - let teamAllowed = tid `elem` exposeInvitationURLsTeamAllowlist allowList - oldState <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsStatus - let newState = wssStatus wsnl - case (teamAllowed, oldState, newState) of - (True, _, _) -> persistAndPushEvent @db tid wsnl - (False, FeatureStatusEnabled, FeatureStatusDisabled) -> persistAndPushEvent @db tid wsnl - (_, _, _) -> throwS @OperationDenied + lockStatus <- getConfigForTeam @db @ExposeInvitationURLsToTeamAdminConfig tid <&> wsLockStatus + case lockStatus of + LockStatusLocked -> throwS @OperationDenied + LockStatusUnlocked -> persistAndPushEvent @db tid wsnl -- -- | If second factor auth is enabled, make sure that end-points that don't support it, but should, are blocked completely. (This is a workaround until we have 2FA for those end-points as well.) -- -- diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index cdaf28c5e0..429c55394e 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -141,7 +141,8 @@ tests s = testGroup "ExposeInvitationURLsToTeamAdmin" [ test s "can be set when TeamId is in allow list" testExposeInvitationURLsToTeamAdminTeamIdInAllowList, - test s "can not be set when allow list is empty" testExposeInvitationURLsToTeamAdminEmptyAllowList + test s "can not be set when allow list is empty" testExposeInvitationURLsToTeamAdminEmptyAllowList, + test s "server config takes precendece over team feature config" testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence ] ] @@ -182,6 +183,30 @@ testExposeInvitationURLsToTeamAdminEmptyAllowList = do const 409 === statusCode assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked +testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence :: TestM () +testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 200 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 409 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + -- | Provides a `Gen` with test objects that are realistic and can easily be asserted validMLSConfigGen :: Gen (Public.WithStatusPatch MLSConfig) validMLSConfigGen = From b1735395f0c4e4191dcbf6fb22a80f6fa84650ba Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 15:23:38 +0200 Subject: [PATCH 40/66] Move test cases --- .../test/integration/API/Teams/Feature.hs | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index 429c55394e..b81ba67296 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -146,67 +146,6 @@ tests s = ] ] -testExposeInvitationURLsToTeamAdminTeamIdInAllowList :: TestM () -testExposeInvitationURLsToTeamAdminTeamIdInAllowList = do - owner <- Util.randomUser - tid <- Util.createBindingTeamInternal "foo" owner - assertQueue "create team" tActivate - void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do - g <- view tsGalley - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked - let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited - void $ - putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do - const 200 === statusCode - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked - -assertExposeInvitationURLsToTeamAdminConfigStatus :: UserId -> TeamId -> FeatureStatus -> LockStatus -> TestM () -assertExposeInvitationURLsToTeamAdminConfigStatus owner tid fStatus lStatus = do - g <- view tsGalley - Util.getTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid !!! do - const 200 === statusCode - const (Right (Public.withStatus fStatus lStatus Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited)) === responseJsonEither - -testExposeInvitationURLsToTeamAdminEmptyAllowList :: TestM () -testExposeInvitationURLsToTeamAdminEmptyAllowList = do - owner <- Util.randomUser - tid <- Util.createBindingTeamInternal "foo" owner - assertQueue "create team" tActivate - void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do - g <- view tsGalley - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked - let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited - void $ - putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do - const 409 === statusCode - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked - -testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence :: TestM () -testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do - owner <- Util.randomUser - tid <- Util.createBindingTeamInternal "foo" owner - assertQueue "create team" tActivate - void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do - g <- view tsGalley - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked - let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited - void $ - putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do - const 200 === statusCode - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked - void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do - g <- view tsGalley - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked - let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited - void $ - putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do - const 409 === statusCode - assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked - -- | Provides a `Gen` with test objects that are realistic and can easily be asserted validMLSConfigGen :: Gen (Public.WithStatusPatch MLSConfig) validMLSConfigGen = @@ -1201,6 +1140,67 @@ testMLS = do wsAssertFeatureConfigUpdate @MLSConfig config3 LockStatusUnlocked getViaEndpoints config3 +testExposeInvitationURLsToTeamAdminTeamIdInAllowList :: TestM () +testExposeInvitationURLsToTeamAdminTeamIdInAllowList = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 200 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked + +testExposeInvitationURLsToTeamAdminEmptyAllowList :: TestM () +testExposeInvitationURLsToTeamAdminEmptyAllowList = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 409 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + +testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence :: TestM () +testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do + owner <- Util.randomUser + tid <- Util.createBindingTeamInternal "foo" owner + assertQueue "create team" tActivate + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 200 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked + void $ + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + g <- view tsGalley + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited + void $ + putTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid enabled !!! do + const 409 === statusCode + assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked + +assertExposeInvitationURLsToTeamAdminConfigStatus :: UserId -> TeamId -> FeatureStatus -> LockStatus -> TestM () +assertExposeInvitationURLsToTeamAdminConfigStatus owner tid fStatus lStatus = do + g <- view tsGalley + Util.getTeamFeatureFlagWithGalley @ExposeInvitationURLsToTeamAdminConfig g owner tid !!! do + const 200 === statusCode + const (Right (Public.withStatus fStatus lStatus Public.ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited)) === responseJsonEither + assertFlagForbidden :: HasCallStack => TestM ResponseLBS -> TestM () assertFlagForbidden res = do res !!! do From 3e5608779069aacb538dde90e368b4fd3e4bdfc1 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 16:31:52 +0200 Subject: [PATCH 41/66] Delete ExposeInvitationURLsTeamAllowlistConfig team feature left overs --- services/galley/src/Galley/API/Teams/Features.hs | 3 +-- services/galley/src/Galley/Cassandra/TeamFeatures.hs | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index cdbac21dbd..3bf6b7315b 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -180,8 +180,7 @@ type FeaturePersistentAllFeatures db = FeaturePersistentConstraint db SndFactorPasswordChallengeConfig, FeaturePersistentConstraint db MLSConfig, FeaturePersistentConstraint db SearchVisibilityInboundConfig, - FeaturePersistentConstraint db ExposeInvitationURLsToTeamAdminConfig, - FeaturePersistentConstraint db ExposeInvitationURLsTeamAllowlistConfig + FeaturePersistentConstraint db ExposeInvitationURLsToTeamAdminConfig ) getFeatureStatus :: diff --git a/services/galley/src/Galley/Cassandra/TeamFeatures.hs b/services/galley/src/Galley/Cassandra/TeamFeatures.hs index f03f3f4b56..169280c51d 100644 --- a/services/galley/src/Galley/Cassandra/TeamFeatures.hs +++ b/services/galley/src/Galley/Cassandra/TeamFeatures.hs @@ -317,7 +317,3 @@ instance FeatureStatusCassandra MLSConfig where instance FeatureStatusCassandra ExposeInvitationURLsToTeamAdminConfig where getFeatureConfig _ = getTrivialConfigC "expose_invitation_urls_to_team_admin" setFeatureConfig _ tid statusNoLock = setFeatureStatusC "expose_invitation_urls_to_team_admin" tid (wssStatus statusNoLock) - -instance FeatureStatusCassandra ExposeInvitationURLsTeamAllowlistConfig where - getFeatureConfig _ _tid = pure Nothing - setFeatureConfig _ _tid _statusNoLock = pure () From 5d0ff3ff1acd079f2b161d8bc10ae6e547bf8c88 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 16:54:21 +0200 Subject: [PATCH 42/66] Add changelog --- changelog.d/2-features/registration-url-in-invitation | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2-features/registration-url-in-invitation diff --git a/changelog.d/2-features/registration-url-in-invitation b/changelog.d/2-features/registration-url-in-invitation new file mode 100644 index 0000000000..85f650499b --- /dev/null +++ b/changelog.d/2-features/registration-url-in-invitation @@ -0,0 +1 @@ +Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `exposeInvitationURLsTeamAllowlist` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. From d312ed34f26ed9354fc76bccf3446b5898d8aaae Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 17:03:33 +0200 Subject: [PATCH 43/66] Add allow list config to Helm chart --- charts/galley/templates/configmap.yaml | 3 +++ charts/galley/values.yaml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index 9a877ab26f..9619db41a3 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -113,5 +113,8 @@ data: mls: {{- toYaml .settings.featureFlags.mls | nindent 10 }} {{- end }} + {{- if .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} + exposeInvitationURLsTeamAllowlist: {{ .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} + {{- end }} {{- end }} {{- end }} diff --git a/charts/galley/values.yaml b/charts/galley/values.yaml index 2c6fa9a6c4..2af07d3eb9 100644 --- a/charts/galley/values.yaml +++ b/charts/galley/values.yaml @@ -79,7 +79,8 @@ config: validateSAMLemails: defaults: status: enabled - + exposeInvitationURLsTeamAllowlist: [] + aws: region: "eu-west-1" proxy: {} From 0db1bd79c1c39e84007d1f16855669a2004df0ce Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 18:18:05 +0200 Subject: [PATCH 44/66] Add section to config-options.md --- .../2-features/registration-url-in-invitation | 2 +- .../src/developer/reference/config-options.md | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/changelog.d/2-features/registration-url-in-invitation b/changelog.d/2-features/registration-url-in-invitation index 85f650499b..4b19c93c5c 100644 --- a/changelog.d/2-features/registration-url-in-invitation +++ b/changelog.d/2-features/registration-url-in-invitation @@ -1 +1 @@ -Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `exposeInvitationURLsTeamAllowlist` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. +Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `settings.featureFlags.exposeInvitationURLsTeamAllowlist.teams` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. diff --git a/docs/src/developer/reference/config-options.md b/docs/src/developer/reference/config-options.md index 8a74338aa7..f574843dba 100644 --- a/docs/src/developer/reference/config-options.md +++ b/docs/src/developer/reference/config-options.md @@ -99,6 +99,55 @@ IMPORTANT: If you switch this back to `disabled-permanently` from that have created them while it was allowed. This may change in the future. +### Expose invitation URLs to team admin + +For further processing (e.g. sending custom emails or rendering the URLs as QR +codes), team invitation URLs can be made part of the result of +`GET /teams/{tid}/invitations`. + +```json +{ + "has_more": false, + "invitations": [ + { + "created_at": "2022-09-15T15:47:28.577Z", + "created_by": "375f56fe-7f12-4c0c-aed8-d48c0326d1fb", + "email": "foo@example.com", + "id": "4decf7f8-bdd4-43b3-aaf2-e912e2c0c46f", + "name": null, + "phone": null, + "role": "member", + "team": "51612209-3b61-49b0-8c55-d21ae65efc1a", + "url": "http://127.0.0.1:8080/register?team=51612209-3b61-49b0-8c55-d21ae65efc1a&team_code=RpxGkK_yjw8ZBegJuFQO0hha-2Tneajp" + } + ] +} +``` + +This can be a privacy issue as it allows the team admin to impersonate as +another team member. The feature is disabled by default. + +To activate this feature two steps are needed. First, the team id (tid) has to +be added to the list of teams for which this feature *can* be enabled +(`exposeInvitationURLsTeamAllowlist`). This is done in `galley`'s `values.yaml`: + +```yaml +settings: + featureFlags: + exposeInvitationURLsTeamAllowlist: + teams: ["51612209-3b61-49b0-8c55-d21ae65efc1a"] +``` + +Then, the feature can be set for the team by enabling the +`exposeInvitationURLsToTeamAdmin` flag. This is done by making a `PUT` request +to `/teams/{tid}/features/exposeInvitationURLsToTeamAdmin` with the body: + +```json +{ + "status": "enabled" +} +``` + ### Team searchVisibility The team flag `searchVisibility` affects the outbound search of user From 6a5248dff870454696fa6887d03c18409bef7bd0 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 18:34:09 +0200 Subject: [PATCH 45/66] Fix Helm chart --- charts/galley/templates/configmap.yaml | 3 ++- charts/galley/values.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index 9619db41a3..72e2062f87 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -114,7 +114,8 @@ data: {{- toYaml .settings.featureFlags.mls | nindent 10 }} {{- end }} {{- if .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} - exposeInvitationURLsTeamAllowlist: {{ .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} + exposeInvitationURLsTeamAllowlist: + {{- toYaml .settings.featureFlags.exposeInvitationURLsTeamAllowlist | nindent 10 }} {{- end }} {{- end }} {{- end }} diff --git a/charts/galley/values.yaml b/charts/galley/values.yaml index 2af07d3eb9..862f59439e 100644 --- a/charts/galley/values.yaml +++ b/charts/galley/values.yaml @@ -79,7 +79,8 @@ config: validateSAMLemails: defaults: status: enabled - exposeInvitationURLsTeamAllowlist: [] + exposeInvitationURLsTeamAllowlist: + teams: [] aws: region: "eu-west-1" From 82b83a3b85a2bbc292749a313ea74ec53969d7ca Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Thu, 15 Sep 2022 19:13:33 +0200 Subject: [PATCH 46/66] Simplify Galley YAML config TeamIds are now configured by a list and not a complex object. --- .../2-features/registration-url-in-invitation | 2 +- charts/galley/templates/configmap.yaml | 3 +-- charts/galley/values.yaml | 3 +-- docs/src/developer/reference/config-options.md | 3 +-- libs/galley-types/src/Galley/Types/Teams.hs | 2 +- libs/wire-api/src/Wire/API/Team/Feature.hs | 16 ++-------------- services/galley/src/Galley/API/Teams/Features.hs | 2 +- .../galley/test/integration/API/Teams/Feature.hs | 10 +++++----- 8 files changed, 13 insertions(+), 28 deletions(-) diff --git a/changelog.d/2-features/registration-url-in-invitation b/changelog.d/2-features/registration-url-in-invitation index 4b19c93c5c..40c5839800 100644 --- a/changelog.d/2-features/registration-url-in-invitation +++ b/changelog.d/2-features/registration-url-in-invitation @@ -1 +1 @@ -Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `settings.featureFlags.exposeInvitationURLsTeamAllowlist.teams` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. +Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `settings.featureFlags.exposeInvitationURLsTeamAllowlist` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index 72e2062f87..c5c80fd5d3 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -114,8 +114,7 @@ data: {{- toYaml .settings.featureFlags.mls | nindent 10 }} {{- end }} {{- if .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} - exposeInvitationURLsTeamAllowlist: - {{- toYaml .settings.featureFlags.exposeInvitationURLsTeamAllowlist | nindent 10 }} + exposeInvitationURLsTeamAllowlist: {{- .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} {{- end }} {{- end }} {{- end }} diff --git a/charts/galley/values.yaml b/charts/galley/values.yaml index 862f59439e..2af07d3eb9 100644 --- a/charts/galley/values.yaml +++ b/charts/galley/values.yaml @@ -79,8 +79,7 @@ config: validateSAMLemails: defaults: status: enabled - exposeInvitationURLsTeamAllowlist: - teams: [] + exposeInvitationURLsTeamAllowlist: [] aws: region: "eu-west-1" diff --git a/docs/src/developer/reference/config-options.md b/docs/src/developer/reference/config-options.md index f574843dba..9609f4b9c3 100644 --- a/docs/src/developer/reference/config-options.md +++ b/docs/src/developer/reference/config-options.md @@ -134,8 +134,7 @@ be added to the list of teams for which this feature *can* be enabled ```yaml settings: featureFlags: - exposeInvitationURLsTeamAllowlist: - teams: ["51612209-3b61-49b0-8c55-d21ae65efc1a"] + exposeInvitationURLsTeamAllowlist: ["51612209-3b61-49b0-8c55-d21ae65efc1a"] ``` Then, the feature can be set for the team by enabling the diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index 90152c9ebb..839efc6a54 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -202,7 +202,7 @@ instance FromJSON FeatureFlags where <*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge")) <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" - <*> (fromMaybe (ExposeInvitationURLsTeamAllowlistConfig []) <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) + <*> (fromMaybe [] <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) withImplicitLockStatusOrDefault obj fieldName = fromMaybe (Defaults (ImplicitLockStatus (defFeatureStatus @cfg))) <$> obj .:? fieldName diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 27e42ea5e2..6d201bb74f 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -68,7 +68,7 @@ module Wire.API.Team.Feature ConferenceCallingConfig (..), GuestLinksConfig (..), ExposeInvitationURLsToTeamAdminConfig (..), - ExposeInvitationURLsTeamAllowlistConfig (..), + ExposeInvitationURLsTeamAllowlistConfig, SndFactorPasswordChallengeConfig (..), SearchVisibilityInboundConfig (..), ClassifiedDomainsConfig (..), @@ -965,19 +965,7 @@ instance FeatureTrivialConfig ExposeInvitationURLsToTeamAdminConfig where ---------------------------------------------------------------------- -- ExposeInvitationURLsTeamAllowlistConfig -data ExposeInvitationURLsTeamAllowlistConfig = ExposeInvitationURLsTeamAllowlistConfig - { exposeInvitationURLsTeamAllowlist :: [TeamId] - } - deriving stock (Show, Eq, Generic) - deriving (ToJSON, FromJSON, S.ToSchema) via (Schema ExposeInvitationURLsTeamAllowlistConfig) - -deriving via (GenericUniform ExposeInvitationURLsTeamAllowlistConfig) instance Arbitrary ExposeInvitationURLsTeamAllowlistConfig - -instance ToSchema ExposeInvitationURLsTeamAllowlistConfig where - schema = - object "ExposeInvitationURLsTeamAllowlistConfig" $ - ExposeInvitationURLsTeamAllowlistConfig - <$> exposeInvitationURLsTeamAllowlist .= field "teams" (array schema) +type ExposeInvitationURLsTeamAllowlistConfig = [TeamId] ---------------------------------------------------------------------- -- FeatureStatus diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index 3bf6b7315b..ef743d7693 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -866,7 +866,7 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where getConfigForTeam tid = do allowList <- input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist) mbOldStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus - let teamAllowed = tid `elem` exposeInvitationURLsTeamAllowlist allowList + let teamAllowed = tid `elem` allowList pure $ case mbOldStatus of Nothing -> computeConfigForTeam teamAllowed FeatureStatusDisabled Just s -> computeConfigForTeam teamAllowed s diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index b81ba67296..d99d3d751b 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -58,7 +58,7 @@ import qualified Wire.API.Event.FeatureConfig as FeatureConfig import Wire.API.Internal.Notification (Notification) import Wire.API.MLS.CipherSuite import Wire.API.Routes.Internal.Galley.TeamFeatureNoConfigMulti as Multi -import Wire.API.Team.Feature (ExposeInvitationURLsTeamAllowlistConfig (..), ExposeInvitationURLsToTeamAdminConfig (..), FeatureStatus (..), FeatureTTL, FeatureTTL' (..), LockStatus (LockStatusUnlocked), MLSConfig (MLSConfig)) +import Wire.API.Team.Feature (ExposeInvitationURLsToTeamAdminConfig (..), FeatureStatus (..), FeatureTTL, FeatureTTL' (..), LockStatus (LockStatusUnlocked), MLSConfig (MLSConfig)) import qualified Wire.API.Team.Feature as Public tests :: IO TestSetup -> TestTree @@ -1146,7 +1146,7 @@ testExposeInvitationURLsToTeamAdminTeamIdInAllowList = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ [tid]) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1161,7 +1161,7 @@ testExposeInvitationURLsToTeamAdminEmptyAllowList = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ []) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1176,7 +1176,7 @@ testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig [tid]) $ do + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ [tid]) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1185,7 +1185,7 @@ testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do const 200 === statusCode assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ ExposeInvitationURLsTeamAllowlistConfig []) $ do + withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ []) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited From b2bca90ebc882f7fe8200041a4566b6182e316be Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 08:22:10 +0200 Subject: [PATCH 47/66] Fix galley mock setup --- .../brig/test/integration/API/User/Account.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/services/brig/test/integration/API/User/Account.hs b/services/brig/test/integration/API/User/Account.hs index 07a4ef632c..bc55d510e9 100644 --- a/services/brig/test/integration/API/User/Account.hs +++ b/services/brig/test/integration/API/User/Account.hs @@ -89,6 +89,7 @@ import Wire.API.Federation.API.Brig (UserDeletedConnectionsNotification (..)) import qualified Wire.API.Federation.API.Brig as FedBrig import Wire.API.Federation.API.Common (EmptyResponse (EmptyResponse)) import Wire.API.Internal.Notification +import Wire.API.Team.Feature (ExposeInvitationURLsToTeamAdminConfig (..), FeatureStatus (..), FeatureTTL' (..), LockStatus (LockStatusLocked), withStatus) import Wire.API.Team.Invitation (Invitation (inInvitation)) import Wire.API.Team.Permission hiding (self) import Wire.API.User @@ -1620,7 +1621,19 @@ testTooManyMembersForLegalhold opts brig = do "too-many-members-for-legalhold" "cannot add more members to team when legalhold service is enabled." ) - else pure $ Wai.responseLBS HTTP.status500 mempty "Unexpected request to mocked galley" + else + if mth == "GET" + && pth == ["i", "teams", Text.pack (show tid), "features", "exposeInvitationURLsToTeamAdmin"] + then + pure . Wai.responseLBS HTTP.status200 mempty $ + encode + ( withStatus + FeatureStatusDisabled + LockStatusLocked + ExposeInvitationURLsToTeamAdminConfig + FeatureTTLUnlimited + ) + else pure $ Wai.responseLBS HTTP.status500 mempty "Unexpected request to mocked galley" void . withMockedGalley opts mockGalley $ do post From 85f4d29abe1b17f3ea30fe842af05f911293e974 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 09:41:10 +0200 Subject: [PATCH 48/66] Fix Helm chart --- charts/galley/templates/configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index c5c80fd5d3..9619db41a3 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -114,7 +114,7 @@ data: {{- toYaml .settings.featureFlags.mls | nindent 10 }} {{- end }} {{- if .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} - exposeInvitationURLsTeamAllowlist: {{- .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} + exposeInvitationURLsTeamAllowlist: {{ .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} {{- end }} {{- end }} {{- end }} From 64690181020f9192c15a1eda4f2c9e7d7e143efc Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 11:55:07 +0200 Subject: [PATCH 49/66] Move allow list from feature flags to settings. --- charts/galley/templates/configmap.yaml | 6 +++--- charts/galley/values.yaml | 2 +- docs/src/developer/reference/config-options.md | 3 +-- libs/galley-types/src/Galley/Types/Teams.hs | 9 ++------- libs/galley-types/test/unit/Test/Galley/Types.hs | 1 - libs/wire-api/src/Wire/API/Team/Feature.hs | 6 ------ services/galley/src/Galley/API/Teams/Features.hs | 2 +- services/galley/src/Galley/Options.hs | 6 ++++++ .../galley/test/integration/API/Teams/Feature.hs | 12 ++++++------ 9 files changed, 20 insertions(+), 27 deletions(-) diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index 9619db41a3..a4839620c0 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -54,6 +54,9 @@ data: {{- if .settings.maxFanoutSize }} maxFanoutSize: {{ .settings.maxFanoutSize }} {{- end }} + {{- if .settings.exposeInvitationURLsTeamAllowlist }} + exposeInvitationURLsTeamAllowlist: {{ .settings.exposeInvitationURLsTeamAllowlist }} + {{- end }} conversationCodeURI: {{ .settings.conversationCodeURI | quote }} {{- if .settings.enableIndexedBillingTeamMembers }} enableIndexedBillingTeamMembers: {{ .settings.enableIndexedBillingTeamMembers }} @@ -113,8 +116,5 @@ data: mls: {{- toYaml .settings.featureFlags.mls | nindent 10 }} {{- end }} - {{- if .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} - exposeInvitationURLsTeamAllowlist: {{ .settings.featureFlags.exposeInvitationURLsTeamAllowlist }} - {{- end }} {{- end }} {{- end }} diff --git a/charts/galley/values.yaml b/charts/galley/values.yaml index 2af07d3eb9..4ee17754e5 100644 --- a/charts/galley/values.yaml +++ b/charts/galley/values.yaml @@ -26,6 +26,7 @@ config: settings: httpPoolSize: 128 maxTeamSize: 10000 + exposeInvitationURLsTeamAllowlist: [] maxConvSize: 500 # Before making indexedBillingTeamMember true while upgrading, please # refer to notes here: https://github.com/wireapp/wire-server-deploy/releases/tag/v2020-05-15 @@ -79,7 +80,6 @@ config: validateSAMLemails: defaults: status: enabled - exposeInvitationURLsTeamAllowlist: [] aws: region: "eu-west-1" diff --git a/docs/src/developer/reference/config-options.md b/docs/src/developer/reference/config-options.md index 9609f4b9c3..bdf4468601 100644 --- a/docs/src/developer/reference/config-options.md +++ b/docs/src/developer/reference/config-options.md @@ -133,8 +133,7 @@ be added to the list of teams for which this feature *can* be enabled ```yaml settings: - featureFlags: - exposeInvitationURLsTeamAllowlist: ["51612209-3b61-49b0-8c55-d21ae65efc1a"] + exposeInvitationURLsTeamAllowlist: ["51612209-3b61-49b0-8c55-d21ae65efc1a", ...] ``` Then, the feature can be set for the team by enabling the diff --git a/libs/galley-types/src/Galley/Types/Teams.hs b/libs/galley-types/src/Galley/Types/Teams.hs index 839efc6a54..a06c1b0ea0 100644 --- a/libs/galley-types/src/Galley/Types/Teams.hs +++ b/libs/galley-types/src/Galley/Types/Teams.hs @@ -39,7 +39,6 @@ module Galley.Types.Teams flagTeamFeatureSndFactorPasswordChallengeStatus, flagTeamFeatureSearchVisibilityInbound, flagMLS, - flagTeamFeatureExposeInvitationURLsTeamAllowlist, Defaults (..), ImplicitLockStatus (..), unImplicitLockStatus, @@ -151,8 +150,7 @@ data FeatureFlags = FeatureFlags _flagsTeamFeatureValidateSAMLEmailsStatus :: !(Defaults (ImplicitLockStatus ValidateSAMLEmailsConfig)), _flagTeamFeatureSndFactorPasswordChallengeStatus :: !(Defaults (WithStatus SndFactorPasswordChallengeConfig)), _flagTeamFeatureSearchVisibilityInbound :: !(Defaults (ImplicitLockStatus SearchVisibilityInboundConfig)), - _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)), - _flagTeamFeatureExposeInvitationURLsTeamAllowlist :: !ExposeInvitationURLsTeamAllowlistConfig + _flagMLS :: !(Defaults (ImplicitLockStatus MLSConfig)) } deriving (Eq, Show, Generic) @@ -202,7 +200,6 @@ instance FromJSON FeatureFlags where <*> (fromMaybe (Defaults (defFeatureStatus @SndFactorPasswordChallengeConfig)) <$> (obj .:? "sndFactorPasswordChallenge")) <*> withImplicitLockStatusOrDefault obj "searchVisibilityInbound" <*> withImplicitLockStatusOrDefault obj "mls" - <*> (fromMaybe [] <$> (obj .:? "exposeInvitationURLsTeamAllowlist")) where withImplicitLockStatusOrDefault :: forall cfg. (IsFeatureConfig cfg, Schema.ToSchema cfg) => Object -> Key -> A.Parser (Defaults (ImplicitLockStatus cfg)) withImplicitLockStatusOrDefault obj fieldName = fromMaybe (Defaults (ImplicitLockStatus (defFeatureStatus @cfg))) <$> obj .:? fieldName @@ -223,7 +220,6 @@ instance ToJSON FeatureFlags where sndFactorPasswordChallenge searchVisibilityInbound mls - exposeInvitationURLsTeamAllowlist ) = object [ "sso" .= sso, @@ -238,8 +234,7 @@ instance ToJSON FeatureFlags where "validateSAMLEmails" .= validateSAMLEmails, "sndFactorPasswordChallenge" .= sndFactorPasswordChallenge, "searchVisibilityInbound" .= searchVisibilityInbound, - "mls" .= mls, - "exposeInvitationURLsTeamAllowlist" .= exposeInvitationURLsTeamAllowlist + "mls" .= mls ] instance FromJSON FeatureSSO where diff --git a/libs/galley-types/test/unit/Test/Galley/Types.hs b/libs/galley-types/test/unit/Test/Galley/Types.hs index 367f0413f1..3e92614977 100644 --- a/libs/galley-types/test/unit/Test/Galley/Types.hs +++ b/libs/galley-types/test/unit/Test/Galley/Types.hs @@ -98,7 +98,6 @@ instance Arbitrary FeatureFlags where <*> arbitrary <*> fmap (fmap unlocked) arbitrary <*> fmap (fmap unlocked) arbitrary - <*> arbitrary where unlocked :: ImplicitLockStatus a -> ImplicitLockStatus a unlocked = ImplicitLockStatus . Public.setLockStatus Public.LockStatusUnlocked . _unImplicitLockStatus diff --git a/libs/wire-api/src/Wire/API/Team/Feature.hs b/libs/wire-api/src/Wire/API/Team/Feature.hs index 6d201bb74f..41eb91997a 100644 --- a/libs/wire-api/src/Wire/API/Team/Feature.hs +++ b/libs/wire-api/src/Wire/API/Team/Feature.hs @@ -68,7 +68,6 @@ module Wire.API.Team.Feature ConferenceCallingConfig (..), GuestLinksConfig (..), ExposeInvitationURLsToTeamAdminConfig (..), - ExposeInvitationURLsTeamAllowlistConfig, SndFactorPasswordChallengeConfig (..), SearchVisibilityInboundConfig (..), ClassifiedDomainsConfig (..), @@ -962,11 +961,6 @@ instance ToSchema ExposeInvitationURLsToTeamAdminConfig where instance FeatureTrivialConfig ExposeInvitationURLsToTeamAdminConfig where trivialConfig = ExposeInvitationURLsToTeamAdminConfig ----------------------------------------------------------------------- --- ExposeInvitationURLsTeamAllowlistConfig - -type ExposeInvitationURLsTeamAllowlistConfig = [TeamId] - ---------------------------------------------------------------------- -- FeatureStatus diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index ef743d7693..e8398a0223 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -864,7 +864,7 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where FeatureTTLUnlimited getConfigForTeam tid = do - allowList <- input <&> view (optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist) + allowList <- input <&> view (optSettings . setExposeInvitationURLsTeamAllowlist . to (fromMaybe [])) mbOldStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus let teamAllowed = tid `elem` allowList pure $ case mbOldStatus of diff --git a/services/galley/src/Galley/Options.hs b/services/galley/src/Galley/Options.hs index 2d82241913..edb3850d29 100644 --- a/services/galley/src/Galley/Options.hs +++ b/services/galley/src/Galley/Options.hs @@ -22,6 +22,7 @@ module Galley.Options setHttpPoolSize, setMaxTeamSize, setMaxFanoutSize, + setExposeInvitationURLsTeamAllowlist, setMaxConvSize, setIntraListing, setConversationCodeURI, @@ -56,6 +57,7 @@ where import Control.Lens hiding (Level, (.=)) import Data.Aeson.TH (deriveFromJSON) import Data.Domain (Domain) +import Data.Id (TeamId) import Data.Misc import Data.Range import Galley.Keys @@ -76,6 +78,10 @@ data Settings = Settings -- This defaults to setMaxTeamSize and cannot be > HardTruncationLimit. Useful -- to tune mainly for testing purposes. _setMaxFanoutSize :: !(Maybe (Range 1 HardTruncationLimit Int32)), + -- | List of teams for which the invitation URL can be added to the list of all + -- invitations retrievable by team admins. See also: + -- 'ExposeInvitationURLsToTeamAdminConfig'. + _setExposeInvitationURLsTeamAllowlist :: !(Maybe [TeamId]), -- | Max number of members in a conversation. NOTE: This must be in sync with Brig _setMaxConvSize :: !Word16, -- | Whether to call Brig for device listing diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index d99d3d751b..cd1033e348 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -27,7 +27,7 @@ import Bilge import Bilge.Assert import Brig.Types.Test.Arbitrary (Arbitrary (arbitrary)) import Cassandra as Cql -import Control.Lens (over, to, view, (.~)) +import Control.Lens (over, to, view, (.~), (?~)) import Control.Lens.Operators () import Control.Monad.Catch (MonadCatch) import Data.Aeson (FromJSON, ToJSON) @@ -42,7 +42,7 @@ import Data.Schema (ToSchema) import qualified Data.Set as Set import Data.Timeout (TimeoutUnit (Second), (#)) import GHC.TypeLits (KnownSymbol) -import Galley.Options (optSettings, setFeatureFlags) +import Galley.Options (optSettings, setExposeInvitationURLsTeamAllowlist, setFeatureFlags) import Galley.Types.Teams import Imports import Network.Wai.Utilities (label) @@ -1146,7 +1146,7 @@ testExposeInvitationURLsToTeamAdminTeamIdInAllowList = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ [tid]) $ do + withSettingsOverrides (\opts -> opts & optSettings . setExposeInvitationURLsTeamAllowlist ?~ [tid]) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1161,7 +1161,7 @@ testExposeInvitationURLsToTeamAdminEmptyAllowList = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ []) $ do + withSettingsOverrides (\opts -> opts & optSettings . setExposeInvitationURLsTeamAllowlist .~ Nothing) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1176,7 +1176,7 @@ testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do tid <- Util.createBindingTeamInternal "foo" owner assertQueue "create team" tActivate void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ [tid]) $ do + withSettingsOverrides (\opts -> opts & optSettings . setExposeInvitationURLsTeamAllowlist ?~ [tid]) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusUnlocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited @@ -1185,7 +1185,7 @@ testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do const 200 === statusCode assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusEnabled Public.LockStatusUnlocked void $ - withSettingsOverrides (\opts -> opts & optSettings . setFeatureFlags . flagTeamFeatureExposeInvitationURLsTeamAllowlist .~ []) $ do + withSettingsOverrides (\opts -> opts & optSettings . setExposeInvitationURLsTeamAllowlist .~ Nothing) $ do g <- view tsGalley assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked let enabled = Public.WithStatusNoLock Public.FeatureStatusEnabled ExposeInvitationURLsToTeamAdminConfig Public.FeatureTTLUnlimited From c2a8ddbe7a6502a9d4860a2ab16c360d7e9fad47 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 11:55:21 +0200 Subject: [PATCH 50/66] rm trailing whitespace. --- charts/galley/templates/configmap.yaml | 6 +++--- docs/src/developer/reference/config-options.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/galley/templates/configmap.yaml b/charts/galley/templates/configmap.yaml index a4839620c0..c5ce757ace 100644 --- a/charts/galley/templates/configmap.yaml +++ b/charts/galley/templates/configmap.yaml @@ -95,15 +95,15 @@ data: {{- if .settings.featureFlags.appLock }} appLock: {{- toYaml .settings.featureFlags.appLock | nindent 10 }} - {{- end }} + {{- end }} {{- if .settings.featureFlags.conferenceCalling }} conferenceCalling: {{- toYaml .settings.featureFlags.conferenceCalling | nindent 10 }} - {{- end }} + {{- end }} {{- if .settings.featureFlags.selfDeletingMessages }} selfDeletingMessages: {{- toYaml .settings.featureFlags.selfDeletingMessages | nindent 10 }} - {{- end }} + {{- end }} {{- if .settings.featureFlags.conversationGuestLinks }} conversationGuestLinks: {{- toYaml .settings.featureFlags.conversationGuestLinks | nindent 10 }} diff --git a/docs/src/developer/reference/config-options.md b/docs/src/developer/reference/config-options.md index bdf4468601..07ce0ec7ca 100644 --- a/docs/src/developer/reference/config-options.md +++ b/docs/src/developer/reference/config-options.md @@ -102,7 +102,7 @@ future. ### Expose invitation URLs to team admin For further processing (e.g. sending custom emails or rendering the URLs as QR -codes), team invitation URLs can be made part of the result of +codes), team invitation URLs can be made part of the result of `GET /teams/{tid}/invitations`. ```json From 5304f1bb36ec3c6814f189003acdaee56c64140c Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 12:25:53 +0200 Subject: [PATCH 51/66] Fixup --- services/galley/galley.integration.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/services/galley/galley.integration.yaml b/services/galley/galley.integration.yaml index be7d1fe7d6..a7759cad19 100644 --- a/services/galley/galley.integration.yaml +++ b/services/galley/galley.integration.yaml @@ -29,6 +29,7 @@ settings: httpPoolSize: 128 maxTeamSize: 32 maxFanoutSize: 18 + exposeInvitationURLsTeamAllowlist: [] maxConvSize: 16 intraListing: false conversationCodeURI: https://account.wire.com/conversation-join/ From 93c9920caa3d1d8156263c4df44f5200fb96c664 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 14:13:36 +0200 Subject: [PATCH 52/66] Add comment Co-authored-by: fisx --- services/galley/src/Galley/API/Teams/Features.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index e8398a0223..ad8be85aab 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -856,6 +856,7 @@ instance SetFeatureConfig db MLSConfig where instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where getConfigForServer = + -- we could look at the galley settings, but we don't have a team here, so there is not much else we can say. pure $ withStatus FeatureStatusDisabled From 480ce9563776d4d362cf310a4466586c4f36f67c Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 14:14:20 +0200 Subject: [PATCH 53/66] Simplify expression Co-authored-by: fisx --- services/galley/src/Galley/API/Teams/Features.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/galley/src/Galley/API/Teams/Features.hs b/services/galley/src/Galley/API/Teams/Features.hs index ad8be85aab..4556f935d7 100644 --- a/services/galley/src/Galley/API/Teams/Features.hs +++ b/services/galley/src/Galley/API/Teams/Features.hs @@ -868,9 +868,7 @@ instance GetFeatureConfig db ExposeInvitationURLsToTeamAdminConfig where allowList <- input <&> view (optSettings . setExposeInvitationURLsTeamAllowlist . to (fromMaybe [])) mbOldStatus <- TeamFeatures.getFeatureConfig @db (Proxy @ExposeInvitationURLsToTeamAdminConfig) tid <&> fmap wssStatus let teamAllowed = tid `elem` allowList - pure $ case mbOldStatus of - Nothing -> computeConfigForTeam teamAllowed FeatureStatusDisabled - Just s -> computeConfigForTeam teamAllowed s + pure $ computeConfigForTeam teamAllowed (fromMaybe FeatureStatusDisabled mbOldStatus) where computeConfigForTeam :: Bool -> FeatureStatus -> WithStatus ExposeInvitationURLsToTeamAdminConfig computeConfigForTeam teamAllowed teamDbStatus = From d05c896614ab23e5d30cb7453925e6c959e7c6f3 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 14:15:08 +0200 Subject: [PATCH 54/66] Refer to docs in changelog Co-authored-by: fisx --- changelog.d/2-features/registration-url-in-invitation | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/2-features/registration-url-in-invitation b/changelog.d/2-features/registration-url-in-invitation index 40c5839800..eb9669b1fc 100644 --- a/changelog.d/2-features/registration-url-in-invitation +++ b/changelog.d/2-features/registration-url-in-invitation @@ -1 +1 @@ -Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. As this feature can cause privacy issues (the administrator could do the registrations on their own), it's enabled in two steps: First, the team ID has to be added to the list `settings.featureFlags.exposeInvitationURLsTeamAllowlist` in galley's server configuration. Then, the team feature flag `exposeInvitationURLsToTeamAdmin` can be enabled. +Optionally add invitation urls to the body of `/teams/{tid}/invitations`. This allows further processing; e.g. to send those links with custom emails or distribute them as QR codes. See [docs](https://docs.wire.com/developer/reference/config-options.html#expose-invitation-urls-to-team-admin) for details and privacy implications. From 1a3c6887e743944380e7404dcfe2e64aba63e961 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 17:08:56 +0200 Subject: [PATCH 55/66] Move the decision of invitation url rendering out of DB --- services/brig/src/Brig/API/Internal.hs | 2 +- services/brig/src/Brig/API/User.hs | 2 +- services/brig/src/Brig/Team/API.hs | 11 ++++++---- services/brig/src/Brig/Team/DB.hs | 30 ++++++++++---------------- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/services/brig/src/Brig/API/Internal.hs b/services/brig/src/Brig/API/Internal.hs index db8ab4b248..4a548f766d 100644 --- a/services/brig/src/Brig/API/Internal.hs +++ b/services/brig/src/Brig/API/Internal.hs @@ -556,7 +556,7 @@ listActivatedAccounts elh includePendingInvitations = do case (accountStatus account, includePendingInvitations, emailIdentity ident) of (PendingInvitation, False, _) -> pure False (PendingInvitation, True, Just email) -> do - hasInvitation <- isJust <$> wrapHttp (lookupInvitationByEmail email) + hasInvitation <- isJust <$> wrapClient (lookupInvitationByEmail email) unless hasInvitation $ do -- user invited via scim should expire together with its invitation API.deleteUserNoVerify (userId . accountUser $ account) diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index b16ba7e470..3f83464b0d 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -413,7 +413,7 @@ createUser new = do findTeamInvitation (Just e) c = lift (wrapClient $ Team.lookupInvitationInfo c) >>= \case Just ii -> do - inv <- lift . wrapHttp $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) + inv <- lift . wrapClient $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) False case (inv, Team.inInviteeEmail <$> inv) of (Just invite, Just em) | e == userEmailKey em -> do diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index 65980ed54e..54c69075ec 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -36,6 +36,7 @@ import qualified Brig.Email as Email import qualified Brig.IO.Intra as Intra import Brig.Options (setMaxTeamSize, setTeamInvitationTimeout) import qualified Brig.Phone as Phone +import Brig.Team.DB (getTeamExposeInvitationURLsToTeamAdmin) import qualified Brig.Team.DB as DB import Brig.Team.Email import Brig.Team.Util (ensurePermissionToAddUser, ensurePermissions) @@ -412,7 +413,8 @@ listInvitationsH (_ ::: uid ::: tid ::: start ::: size) = do listInvitations :: UserId -> TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> (Handler r) Public.InvitationList listInvitations uid tid start size = do ensurePermissions uid tid [AddTeamMember] - rs <- lift $ wrapHttp $ DB.lookupInvitations tid start size + showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid + rs <- lift $ wrapClient $ DB.lookupInvitations tid start size showInvitationUrl pure $! Public.InvitationList (DB.resultList rs) (DB.resultHasMore rs) getInvitationH :: JSON ::: UserId ::: TeamId ::: InvitationId -> (Handler r) Response @@ -425,7 +427,8 @@ getInvitationH (_ ::: uid ::: tid ::: iid) = do getInvitation :: UserId -> TeamId -> InvitationId -> (Handler r) (Maybe Public.Invitation) getInvitation uid tid iid = do ensurePermissions uid tid [AddTeamMember] - lift $ wrapHttp $ DB.lookupInvitation tid iid + showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid + lift $ wrapClient $ DB.lookupInvitation tid iid showInvitationUrl getInvitationByCodeH :: JSON ::: Public.InvitationCode -> (Handler r) Response getInvitationByCodeH (_ ::: c) = do @@ -433,7 +436,7 @@ getInvitationByCodeH (_ ::: c) = do getInvitationByCode :: Public.InvitationCode -> (Handler r) Public.Invitation getInvitationByCode c = do - inv <- lift . wrapHttp $ DB.lookupInvitationByCode c + inv <- lift . wrapClient $ DB.lookupInvitationByCode c maybe (throwStd $ errorToWai @'E.InvalidInvitationCode) pure inv headInvitationByEmailH :: JSON ::: Email -> (Handler r) Response @@ -453,7 +456,7 @@ getInvitationByEmailH (_ ::: email) = getInvitationByEmail :: Email -> (Handler r) Public.Invitation getInvitationByEmail email = do - inv <- lift $ wrapHttp $ DB.lookupInvitationByEmail email + inv <- lift $ wrapClient $ DB.lookupInvitationByEmail email maybe (throwStd (notFound "Invitation not found")) pure inv suspendTeamH :: JSON ::: TeamId -> (Handler r) Response diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index fa561b91fe..48b54e775e 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -32,6 +32,7 @@ module Brig.Team.DB lookupInvitationByEmail, mkInvitationCode, mkInvitationId, + getTeamExposeInvitationURLsToTeamAdmin, InvitationInfo (..), InvitationByEmail (..), ) @@ -128,20 +129,17 @@ insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName cqlInvitationByEmail = "INSERT INTO team_invitation_email (email, team, invitation, code) VALUES (?, ?, ?, ?) USING TTL ?" lookupInvitation :: - ( Log.MonadLogger m, + ( MonadClient m, MonadReader Env m, - MonadMask m, - MonadHttp m, - HasRequestId m, - MonadClient m + Log.MonadLogger m ) => TeamId -> InvitationId -> + Bool -> m (Maybe Invitation) -lookupInvitation t r = do - showUrl <- getTeamExposeInvitationURLsToTeamAdmin t +lookupInvitation t r showInvitationUrl = do inv <- retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) - traverse (toInvitation showUrl) inv + traverse (toInvitation showInvitationUrl) inv where cqlInvitation :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) cqlInvitation = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? AND id = ?" @@ -150,15 +148,13 @@ lookupInvitationByCode :: ( Log.MonadLogger m, MonadReader Env m, MonadMask m, - MonadHttp m, - HasRequestId m, MonadClient m ) => InvitationCode -> m (Maybe Invitation) lookupInvitationByCode i = lookupInvitationInfo i >>= \case - Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId + Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId False _ -> pure Nothing lookupInvitationCode :: MonadClient m => TeamId -> InvitationId -> m (Maybe InvitationCode) @@ -179,20 +175,18 @@ lookupInvitations :: ( Log.MonadLogger m, MonadReader Env m, MonadMask m, - MonadHttp m, - HasRequestId m, MonadClient m ) => TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> + Bool -> m (ResultPage Invitation) -lookupInvitations team start (fromRange -> size) = do +lookupInvitations team start (fromRange -> size) showInvitationUrl = do page <- case start of Just ref -> retry x1 $ paginate cqlSelectFrom (paramsP LocalQuorum (team, ref) (size + 1)) Nothing -> retry x1 $ paginate cqlSelect (paramsP LocalQuorum (Identity team) (size + 1)) - showUrl <- getTeamExposeInvitationURLsToTeamAdmin team - toResult (hasMore page) <$> traverse (toInvitation showUrl) (trim page) + toResult (hasMore page) <$> traverse (toInvitation showInvitationUrl) (trim page) where trim p = take (fromIntegral size) (result p) toResult more invs = @@ -251,15 +245,13 @@ lookupInvitationByEmail :: ( Log.MonadLogger m, MonadReader Env m, MonadMask m, - MonadHttp m, - HasRequestId m, MonadClient m ) => Email -> m (Maybe Invitation) lookupInvitationByEmail e = lookupInvitationInfoByEmail e >>= \case - InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId + InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId False _ -> pure Nothing lookupInvitationInfoByEmail :: (Log.MonadLogger m, MonadClient m) => Email -> m InvitationByEmail From 4f603bcd8d684cd4b4e5f5569ae0ef436db57fa7 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 17:45:19 +0200 Subject: [PATCH 56/66] Add FUTUREWORK note --- services/brig/src/Brig/Team/DB.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 48b54e775e..fde3ecc04f 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -88,6 +88,9 @@ data InvitationByEmail | InvitationByEmailNotFound | InvitationByEmailMoreThanOne +-- FUTUREWORK: This module (Brig.Team.DB) should not contain functions that need +-- MonadHttp. Split insertInvitation up into a database and a HTTP related part. +-- Then, move the HTTP part elsewhere. insertInvitation :: ( Log.MonadLogger m, MonadReader Env m, From 325b588687e716bfa70b32a73debe9f3bcc2df4d Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Fri, 16 Sep 2022 17:56:38 +0200 Subject: [PATCH 57/66] Explain why a test case is needed (Haddock) --- services/galley/test/integration/API/Teams/Feature.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index cd1033e348..28755f80f7 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -1170,6 +1170,12 @@ testExposeInvitationURLsToTeamAdminEmptyAllowList = do const 409 === statusCode assertExposeInvitationURLsToTeamAdminConfigStatus owner tid FeatureStatusDisabled Public.LockStatusLocked +-- | Ensure that the server config takes precedence over a saved team config. +-- +-- In other words: When a team id is no longer in the +-- `setExposeInvitationURLsTeamAllowlist` the +-- `ExposeInvitationURLsToTeamAdminConfig` is always disabled (even tough it +-- might have been enabled before). testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence :: TestM () testExposeInvitationURLsToTeamAdminServerConfigTakesPrecedence = do owner <- Util.randomUser From 1a93732eb9c5288c812e1da4925c0a137dbdadd9 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 21:46:13 +0200 Subject: [PATCH 58/66] Remove *all* the lookups to the feature flag from `Brig.Team.DB`. --- services/brig/src/Brig/Team/API.hs | 2 ++ services/brig/src/Brig/Team/DB.hs | 16 ++-------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index 54c69075ec..961a819299 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -378,6 +378,7 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do let locale = irLocale body let inviteeName = irInviteeName body + showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid lift $ do iid <- liftIO DB.mkInvitationId @@ -395,6 +396,7 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do inviteeName inviteePhone timeout + showInvitationUrl (newInv, code) <$ sendInvitationMail inviteeEmail tid fromEmail code locale deleteInvitationH :: JSON ::: UserId ::: TeamId ::: InvitationId -> (Handler r) Response diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index fde3ecc04f..221cdd6217 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -38,8 +38,6 @@ module Brig.Team.DB ) where -import Bilge.IO -import Bilge.RPC import Brig.App as App import Brig.Data.Instances () import Brig.Data.Types as T @@ -49,7 +47,6 @@ import Brig.Team.Template import Brig.Template (renderTextWithBranding) import Cassandra as C import Control.Lens (view) -import Control.Monad.Catch (MonadMask) import Data.Bifunctor import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C @@ -88,15 +85,9 @@ data InvitationByEmail | InvitationByEmailNotFound | InvitationByEmailMoreThanOne --- FUTUREWORK: This module (Brig.Team.DB) should not contain functions that need --- MonadHttp. Split insertInvitation up into a database and a HTTP related part. --- Then, move the HTTP part elsewhere. insertInvitation :: ( Log.MonadLogger m, MonadReader Env m, - MonadMask m, - MonadHttp m, - HasRequestId m, MonadClient m ) => InvitationId -> @@ -109,10 +100,10 @@ insertInvitation :: Maybe Phone -> -- | The timeout for the invitation code. Timeout -> + Bool -> m (Invitation, InvitationCode) -insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout = do +insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout showUrl = do code <- liftIO mkInvitationCode - showUrl <- getTeamExposeInvitationURLsToTeamAdmin t url <- if showUrl then mkInviteUrl t code else pure Nothing let inv = Invitation t role iid now minviter email inviteeName phone url retry x5 . batch $ do @@ -150,7 +141,6 @@ lookupInvitation t r showInvitationUrl = do lookupInvitationByCode :: ( Log.MonadLogger m, MonadReader Env m, - MonadMask m, MonadClient m ) => InvitationCode -> @@ -177,7 +167,6 @@ lookupInvitationCodeEmail t r = retry x1 (query1 cqlInvitationCodeEmail (params lookupInvitations :: ( Log.MonadLogger m, MonadReader Env m, - MonadMask m, MonadClient m ) => TeamId -> @@ -247,7 +236,6 @@ lookupInvitationInfo ic@(InvitationCode c) lookupInvitationByEmail :: ( Log.MonadLogger m, MonadReader Env m, - MonadMask m, MonadClient m ) => Email -> From 904b0b03adfd9b22a8033a3ca6d78e3763075619 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 21:59:22 +0200 Subject: [PATCH 59/66] Make custom bool. --- services/brig/brig.cabal | 1 + services/brig/src/Brig/API/User.hs | 3 ++- services/brig/src/Brig/IO/Intra.hs | 7 ++++--- services/brig/src/Brig/Team/DB.hs | 29 +++++++++++++++------------- services/brig/src/Brig/Team/Types.hs | 23 ++++++++++++++++++++++ 5 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 services/brig/src/Brig/Team/Types.hs diff --git a/services/brig/brig.cabal b/services/brig/brig.cabal index 392f90d30e..95f744d763 100644 --- a/services/brig/brig.cabal +++ b/services/brig/brig.cabal @@ -96,6 +96,7 @@ library Brig.Team.DB Brig.Team.Email Brig.Team.Template + Brig.Team.Types Brig.Team.Util Brig.Template Brig.Unique diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index 3f83464b0d..b9570f6aa7 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -125,6 +125,7 @@ import Brig.Options hiding (Timeout, internalEvents) import Brig.Password import qualified Brig.Queue as Queue import qualified Brig.Team.DB as Team +import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Types.Activation (ActivationPair) import Brig.Types.Connection import Brig.Types.Intra @@ -413,7 +414,7 @@ createUser new = do findTeamInvitation (Just e) c = lift (wrapClient $ Team.lookupInvitationInfo c) >>= \case Just ii -> do - inv <- lift . wrapClient $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) False + inv <- lift . wrapClient $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) HideInvitationUrl case (inv, Team.inInviteeEmail <$> inv) of (Just invite, Just em) | e == userEmailKey em -> do diff --git a/services/brig/src/Brig/IO/Intra.hs b/services/brig/src/Brig/IO/Intra.hs index 937e0456c4..c5c4b65042 100644 --- a/services/brig/src/Brig/IO/Intra.hs +++ b/services/brig/src/Brig/IO/Intra.hs @@ -81,6 +81,7 @@ import qualified Brig.Data.Connection as Data import Brig.Federation.Client (notifyUserDeleted) import qualified Brig.IO.Journal as Journal import Brig.RPC +import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Types.User.Event import Brig.User.Search.Index (MonadIndexIO) import qualified Brig.User.Search.Index as Search @@ -1377,14 +1378,14 @@ getTeamExposeInvitationURLsToTeamAdmin :: HasRequestId m ) => TeamId -> - m Bool + m ShowOrHideInvitationUrl getTeamExposeInvitationURLsToTeamAdmin tid = do debug $ remote "galley" . msg (val "Get expose invitation URLs to team admin settings") response <- galleyRequest GET req status <- wsStatus <$> decodeBody @(WithStatus ExposeInvitationURLsToTeamAdminConfig) "galley" response case status of - FeatureStatusEnabled -> pure True - FeatureStatusDisabled -> pure False + FeatureStatusEnabled -> pure ShowInvitationUrl + FeatureStatusDisabled -> pure HideInvitationUrl where req = paths ["i", "teams", toByteString' tid, "features", featureNameBS @ExposeInvitationURLsToTeamAdminConfig] diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 221cdd6217..96651b45f2 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -44,6 +44,7 @@ import Brig.Data.Types as T import Brig.IO.Intra import Brig.Options import Brig.Team.Template +import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Template (renderTextWithBranding) import Cassandra as C import Control.Lens (view) @@ -100,11 +101,11 @@ insertInvitation :: Maybe Phone -> -- | The timeout for the invitation code. Timeout -> - Bool -> + ShowOrHideInvitationUrl -> m (Invitation, InvitationCode) insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout showUrl = do code <- liftIO mkInvitationCode - url <- if showUrl then mkInviteUrl t code else pure Nothing + url <- mkInviteUrl t code showUrl let inv = Invitation t role iid now minviter email inviteeName phone url retry x5 . batch $ do setType BatchLogged @@ -129,11 +130,11 @@ lookupInvitation :: ) => TeamId -> InvitationId -> - Bool -> + ShowOrHideInvitationUrl -> m (Maybe Invitation) -lookupInvitation t r showInvitationUrl = do +lookupInvitation t r showUrl = do inv <- retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) - traverse (toInvitation showInvitationUrl) inv + traverse (toInvitation showUrl) inv where cqlInvitation :: PrepQuery R (TeamId, InvitationId) (TeamId, Maybe Role, InvitationId, UTCTimeMillis, Maybe UserId, Email, Maybe Name, Maybe Phone, InvitationCode) cqlInvitation = "SELECT team, role, id, created_at, created_by, email, name, phone, code FROM team_invitation WHERE team = ? AND id = ?" @@ -147,7 +148,7 @@ lookupInvitationByCode :: m (Maybe Invitation) lookupInvitationByCode i = lookupInvitationInfo i >>= \case - Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId False + Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId HideInvitationUrl _ -> pure Nothing lookupInvitationCode :: MonadClient m => TeamId -> InvitationId -> m (Maybe InvitationCode) @@ -172,13 +173,13 @@ lookupInvitations :: TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> - Bool -> + ShowOrHideInvitationUrl -> m (ResultPage Invitation) -lookupInvitations team start (fromRange -> size) showInvitationUrl = do +lookupInvitations team start (fromRange -> size) showUrl = do page <- case start of Just ref -> retry x1 $ paginate cqlSelectFrom (paramsP LocalQuorum (team, ref) (size + 1)) Nothing -> retry x1 $ paginate cqlSelect (paramsP LocalQuorum (Identity team) (size + 1)) - toResult (hasMore page) <$> traverse (toInvitation showInvitationUrl) (trim page) + toResult (hasMore page) <$> traverse (toInvitation showUrl) (trim page) where trim p = take (fromIntegral size) (result p) toResult more invs = @@ -242,7 +243,7 @@ lookupInvitationByEmail :: m (Maybe Invitation) lookupInvitationByEmail e = lookupInvitationInfoByEmail e >>= \case - InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId False + InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId HideInvitationUrl _ -> pure Nothing lookupInvitationInfoByEmail :: (Log.MonadLogger m, MonadClient m) => Email -> m InvitationByEmail @@ -277,7 +278,7 @@ toInvitation :: ( MonadReader Env m, Log.MonadLogger m ) => - Bool -> + ShowOrHideInvitationUrl -> ( TeamId, Maybe Role, InvitationId, @@ -290,7 +291,7 @@ toInvitation :: ) -> m Invitation toInvitation showUrl (t, r, i, tm, minviter, e, inviteeName, p, code) = do - url <- if showUrl then mkInviteUrl t code else pure Nothing + url <- mkInviteUrl t code showUrl pure $ Invitation t (fromMaybe defaultRole r) i tm minviter e inviteeName p url mkInviteUrl :: @@ -299,8 +300,10 @@ mkInviteUrl :: ) => TeamId -> InvitationCode -> + ShowOrHideInvitationUrl -> m (Maybe (URIRef Absolute)) -mkInviteUrl team (InvitationCode c) = do +mkInviteUrl _ _ HideInvitationUrl = pure Nothing +mkInviteUrl team (InvitationCode c) ShowInvitationUrl = do template <- invitationEmailUrl . invitationEmail . snd <$> teamTemplates Nothing branding <- view App.templateBranding let url = toStrict $ renderTextWithBranding template replace branding diff --git a/services/brig/src/Brig/Team/Types.hs b/services/brig/src/Brig/Team/Types.hs new file mode 100644 index 0000000000..e85bc4eb5b --- /dev/null +++ b/services/brig/src/Brig/Team/Types.hs @@ -0,0 +1,23 @@ +-- 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.Team.Types where + +import Imports + +data ShowOrHideInvitationUrl = ShowInvitationUrl | HideInvitationUrl + deriving (Eq, Show) From f0bb831dac02578e7a647bde398a70b2454e50ac Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 22:05:01 +0200 Subject: [PATCH 60/66] Move Bool into the first argument position in all of `Brig.Team.DB`. --- services/brig/src/Brig/API/User.hs | 2 +- services/brig/src/Brig/Team/API.hs | 6 +++--- services/brig/src/Brig/Team/DB.hs | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/services/brig/src/Brig/API/User.hs b/services/brig/src/Brig/API/User.hs index b9570f6aa7..ea3932461f 100644 --- a/services/brig/src/Brig/API/User.hs +++ b/services/brig/src/Brig/API/User.hs @@ -414,7 +414,7 @@ createUser new = do findTeamInvitation (Just e) c = lift (wrapClient $ Team.lookupInvitationInfo c) >>= \case Just ii -> do - inv <- lift . wrapClient $ Team.lookupInvitation (Team.iiTeam ii) (Team.iiInvId ii) HideInvitationUrl + inv <- lift . wrapClient $ Team.lookupInvitation HideInvitationUrl (Team.iiTeam ii) (Team.iiInvId ii) case (inv, Team.inInviteeEmail <$> inv) of (Just invite, Just em) | e == userEmailKey em -> do diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index 961a819299..d4e0003ea6 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -387,6 +387,7 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do (newInv, code) <- wrapHttp $ DB.insertInvitation + showInvitationUrl iid tid inviteeRole @@ -396,7 +397,6 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do inviteeName inviteePhone timeout - showInvitationUrl (newInv, code) <$ sendInvitationMail inviteeEmail tid fromEmail code locale deleteInvitationH :: JSON ::: UserId ::: TeamId ::: InvitationId -> (Handler r) Response @@ -416,7 +416,7 @@ listInvitations :: UserId -> TeamId -> Maybe InvitationId -> Range 1 500 Int32 - listInvitations uid tid start size = do ensurePermissions uid tid [AddTeamMember] showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid - rs <- lift $ wrapClient $ DB.lookupInvitations tid start size showInvitationUrl + rs <- lift $ wrapClient $ DB.lookupInvitations showInvitationUrl tid start size pure $! Public.InvitationList (DB.resultList rs) (DB.resultHasMore rs) getInvitationH :: JSON ::: UserId ::: TeamId ::: InvitationId -> (Handler r) Response @@ -430,7 +430,7 @@ getInvitation :: UserId -> TeamId -> InvitationId -> (Handler r) (Maybe Public.I getInvitation uid tid iid = do ensurePermissions uid tid [AddTeamMember] showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid - lift $ wrapClient $ DB.lookupInvitation tid iid showInvitationUrl + lift $ wrapClient $ DB.lookupInvitation showInvitationUrl tid iid getInvitationByCodeH :: JSON ::: Public.InvitationCode -> (Handler r) Response getInvitationByCodeH (_ ::: c) = do diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 96651b45f2..e10ace27fd 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -91,6 +91,7 @@ insertInvitation :: MonadReader Env m, MonadClient m ) => + ShowOrHideInvitationUrl -> InvitationId -> TeamId -> Role -> @@ -101,11 +102,10 @@ insertInvitation :: Maybe Phone -> -- | The timeout for the invitation code. Timeout -> - ShowOrHideInvitationUrl -> m (Invitation, InvitationCode) -insertInvitation iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout showUrl = do +insertInvitation showUrl iid t role (toUTCTimeMillis -> now) minviter email inviteeName phone timeout = do code <- liftIO mkInvitationCode - url <- mkInviteUrl t code showUrl + url <- mkInviteUrl showUrl t code let inv = Invitation t role iid now minviter email inviteeName phone url retry x5 . batch $ do setType BatchLogged @@ -128,11 +128,11 @@ lookupInvitation :: MonadReader Env m, Log.MonadLogger m ) => + ShowOrHideInvitationUrl -> TeamId -> InvitationId -> - ShowOrHideInvitationUrl -> m (Maybe Invitation) -lookupInvitation t r showUrl = do +lookupInvitation showUrl t r = do inv <- retry x1 (query1 cqlInvitation (params LocalQuorum (t, r))) traverse (toInvitation showUrl) inv where @@ -148,7 +148,7 @@ lookupInvitationByCode :: m (Maybe Invitation) lookupInvitationByCode i = lookupInvitationInfo i >>= \case - Just InvitationInfo {..} -> lookupInvitation iiTeam iiInvId HideInvitationUrl + Just InvitationInfo {..} -> lookupInvitation HideInvitationUrl iiTeam iiInvId _ -> pure Nothing lookupInvitationCode :: MonadClient m => TeamId -> InvitationId -> m (Maybe InvitationCode) @@ -170,12 +170,12 @@ lookupInvitations :: MonadReader Env m, MonadClient m ) => + ShowOrHideInvitationUrl -> TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> - ShowOrHideInvitationUrl -> m (ResultPage Invitation) -lookupInvitations team start (fromRange -> size) showUrl = do +lookupInvitations showUrl team start (fromRange -> size) = do page <- case start of Just ref -> retry x1 $ paginate cqlSelectFrom (paramsP LocalQuorum (team, ref) (size + 1)) Nothing -> retry x1 $ paginate cqlSelect (paramsP LocalQuorum (Identity team) (size + 1)) @@ -243,7 +243,7 @@ lookupInvitationByEmail :: m (Maybe Invitation) lookupInvitationByEmail e = lookupInvitationInfoByEmail e >>= \case - InvitationByEmail InvitationInfo {..} -> lookupInvitation iiTeam iiInvId HideInvitationUrl + InvitationByEmail InvitationInfo {..} -> lookupInvitation HideInvitationUrl iiTeam iiInvId _ -> pure Nothing lookupInvitationInfoByEmail :: (Log.MonadLogger m, MonadClient m) => Email -> m InvitationByEmail @@ -291,19 +291,19 @@ toInvitation :: ) -> m Invitation toInvitation showUrl (t, r, i, tm, minviter, e, inviteeName, p, code) = do - url <- mkInviteUrl t code showUrl + url <- mkInviteUrl showUrl t code pure $ Invitation t (fromMaybe defaultRole r) i tm minviter e inviteeName p url mkInviteUrl :: ( MonadReader Env m, Log.MonadLogger m ) => + ShowOrHideInvitationUrl -> TeamId -> InvitationCode -> - ShowOrHideInvitationUrl -> m (Maybe (URIRef Absolute)) -mkInviteUrl _ _ HideInvitationUrl = pure Nothing -mkInviteUrl team (InvitationCode c) ShowInvitationUrl = do +mkInviteUrl HideInvitationUrl _ _ = pure Nothing +mkInviteUrl ShowInvitationUrl team (InvitationCode c) = do template <- invitationEmailUrl . invitationEmail . snd <$> teamTemplates Nothing branding <- view App.templateBranding let url = toStrict $ renderTextWithBranding template replace branding From ff74b29d5a3e96ed911e49b494756476c271b996 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 22:11:06 +0200 Subject: [PATCH 61/66] Expose `ShowOrHideInvitationUrl` to application logic consistently. --- services/brig/src/Brig/API/Internal.hs | 3 ++- services/brig/src/Brig/Team/API.hs | 5 +++-- services/brig/src/Brig/Team/DB.hs | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/services/brig/src/Brig/API/Internal.hs b/services/brig/src/Brig/API/Internal.hs index 4a548f766d..d4d567f4ba 100644 --- a/services/brig/src/Brig/API/Internal.hs +++ b/services/brig/src/Brig/API/Internal.hs @@ -50,6 +50,7 @@ import Brig.Options hiding (internalEvents, sesQueue) import qualified Brig.Provider.API as Provider import qualified Brig.Team.API as Team import Brig.Team.DB (lookupInvitationByEmail) +import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Types.Connection import Brig.Types.Intra import Brig.Types.Team.LegalHold (LegalHoldClientRequest (..)) @@ -556,7 +557,7 @@ listActivatedAccounts elh includePendingInvitations = do case (accountStatus account, includePendingInvitations, emailIdentity ident) of (PendingInvitation, False, _) -> pure False (PendingInvitation, True, Just email) -> do - hasInvitation <- isJust <$> wrapClient (lookupInvitationByEmail email) + hasInvitation <- isJust <$> wrapClient (lookupInvitationByEmail HideInvitationUrl email) unless hasInvitation $ do -- user invited via scim should expire together with its invitation API.deleteUserNoVerify (userId . accountUser $ account) diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index d4e0003ea6..c837d94bd2 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -39,6 +39,7 @@ import qualified Brig.Phone as Phone import Brig.Team.DB (getTeamExposeInvitationURLsToTeamAdmin) import qualified Brig.Team.DB as DB import Brig.Team.Email +import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Team.Util (ensurePermissionToAddUser, ensurePermissions) import Brig.Types.Intra (AccountStatus (..), NewUserScimInvitation (..), UserAccount (..)) import Brig.Types.Team (TeamSize) @@ -438,7 +439,7 @@ getInvitationByCodeH (_ ::: c) = do getInvitationByCode :: Public.InvitationCode -> (Handler r) Public.Invitation getInvitationByCode c = do - inv <- lift . wrapClient $ DB.lookupInvitationByCode c + inv <- lift . wrapClient $ DB.lookupInvitationByCode HideInvitationUrl c maybe (throwStd $ errorToWai @'E.InvalidInvitationCode) pure inv headInvitationByEmailH :: JSON ::: Email -> (Handler r) Response @@ -458,7 +459,7 @@ getInvitationByEmailH (_ ::: email) = getInvitationByEmail :: Email -> (Handler r) Public.Invitation getInvitationByEmail email = do - inv <- lift $ wrapClient $ DB.lookupInvitationByEmail email + inv <- lift $ wrapClient $ DB.lookupInvitationByEmail HideInvitationUrl email maybe (throwStd (notFound "Invitation not found")) pure inv suspendTeamH :: JSON ::: TeamId -> (Handler r) Response diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index e10ace27fd..8fc7a8fb92 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -144,11 +144,12 @@ lookupInvitationByCode :: MonadReader Env m, MonadClient m ) => + ShowOrHideInvitationUrl -> InvitationCode -> m (Maybe Invitation) -lookupInvitationByCode i = +lookupInvitationByCode showUrl i = lookupInvitationInfo i >>= \case - Just InvitationInfo {..} -> lookupInvitation HideInvitationUrl iiTeam iiInvId + Just InvitationInfo {..} -> lookupInvitation showUrl iiTeam iiInvId _ -> pure Nothing lookupInvitationCode :: MonadClient m => TeamId -> InvitationId -> m (Maybe InvitationCode) @@ -239,11 +240,12 @@ lookupInvitationByEmail :: MonadReader Env m, MonadClient m ) => + ShowOrHideInvitationUrl -> Email -> m (Maybe Invitation) -lookupInvitationByEmail e = +lookupInvitationByEmail showUrl e = lookupInvitationInfoByEmail e >>= \case - InvitationByEmail InvitationInfo {..} -> lookupInvitation HideInvitationUrl iiTeam iiInvId + InvitationByEmail InvitationInfo {..} -> lookupInvitation showUrl iiTeam iiInvId _ -> pure Nothing lookupInvitationInfoByEmail :: (Log.MonadLogger m, MonadClient m) => Email -> m InvitationByEmail From 96b35f69db37580eae89fcc9b6be44be3549d5fd Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 22:26:22 +0200 Subject: [PATCH 62/66] Cleanup --- services/brig/src/Brig/Team/API.hs | 9 ++++----- services/brig/src/Brig/Team/DB.hs | 2 -- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/services/brig/src/Brig/Team/API.hs b/services/brig/src/Brig/Team/API.hs index c837d94bd2..f00cd9ae1b 100644 --- a/services/brig/src/Brig/Team/API.hs +++ b/services/brig/src/Brig/Team/API.hs @@ -36,7 +36,6 @@ import qualified Brig.Email as Email import qualified Brig.IO.Intra as Intra import Brig.Options (setMaxTeamSize, setTeamInvitationTimeout) import qualified Brig.Phone as Phone -import Brig.Team.DB (getTeamExposeInvitationURLsToTeamAdmin) import qualified Brig.Team.DB as DB import Brig.Team.Email import Brig.Team.Types (ShowOrHideInvitationUrl (..)) @@ -379,14 +378,14 @@ createInvitation' tid inviteeRole mbInviterUid fromEmail body = do let locale = irLocale body let inviteeName = irInviteeName body - showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid + showInvitationUrl <- lift $ wrapHttp $ Intra.getTeamExposeInvitationURLsToTeamAdmin tid lift $ do iid <- liftIO DB.mkInvitationId now <- liftIO =<< view currentTime timeout <- setTeamInvitationTimeout <$> view settings (newInv, code) <- - wrapHttp $ + wrapClient $ DB.insertInvitation showInvitationUrl iid @@ -416,7 +415,7 @@ listInvitationsH (_ ::: uid ::: tid ::: start ::: size) = do listInvitations :: UserId -> TeamId -> Maybe InvitationId -> Range 1 500 Int32 -> (Handler r) Public.InvitationList listInvitations uid tid start size = do ensurePermissions uid tid [AddTeamMember] - showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid + showInvitationUrl <- lift $ wrapHttp $ Intra.getTeamExposeInvitationURLsToTeamAdmin tid rs <- lift $ wrapClient $ DB.lookupInvitations showInvitationUrl tid start size pure $! Public.InvitationList (DB.resultList rs) (DB.resultHasMore rs) @@ -430,7 +429,7 @@ getInvitationH (_ ::: uid ::: tid ::: iid) = do getInvitation :: UserId -> TeamId -> InvitationId -> (Handler r) (Maybe Public.Invitation) getInvitation uid tid iid = do ensurePermissions uid tid [AddTeamMember] - showInvitationUrl <- lift $ wrapHttp $ getTeamExposeInvitationURLsToTeamAdmin tid + showInvitationUrl <- lift $ wrapHttp $ Intra.getTeamExposeInvitationURLsToTeamAdmin tid lift $ wrapClient $ DB.lookupInvitation showInvitationUrl tid iid getInvitationByCodeH :: JSON ::: Public.InvitationCode -> (Handler r) Response diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index 8fc7a8fb92..a92f47b696 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -32,7 +32,6 @@ module Brig.Team.DB lookupInvitationByEmail, mkInvitationCode, mkInvitationId, - getTeamExposeInvitationURLsToTeamAdmin, InvitationInfo (..), InvitationByEmail (..), ) @@ -41,7 +40,6 @@ where import Brig.App as App import Brig.Data.Instances () import Brig.Data.Types as T -import Brig.IO.Intra import Brig.Options import Brig.Team.Template import Brig.Team.Types (ShowOrHideInvitationUrl (..)) From 1e69fd0f6a6730b2a2f0638a8f54fa4e79465918 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 22:37:09 +0200 Subject: [PATCH 63/66] hlint --- .../Golden/Generated/InvitationList_team.hs | 300 +++++++++--------- .../API/Golden/Generated/Invitation_team.hs | 122 +++---- .../brig/test/integration/API/User/Account.hs | 43 ++- .../test/integration/API/Teams/Feature.hs | 3 +- 4 files changed, 232 insertions(+), 236 deletions(-) diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs index ee7623fa21..2b94790051 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/InvitationList_team.hs @@ -52,10 +52,10 @@ testObject_InvitationList_team_2 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T09:28:36.729Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T09:28:36.729Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "\153442", emailDomain = "w"}, inInviteeName = @@ -80,10 +80,10 @@ testObject_InvitationList_team_4 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T19:46:50.121Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T19:46:50.121Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -97,10 +97,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T09:00:02.901Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T09:00:02.901Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -114,10 +114,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T11:10:31.203Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T11:10:31.203Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -131,10 +131,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T23:41:34.529Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T23:41:34.529Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -148,10 +148,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T00:29:17.658Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T00:29:17.658Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -159,10 +159,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T13:34:37.117Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T13:34:37.117Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -176,10 +176,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T18:05:30.889Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T18:05:30.889Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -193,10 +193,10 @@ testObject_InvitationList_team_4 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T15:21:05.519Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T15:21:05.519Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -221,10 +221,10 @@ testObject_InvitationList_team_6 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T06:42:29.677Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T06:42:29.677Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -232,10 +232,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T11:26:36.672Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T11:26:36.672Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -243,10 +243,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T00:31:56.241Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T00:31:56.241Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -254,10 +254,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T21:10:47.237Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T21:10:47.237Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -271,10 +271,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T15:43:22.250Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T15:43:22.250Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -282,10 +282,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T20:44:34.056Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T20:44:34.056Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Just (Name {fromName = "\1100765v\191022UcU+_\23043!?e Pr\40620=x-z5N\1059506"}), @@ -293,10 +293,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T11:23:55.061Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T11:23:55.061Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -304,10 +304,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T10:06:43.943Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T10:06:43.943Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -315,10 +315,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T19:42:31.295Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T19:42:31.295Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -332,10 +332,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T06:58:18.517Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T06:58:18.517Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -343,10 +343,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T00:40:39.103Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T00:40:39.103Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Just (Name {fromName = "')\28977mD\71122?\v\"Q&_8\DC4a"}), @@ -354,10 +354,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T21:44:30.848Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T21:44:30.848Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -365,10 +365,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T14:27:46.655Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T14:27:46.655Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -382,10 +382,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T03:57:53.185Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T03:57:53.185Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Just (Name {fromName = "\EM\1085994\5162\&29\93808\GS\n\RSzC`"}), @@ -393,10 +393,10 @@ testObject_InvitationList_team_6 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T14:35:39.474Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T14:35:39.474Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -412,10 +412,10 @@ testObject_InvitationList_team_7 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T14:44:40.049Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T14:44:40.049Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -429,10 +429,10 @@ testObject_InvitationList_team_7 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T19:09:35.565Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T19:09:35.565Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -446,10 +446,10 @@ testObject_InvitationList_team_7 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T11:05:26.660Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T11:05:26.660Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -471,10 +471,10 @@ testObject_InvitationList_team_8 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T13:24:44.890Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T13:24:44.890Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -483,10 +483,10 @@ testObject_InvitationList_team_8 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T15:37:31.278Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T15:37:31.278Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -508,10 +508,10 @@ testObject_InvitationList_team_9 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T12:45:57.694Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T12:45:57.694Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -519,10 +519,10 @@ testObject_InvitationList_team_9 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T08:06:09.682Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T08:06:09.682Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -536,10 +536,10 @@ testObject_InvitationList_team_9 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T01:04:27.531Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T01:04:27.531Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -561,10 +561,10 @@ testObject_InvitationList_team_10 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T17:28:36.896Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T17:28:36.896Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "}", emailDomain = ""}, inInviteeName = @@ -585,10 +585,10 @@ testObject_InvitationList_team_11 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T01:33:08.374Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T01:33:08.374Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "Z"}, inInviteeName = @@ -613,10 +613,10 @@ testObject_InvitationList_team_13 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T04:37:12.563Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T04:37:12.563Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -624,10 +624,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T05:36:38.967Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T05:36:38.967Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -641,10 +641,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T17:31:07.346Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T17:31:07.346Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -658,10 +658,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T17:18:26.847Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T17:18:26.847Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -675,10 +675,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T12:43:17.559Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T12:43:17.559Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -692,10 +692,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T14:24:17.699Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T14:24:17.699Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -704,10 +704,10 @@ testObject_InvitationList_team_13 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T16:30:09.682Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T16:30:09.682Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -732,10 +732,10 @@ testObject_InvitationList_team_15 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T15:54:11.332Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T15:54:11.332Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -749,10 +749,10 @@ testObject_InvitationList_team_15 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T23:06:13.648Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T23:06:13.648Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -761,10 +761,10 @@ testObject_InvitationList_team_15 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T10:37:03.809Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T10:37:03.809Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -778,10 +778,10 @@ testObject_InvitationList_team_15 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T04:46:03.504Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T04:46:03.504Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = @@ -795,10 +795,10 @@ testObject_InvitationList_team_15 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T12:53:52.047Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T12:53:52.047Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -814,10 +814,10 @@ testObject_InvitationList_team_16 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T15:25:30.297Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T15:25:30.297Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "\SI", emailDomain = ""}, inInviteeName = @@ -839,10 +839,10 @@ testObject_InvitationList_team_17 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T10:54:19.942Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T10:54:19.942Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "&", emailDomain = "\179430"}, inInviteeName = Nothing, @@ -864,10 +864,10 @@ testObject_InvitationList_team_20 = InvitationList { ilInvitations = [ Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T07:22:02.426Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T07:22:02.426Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = Nothing, @@ -875,10 +875,10 @@ testObject_InvitationList_team_20 = inInviteeUrl = Nothing }, Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T18:56:29.712Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T18:56:29.712Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "", emailDomain = ""}, inInviteeName = diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs index 0ccfbf4069..3bdaed9dae 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE OverloadedLists #-} + -- This file is part of the Wire Server implementation. -- @@ -31,10 +31,10 @@ import Wire.API.User.Profile (Name (Name, fromName)) testObject_Invitation_team_1 :: Invitation testObject_Invitation_team_1 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000002"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000002")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-11T20:13:15.856Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-11T20:13:15.856Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "\FS\58114Y", emailDomain = "7"}, inInviteeName = Nothing, @@ -45,10 +45,10 @@ testObject_Invitation_team_1 = testObject_Invitation_team_2 :: Invitation testObject_Invitation_team_2 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-12T14:47:35.551Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-12T14:47:35.551Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000001"))), inInviteeEmail = Email {emailLocal = "i", emailDomain = "m_:"}, inInviteeName = Just (Name {fromName = "\1067847} 2pGEW+\rT\171609p\174643\157218&\146145v0\b"}), @@ -59,10 +59,10 @@ testObject_Invitation_team_2 = testObject_Invitation_team_3 :: Invitation testObject_Invitation_team_3 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T22:07:35.846Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T22:07:35.846Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000200000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "\31189L"}, inInviteeName = Nothing, @@ -73,10 +73,10 @@ testObject_Invitation_team_3 = testObject_Invitation_team_4 :: Invitation testObject_Invitation_team_4 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T09:23:58.270Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T09:23:58.270Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000001"))), inInviteeEmail = Email {emailLocal = "^", emailDomain = "e"}, inInviteeName = Nothing, @@ -87,10 +87,10 @@ testObject_Invitation_team_4 = testObject_Invitation_team_5 :: Invitation testObject_Invitation_team_5 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000000000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000000000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T03:42:15.266Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000000000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T03:42:15.266Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "\SOHV", emailDomain = "f\1086249\43462"}, inInviteeName = @@ -107,10 +107,10 @@ testObject_Invitation_team_5 = testObject_Invitation_team_6 :: Invitation testObject_Invitation_team_6 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000100000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T08:56:40.919Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000100000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T08:56:40.919Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000200000000"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "OC"}, inInviteeName = @@ -127,10 +127,10 @@ testObject_Invitation_team_6 = testObject_Invitation_team_7 :: Invitation testObject_Invitation_team_7 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000200000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000200000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-07T18:46:22.786Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-07T18:46:22.786Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "oj", emailDomain = ""}, inInviteeName = @@ -147,10 +147,10 @@ testObject_Invitation_team_7 = testObject_Invitation_team_8 :: Invitation testObject_Invitation_team_8 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000001")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000200000000"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-07T12:20:00.738Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000200000000")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-07T12:20:00.738Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "Y\fr"}, inInviteeName = Just (Name {fromName = "\67592\154970\1102305lE\990376\SYN\rjI!@\RS\1094043"}), @@ -161,10 +161,10 @@ testObject_Invitation_team_8 = testObject_Invitation_team_9 :: Invitation testObject_Invitation_team_9 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000200000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000200000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-06T13:15:34.606Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-06T13:15:34.606Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000002"))), inInviteeEmail = Email {emailLocal = "Qi\183990", emailDomain = "\144719"}, inInviteeName = Nothing, @@ -175,10 +175,10 @@ testObject_Invitation_team_9 = testObject_Invitation_team_10 :: Invitation testObject_Invitation_team_10 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000001")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000200000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-10T19:57:59.926Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000200000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-10T19:57:59.926Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000000000002"))), inInviteeEmail = Email {emailLocal = "\SOH", emailDomain = "\SUB"}, inInviteeName = Nothing, @@ -189,10 +189,10 @@ testObject_Invitation_team_10 = testObject_Invitation_team_11 :: Invitation testObject_Invitation_team_11 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-10T16:20:51.120Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-10T16:20:51.120Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000100000000"))), inInviteeEmail = Email {emailLocal = "6", emailDomain = "\1101264N"}, inInviteeName = @@ -209,10 +209,10 @@ testObject_Invitation_team_11 = testObject_Invitation_team_12 :: Invitation testObject_Invitation_team_12 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-12T22:47:35.829Z")), + inInvitation = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-12T22:47:35.829Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0002-0000-000000000000"))), inInviteeEmail = Email {emailLocal = "\1016862\141073\RS", emailDomain = ""}, inInviteeName = @@ -229,10 +229,10 @@ testObject_Invitation_team_12 = testObject_Invitation_team_13 :: Invitation testObject_Invitation_team_13 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000001")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000200000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T01:18:31.982Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000200000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T01:18:31.982Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000100000002"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "\DELr"}, inInviteeName = Just (Name {fromName = "U"}), @@ -243,10 +243,10 @@ testObject_Invitation_team_13 = testObject_Invitation_team_14 :: Invitation testObject_Invitation_team_14 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0002-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0002-0000-000100000000")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000200000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-12T23:54:25.090Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000200000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-12T23:54:25.090Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0002-0000-000200000000"))), inInviteeEmail = Email {emailLocal = "EI", emailDomain = "{"}, inInviteeName = Nothing, @@ -257,10 +257,10 @@ testObject_Invitation_team_14 = testObject_Invitation_team_15 :: Invitation testObject_Invitation_team_15 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000100000001"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000100000001")), inRole = RoleOwner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000200000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T22:22:28.568Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000200000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T22:22:28.568Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = ".", emailDomain = "\DEL"}, inInviteeName = @@ -277,10 +277,10 @@ testObject_Invitation_team_15 = testObject_Invitation_team_16 :: Invitation testObject_Invitation_team_16 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000002"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000002")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000200000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-09T09:56:33.113Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000200000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-09T09:56:33.113Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "\\", emailDomain = "\"\DEL{"}, inInviteeName = Just (Name {fromName = "\GS\DC4Q;6/_f*7\1093966\SI+\1092810\41698\&9"}), @@ -291,10 +291,10 @@ testObject_Invitation_team_16 = testObject_Invitation_team_17 :: Invitation testObject_Invitation_team_17 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000002"))), + { inTeam = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000002")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T06:30:23.239Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T06:30:23.239Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000000000001"))), inInviteeEmail = Email {emailLocal = "", emailDomain = "\SOH[\97119"}, inInviteeName = @@ -311,10 +311,10 @@ testObject_Invitation_team_17 = testObject_Invitation_team_18 :: Invitation testObject_Invitation_team_18 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000000")), inRole = RoleAdmin, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-08T06:07:59.528Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000002")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-08T06:07:59.528Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "\SOH", emailDomain = "l\42676a"}, inInviteeName = @@ -331,10 +331,10 @@ testObject_Invitation_team_18 = testObject_Invitation_team_19 :: Invitation testObject_Invitation_team_19 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000200000000")), inRole = RoleMember, - inInvitation = (Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-07T15:08:06.796Z")), + inInvitation = Id (fromJust (UUID.fromString "00000001-0000-0002-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-07T15:08:06.796Z"), inCreatedBy = Nothing, inInviteeEmail = Email {emailLocal = "\1019726\96050\DEL", emailDomain = "(S\ETB"}, inInviteeName = @@ -351,10 +351,10 @@ testObject_Invitation_team_19 = testObject_Invitation_team_20 :: Invitation testObject_Invitation_team_20 = Invitation - { inTeam = (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))), + { inTeam = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000")), inRole = RoleExternalPartner, - inInvitation = (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000001"))), - inCreatedAt = (fromJust (readUTCTimeMillis "1864-05-12T08:07:17.747Z")), + inInvitation = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000000000001")), + inCreatedAt = fromJust (readUTCTimeMillis "1864-05-12T08:07:17.747Z"), inCreatedBy = Just (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))), inInviteeEmail = Email {emailLocal = "b", emailDomain = "u9T"}, inInviteeName = Nothing, diff --git a/services/brig/test/integration/API/User/Account.hs b/services/brig/test/integration/API/User/Account.hs index bc55d510e9..d34cb878fe 100644 --- a/services/brig/test/integration/API/User/Account.hs +++ b/services/brig/test/integration/API/User/Account.hs @@ -1611,29 +1611,26 @@ testTooManyMembersForLegalhold opts brig = do responseJsonError =<< postInvitation brig tid owner invite - opts + let classifiedDomainsDisabled opts = opts & over (optSettings . setFeatureFlags . flagClassifiedDomains) (\(ImplicitLockStatus s) -> ImplicitLockStatus (s & Public.setStatus Public.FeatureStatusDisabled & Public.setConfig (Public.ClassifiedDomainsConfig []))) From 53e4106f4796d4d5c7ee9536352f6d8a7e143cca Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 16 Sep 2022 22:39:13 +0200 Subject: [PATCH 64/66] ormolu --- .../golden/Test/Wire/API/Golden/Generated/Invitation_team.hs | 2 -- services/brig/test/integration/API/User/Account.hs | 2 +- services/galley/test/integration/API/Teams/Feature.hs | 3 ++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs index 3bdaed9dae..02c5a04e6c 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/Invitation_team.hs @@ -1,5 +1,3 @@ - - -- This file is part of the Wire Server implementation. -- -- Copyright (C) 2022 Wire Swiss GmbH diff --git a/services/brig/test/integration/API/User/Account.hs b/services/brig/test/integration/API/User/Account.hs index d34cb878fe..eb579b4438 100644 --- a/services/brig/test/integration/API/User/Account.hs +++ b/services/brig/test/integration/API/User/Account.hs @@ -1621,7 +1621,7 @@ testTooManyMembersForLegalhold opts brig = do "cannot add more members to team when legalhold service is enabled." ) | mth == "GET" - && pth == ["i", "teams", Text.pack (show tid), "features", "exposeInvitationURLsToTeamAdmin"] = + && pth == ["i", "teams", Text.pack (show tid), "features", "exposeInvitationURLsToTeamAdmin"] = pure . Wai.responseLBS HTTP.status200 mempty $ encode ( withStatus diff --git a/services/galley/test/integration/API/Teams/Feature.hs b/services/galley/test/integration/API/Teams/Feature.hs index ee4205d3a5..365584c46f 100644 --- a/services/galley/test/integration/API/Teams/Feature.hs +++ b/services/galley/test/integration/API/Teams/Feature.hs @@ -450,7 +450,8 @@ testClassifiedDomainsDisabled = do liftIO $ Public.wsStatus result @?= Public.wssStatus expected' liftIO $ Public.wsConfig result @?= Public.wssConfig expected' - let classifiedDomainsDisabled opts = opts + let classifiedDomainsDisabled opts = + opts & over (optSettings . setFeatureFlags . flagClassifiedDomains) (\(ImplicitLockStatus s) -> ImplicitLockStatus (s & Public.setStatus Public.FeatureStatusDisabled & Public.setConfig (Public.ClassifiedDomainsConfig []))) From d03ad32536ae44563c942ff9adacbfb018d42c41 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Sun, 18 Sep 2022 19:17:56 +0200 Subject: [PATCH 65/66] Simplify expression Co-authored-by: fisx --- services/brig/src/Brig/Team/DB.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index a92f47b696..b5296b012f 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -315,8 +315,8 @@ mkInviteUrl ShowInvitationUrl team (InvitationCode c) = do parseHttpsUrl :: Log.MonadLogger m => Text -> m (Maybe (URIRef Absolute)) parseHttpsUrl url = - either (\e -> logError url e >> pure Nothing) (\s' -> pure $ Just s') $ - first show $ parseURI laxURIParserOptions (encodeUtf8 url) + either (\e -> logError url e >> pure Nothing) (pure . Just) $ + parseURI laxURIParserOptions (encodeUtf8 url) logError :: (Log.MonadLogger m, Show e) => Text -> e -> m () logError url e = From dc543a9332d1beaddde6acb77b5eb1485aa7d067 Mon Sep 17 00:00:00 2001 From: Sven Tennie Date: Mon, 19 Sep 2022 07:50:59 +0200 Subject: [PATCH 66/66] Remove unused import --- services/brig/src/Brig/Team/DB.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/services/brig/src/Brig/Team/DB.hs b/services/brig/src/Brig/Team/DB.hs index b5296b012f..e52a511372 100644 --- a/services/brig/src/Brig/Team/DB.hs +++ b/services/brig/src/Brig/Team/DB.hs @@ -46,7 +46,6 @@ import Brig.Team.Types (ShowOrHideInvitationUrl (..)) import Brig.Template (renderTextWithBranding) import Cassandra as C import Control.Lens (view) -import Data.Bifunctor import Data.Conduit (runConduit, (.|)) import qualified Data.Conduit.List as C import Data.Id