Skip to content

Commit

Permalink
Merge branch 'wireapp:develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
offsoc authored Nov 12, 2024
2 parents 9ffe6bd + 368b046 commit 80a296a
Show file tree
Hide file tree
Showing 31 changed files with 152 additions and 45 deletions.
1 change: 1 addition & 0 deletions changelog.d/1-api-changes/invitation
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The endpoint `POST /teams/:tid/invitations` gained a new optional field `allow_existing`, which controls whether an existing personal user should should be invited to the team
20 changes: 20 additions & 0 deletions integration/test/Test/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,26 @@ testInvitePersonalUserToTeamMultipleInvitations = do
resp.json %. "team" `shouldMatch` tid
acceptTeamInvitation user code (Just defPassword) >>= assertStatus 400

testInvitePersonalUserToTeamLegacy :: (HasCallStack) => App ()
testInvitePersonalUserToTeamLegacy = withAPIVersion 6 $ do
(owner, tid, _) <- createTeam OwnDomain 0
user <- I.createUser OwnDomain def >>= getJSON 201

-- inviting an existing user should fail
do
email <- user %. "email" >>= asString
bindResponse (postInvitation owner (PostInvitation (Just email) Nothing)) $ \resp -> do
resp.status `shouldMatchInt` 409
resp.json %. "label" `shouldMatch` "email-exists"

-- inviting a new user should succeed
do
email <- randomEmail
bindResponse (postInvitation owner (PostInvitation (Just email) Nothing)) $ \resp -> do
resp.status `shouldMatchInt` 201
resp.json %. "email" `shouldMatch` email
resp.json %. "team" `shouldMatch` tid

testInvitationTypesAreDistinct :: (HasCallStack) => App ()
testInvitationTypesAreDistinct = do
-- We are only testing one direction because the other is not possible
Expand Down
33 changes: 31 additions & 2 deletions libs/wire-api/src/Wire/API/Routes/Public/Brig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1546,8 +1546,9 @@ type CallingAPI =

type TeamsAPI =
Named
"send-team-invitation"
"send-team-invitation@v6"
( Summary "Create and send a new team invitation."
:> Until V7
:> Description
"Invitations are sent by email. The maximum allowed number of \
\pending team invitations is equal to the team size."
Expand All @@ -1562,7 +1563,7 @@ type TeamsAPI =
:> "teams"
:> Capture "tid" TeamId
:> "invitations"
:> ReqBody '[JSON] InvitationRequest
:> VersionedReqBody V6 '[JSON] InvitationRequest
:> MultiVerb1
'POST
'[JSON]
Expand All @@ -1572,6 +1573,34 @@ type TeamsAPI =
(Respond 201 "Invitation was created and sent." Invitation)
)
)
:<|> Named
"send-team-invitation"
( Summary "Create and send a new team invitation."
:> From V7
:> Description
"Invitations are sent by email. The maximum allowed number of \
\pending team invitations is equal to the team size."
:> CanThrow 'NoEmail
:> CanThrow 'NoIdentity
:> CanThrow 'InvalidEmail
:> CanThrow 'BlacklistedEmail
:> CanThrow 'TooManyTeamInvitations
:> CanThrow 'InsufficientTeamPermissions
:> CanThrow 'InvalidInvitationCode
:> ZLocalUser
:> "teams"
:> Capture "tid" TeamId
:> "invitations"
:> ReqBody '[JSON] InvitationRequest
:> MultiVerb1
'POST
'[JSON]
( WithHeaders
'[Header "Location" InvitationLocation]
(Invitation, InvitationLocation)
(Respond 201 "Invitation was created and sent." Invitation)
)
)
:<|> Named
"get-team-invitations"
( Summary "List the sent team invitations"
Expand Down
37 changes: 25 additions & 12 deletions libs/wire-api/src/Wire/API/Team/Invitation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import URI.ByteString
import Wire.API.Error
import Wire.API.Error.Brig
import Wire.API.Routes.MultiVerb
import Wire.API.Routes.Version
import Wire.API.Routes.Versioned
import Wire.API.Team.Role (Role, defaultRole)
import Wire.API.User
import Wire.Arbitrary (Arbitrary, GenericUniform (..))
Expand All @@ -57,24 +59,35 @@ data InvitationRequest = InvitationRequest
{ locale :: Maybe Locale,
role :: Maybe Role,
inviteeName :: Maybe Name,
inviteeEmail :: EmailAddress
inviteeEmail :: EmailAddress,
allowExisting :: Bool
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform InvitationRequest)
deriving (A.FromJSON, A.ToJSON, S.ToSchema) via (Schema InvitationRequest)

instance ToSchema (Versioned V6 InvitationRequest) where
schema = Versioned <$> unVersioned .= invitationRequestSchema False

instance ToSchema InvitationRequest where
schema =
objectWithDocModifier "InvitationRequest" (description ?~ "A request to join a team on Wire.") $
InvitationRequest
<$> locale
.= optFieldWithDocModifier "locale" (description ?~ "Locale to use for the invitation.") (maybeWithDefault A.Null schema)
<*> (.role)
.= optFieldWithDocModifier "role" (description ?~ "Role of the invitee (invited user).") (maybeWithDefault A.Null schema)
<*> (.inviteeName)
.= optFieldWithDocModifier "name" (description ?~ "Name of the invitee (1 - 128 characters).") (maybeWithDefault A.Null schema)
<*> (.inviteeEmail)
.= fieldWithDocModifier "email" (description ?~ "Email of the invitee.") schema
schema = invitationRequestSchema True

invitationRequestSchema :: Bool -> ValueSchema NamedSwaggerDoc InvitationRequest
invitationRequestSchema allowExisting =
objectWithDocModifier "InvitationRequest" (description ?~ "A request to join a team on Wire.") $
InvitationRequest
<$> locale
.= optFieldWithDocModifier "locale" (description ?~ "Locale to use for the invitation.") (maybeWithDefault A.Null schema)
<*> (.role)
.= optFieldWithDocModifier "role" (description ?~ "Role of the invitee (invited user).") (maybeWithDefault A.Null schema)
<*> (.inviteeName)
.= optFieldWithDocModifier "name" (description ?~ "Name of the invitee (1 - 128 characters).") (maybeWithDefault A.Null schema)
<*> (.inviteeEmail)
.= fieldWithDocModifier "email" (description ?~ "Email of the invitee.") schema
<*> (.allowExisting)
.= ( fromMaybe allowExisting
<$> optFieldWithDocModifier "allow_existing" (description ?~ "Whether invitations to existing users are allowed.") schema
)

--------------------------------------------------------------------------------
-- Invitation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module Test.Wire.API.Golden.Generated.InvitationRequest_team where

import Data.ISO3166_CountryCodes (CountryCode (BJ, FJ, GH, LB, ME, NL, OM, PA, TC, TZ))
import Data.LanguageCodes qualified (ISO639_1 (AF, AR, DA, DV, KJ, KS, KU, LG, NN, NY, OM, SI))
import Imports (Maybe (Just, Nothing))
import Imports
import Wire.API.Locale
import Wire.API.Team.Invitation (InvitationRequest (..))
import Wire.API.Team.Role (Role (RoleAdmin, RoleExternalPartner, RoleMember, RoleOwner))
Expand All @@ -32,7 +32,8 @@ testObject_InvitationRequest_team_1 =
{ locale = Just (Locale {lLanguage = Language Data.LanguageCodes.NN, lCountry = Nothing}),
role = Just RoleOwner,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_2 :: InvitationRequest
Expand All @@ -42,7 +43,8 @@ testObject_InvitationRequest_team_2 =
Just (Locale {lLanguage = Language Data.LanguageCodes.AF, lCountry = Just (Country {fromCountry = GH})}),
role = Nothing,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_3 :: InvitationRequest
Expand All @@ -58,7 +60,8 @@ testObject_InvitationRequest_team_3 =
"\27175\1085444\v\182035\144967G\189107\1042607\ETX\180573\1047918\ETX\1075522ZG\1087064\STX+i\46576Ux\FS\FS5\ESC\ae\10301\36223(3\1009347\\\t\EOT\v@\ENQs\r#R\136368G'N^?\NAKB\f\FS\NULx\1024041@\34031\1105463\1058551`A]@\34846\133788*\1025332N;\ETX\FSh\bS\US\US\SO`^qU<\21803\SYN\1094791\ETX\1112073M\SI\1019355\4619=zM[\181520\161190\n\SI}\ENQ\1008012\aaZI\18628\ACKE#G^t\148685\DLE\157774LY\182624\&6vt\\"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_4 :: InvitationRequest
Expand All @@ -67,7 +70,8 @@ testObject_InvitationRequest_team_4 =
{ locale = Nothing,
role = Just RoleMember,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_5 :: InvitationRequest
Expand All @@ -76,7 +80,8 @@ testObject_InvitationRequest_team_5 =
{ locale = Nothing,
role = Just RoleAdmin,
inviteeName = Just (Name {fromName = "\171800\1076860\1103443\CAN8=\n;}\169054M\ao\v3+\n"}),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_6 :: InvitationRequest
Expand All @@ -92,7 +97,8 @@ testObject_InvitationRequest_team_6 =
"\RSD[alw\RS\ACKP \999760\rO\175510'8\989959\1082925g W:8\v:-(`+\131521\ESC_\CAN\1105214\44926(\"&\DC2NZ\1082341\ACKS\SYNLOW|p\EM\194645\&1\175388"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_7 :: InvitationRequest
Expand All @@ -101,7 +107,8 @@ testObject_InvitationRequest_team_7 =
{ locale = Nothing,
role = Just RoleAdmin,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_8 :: InvitationRequest
Expand All @@ -112,7 +119,8 @@ testObject_InvitationRequest_team_8 =
role = Nothing,
inviteeName =
Just (Name {fromName = "\1036838&f\1104978\1021739j5\CANv]k\1034960\993099c[\1019257\1047325\EOTw.uL~/"}),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_9 :: InvitationRequest
Expand All @@ -123,7 +131,8 @@ testObject_InvitationRequest_team_9 =
role = Just RoleAdmin,
inviteeName =
Just (Name {fromName = "|H\181717/%\RSu\1019619\&7V\142010\62451*G\SOHE\993531,\1015423WGtY\SYN*Nd\156695{Pl"}),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_10 :: InvitationRequest
Expand All @@ -139,7 +148,8 @@ testObject_InvitationRequest_team_10 =
"H\1008404\RS\45861\92335uv\1045159\DC2\1045852\SUB \160164=a\ESC4H,B\CAN\1039540GpV0\1044935;_\NUL\173370Z\DC1\28376\NAK6\32784'W9z\11986\t\59610r\150374\1057016\SYN_ge\35917\EOTD\94732o\an>\993583"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_11 :: InvitationRequest
Expand All @@ -154,7 +164,8 @@ testObject_InvitationRequest_team_11 =
"\167004\41433\11577\74832h_5bb2}\46841\166935P\NUL\SOT*\US`b\170964\SI:4\n5\SUB\GS*T\1016149Bv\ESC\ETX\GS\1050773\175887Uu\r_\DLE)y\153990\EOT\b\US\DC4\FS\CAN?\1050027\149716\22398\NAK\SUB4\v 5\NULi\43113o=\tnG\37464\ETBiC\DC39\SOP\1026840\n\v\EM\SYNU\7800%\49334\DC2\USF\FS"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}

testObject_InvitationRequest_team_12 :: InvitationRequest
Expand All @@ -170,7 +181,8 @@ testObject_InvitationRequest_team_12 =
"_\EM@\GS0\52658\1041209\1014911\FS\DLE\1100406!\1081838\SOc\US\NUL\SOH>\1074611\168456\EM\175538\&1}!h0\DLE\1053201w\EOT\1073681\&1aJ6c\GS\986890b\131925{\996638\131443\a\1094281"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_13 :: InvitationRequest
Expand All @@ -185,7 +197,8 @@ testObject_InvitationRequest_team_13 =
"C\990664+\1033671\n#s\1072813\FSpb\SOH\1015233\1073302\&1\ETBE_\CANj\EMV\US\1063126\15431\1099470lO8\ACK\1056562\FS\SYN\CAN\DLE6\137862-beR!s\48584\ETB\v\1049375\984016xt\SIRf~w\1030329\DEL+_\70046\&91:,\1034030#cf\1056279\3624\2548\6959B\"\1097722F\t\1109914\1069782/\DEL\DLE'\1004715*\171262\&7\156200w\1061410H\59715x\DC32\EMt\163668o6\DC4F%=t\1003324\1097336=\NUL\ENQA\1101771\1011923\NUL\EOT[i\992519@\b\FS\f"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_14 :: InvitationRequest
Expand All @@ -195,7 +208,8 @@ testObject_InvitationRequest_team_14 =
Just (Locale {lLanguage = Language Data.LanguageCodes.DV, lCountry = Just (Country {fromCountry = LB})}),
role = Just RoleAdmin,
inviteeName = Just (Name {fromName = "\NAKwGn\996611\149528\&1}\EOTgY.>=}"}),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_15 :: InvitationRequest
Expand All @@ -210,7 +224,8 @@ testObject_InvitationRequest_team_15 =
"y\1104714\&5\1000317\710S\1019005\DC4\rH/_\DC3A\ETX\119343\&0w\GS?TQd*1&[?cHW}\21482\1021206\CAN\180566Q+\ETXmh\995371X\SO\ENQ\DC1^g\144398\bqrNV\SO\1095058WMe\a\ENQ"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_16 :: InvitationRequest
Expand All @@ -220,7 +235,8 @@ testObject_InvitationRequest_team_16 =
Just (Locale {lLanguage = Language Data.LanguageCodes.OM, lCountry = Just (Country {fromCountry = BJ})}),
role = Just RoleAdmin,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_17 :: InvitationRequest
Expand All @@ -230,7 +246,8 @@ testObject_InvitationRequest_team_17 =
Just (Locale {lLanguage = Language Data.LanguageCodes.KJ, lCountry = Just (Country {fromCountry = TC})}),
role = Just RoleExternalPartner,
inviteeName = Nothing,
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_18 :: InvitationRequest
Expand All @@ -245,7 +262,8 @@ testObject_InvitationRequest_team_18 =
"8VPAp\137681\&2L<s\ACKt]\1051893\1028831G/\SIQb\1099332<\62973B\DC3\995191kJ&\1028424\DLE\a \66433\SO\987741\1099076$\99376\"u2g\ENQ[<.N;%\EMsm\43781*\1030957s\184809DsCowW-\1069896&EF=\\H\NAK,Z\rJ\ETBw-\STX\ahC`\1077061\52563\&1Ds^7Udh+e\fL Ld\ESCh&\1000121\1102718\1028691;\142313\a\985672Xp\26072\SOP\b\t\187311\1063310.\DEL\RSp"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_19 :: InvitationRequest
Expand All @@ -260,7 +278,8 @@ testObject_InvitationRequest_team_19 =
"kl\ETX\EOT\SYN%s7\1031959fX\994905A\b7\DC1\DELD\EOT\DC1\165155s\DELg)dD\157274Rx[\1026892Tw\68117\RS\SUB\1049684z\\\SI\ENQ\17054l\1089470l|oKc\\(\187173\1101164=\33052\&2VI*\1095067\&2oTh&#+;o\5017dXA\12103=*\1074686Q\1032360{\994965\917585\&5}\GS9D\186360\1064921r\1080854P:<!|\1002411\v4Pt1\983861g\b\STX\152876\rfY\135334$\DEL_\54841\"\1035381\&8"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = True
}

testObject_InvitationRequest_team_20 :: InvitationRequest
Expand All @@ -275,5 +294,6 @@ testObject_InvitationRequest_team_20 =
"N\1014949\3115qE\1086743,\1069753\1076493\&3-19bY\"Iz|BpQ\1112885\"\ACKdfC\1095189p\SO\1038198%-Z\SUB\1082854!Z\156657d\va\174302\ESC\b\ESCg\DELb\b\1009771\995646X}\STX\\^\1091690\&9\58052\1113953"
}
),
inviteeEmail = unsafeEmailAddress "some" "example"
inviteeEmail = unsafeEmailAddress "some" "example",
allowExisting = False
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": "nn",
"name": null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": false,
"email": "some@example",
"locale": "ny-OM",
"name": "H󶌔\u001e댥𖢯uv󿊧\u0012󿕜\u001a 𧆤=a\u001b4H,B\u0018󽲴GpV0󿇇;_\u0000𪔺Z\u0011\u00156耐'W9z⻒\tr𤭦􂃸\u0016_ge豍\u0004D𗈌o\u0007n>󲤯",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": false,
"email": "some@example",
"locale": "si",
"name": "𨱜ꇙⴹ𒑐h_5bb2}뛹𨰗P\u0000\u000eT*\u001f`b𩯔\u000f:4\n5\u001a\u001d*T󸅕Bv\u001b\u0003\u001d􀢕𪼏Uu\r_\u0010)y𥦆\u0004\u0008\u001f\u0014\u001c\u0018?􀖫𤣔坾\u0015\u001a4\u000b 5\u0000iꡩo=\tnG鉘\u0017iC\u00139\u000eP󺬘\n\u000b\u0019\u0016UṸ%삶\u0012\u001fF\u001c",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": "ar-PA",
"name": "_\u0019@\u001d0춲󾌹󷱿\u001c\u0010􌩶!􈇮\u000ec\u001f\u0000\u0001>􆖳𩈈\u0019𪶲1}!h0\u0010􁈑w\u0004􆈑1aJ6c\u001d󰼊b𠍕{󳔞𠅳\u0007􋊉",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": null,
"name": "C󱷈+󼗇\n#s􅺭\u001cpb\u0001󷷁􆂖1\u0017E_\u0018j\u0019V\u001f􃣖㱇􌛎lO8\u0006􁼲\u001c\u0016\u0018\u00106𡪆-beR!s뷈\u0017\u000b􀌟󰏐xt\u000fRf~w󻢹+_𑆞91:,󼜮#cf􁸗ศ৴ᬯB\"􋿺F\t􎾚􅋖/\u0010'󵒫*𩳾7𦈨w􃈢Hx\u00132\u0019t𧽔o6\u0014F%=t󴼼􋹸=\u0000\u0005A􌿋󷃓\u0000\u0004[i󲔇@\u0008\u001c\u000c",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": "dv-LB",
"name": "\u0015wGn󳔃𤠘1}\u0004gY.>=}",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": null,
"name": "y􍭊5󴍽ˆS󸱽\u0014\rH/_\u0013A\u0003𝈯0w\u001d?TQd*1&[?cHW}只󹔖\u0018𬅖Q+\u0003mh󳀫X\u000e\u0005\u0011^g𣐎\u0008qrNV\u000e􋖒WMe\u0007\u0005",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"allow_existing": true,
"email": "some@example",
"locale": "om-BJ",
"name": null,
Expand Down
Loading

0 comments on commit 80a296a

Please sign in to comment.