Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Federation: Convert prekeys endpoints #1372

Merged
merged 31 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c00e318
wip
smatting Feb 9, 2021
651aacb
Make compile
smatting Feb 9, 2021
d8c5b5f
add routing
smatting Feb 9, 2021
094a576
Move duplicated instances from galley to wire-api
smatting Feb 17, 2021
f39354e
errors to exceptions
smatting Feb 18, 2021
eb8d273
Add (deprecated) to swagger docs
smatting Feb 18, 2021
2559cbb
clean up
smatting Feb 18, 2021
22bf1bd
nginz: Remove extraneous routing rules
smatting Feb 22, 2021
bd492e3
PrekeyId: use newtype ToSchema instance
smatting Feb 22, 2021
f424c3d
Remove extraneous routes from services-demo
smatting Feb 22, 2021
18fc922
galley(partial): refactor OpaqueUserId -> UserId
smatting Feb 22, 2021
44fd44a
Fix: Z-Auth mistakingly added to endpoints
smatting Feb 22, 2021
2bdf27e
keep modelOtrClientMap because used by NewOtrMessage
smatting Feb 22, 2021
ff8571f
Remove TODO
smatting Feb 22, 2021
c0b0bca
Remove TODO
smatting Feb 22, 2021
c10c51b
Remove unused modelPrekeyBundle
smatting Feb 22, 2021
8da75da
remove unused modelCientPrekey
smatting Feb 22, 2021
2f9df6a
hi ci
smatting Feb 22, 2021
03c1768
fix api-bot
smatting Feb 23, 2021
e0f075c
fix api-simulations
smatting Feb 23, 2021
1288598
Add test for /users/<localdomain>/:uid/prekeys
smatting Feb 23, 2021
c6dea20
Add test for /users/<localdomain>/:uid/prekeys/:client
smatting Feb 23, 2021
4ff518c
factor out helper generateClients
smatting Feb 23, 2021
e2c2812
Add test for old /users/prekey endpoint
smatting Feb 23, 2021
2a158a0
Add test for post /list-keys
smatting Feb 23, 2021
fcfa202
Add clarification TODOs
smatting Feb 23, 2021
b816ea0
Make swagger examples nice
smatting Feb 23, 2021
9d57293
Revert "Add clarification TODOs"
smatting Feb 23, 2021
45deea5
Move /list-prekeys to /users/list-prekeys
smatting Feb 23, 2021
c2ac6b4
hi ci
smatting Feb 23, 2021
ba42ab4
Apply suggestions from code review
smatting Mar 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions libs/api-bot/src/Network/Wire/Bot/Crypto.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ encrypt cl cnv val = fmap (OtrRecipients . UserClientMap)
ciphertext <- do
bs <- CBox.encrypt s val >>= unwrap >>= CBox.copyBytes
return $! decodeUtf8 $! B64.encode bs
let userId = makeIdOpaque u
return $ Map.insertWith Map.union userId (Map.singleton c ciphertext) rcps
return $ Map.insertWith Map.union u (Map.singleton c ciphertext) rcps

-- | Decrypt an OTR message received from a given user and client.
decrypt :: BotClient -> UserId -> ClientId -> ByteString -> BotSession ByteString
Expand Down
4 changes: 2 additions & 2 deletions libs/galley-types/src/Galley/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ module Galley.Types
where

import Data.Aeson
import Data.Id (ClientId, ConvId, OpaqueUserId, TeamId, UserId)
import Data.Id (ClientId, ConvId, TeamId, UserId)
import Data.Json.Util ((#))
import qualified Data.Map.Strict as Map
import Data.Misc (Milliseconds)
Expand Down Expand Up @@ -151,7 +151,7 @@ instance FromJSON Accept where
--------------------------------------------------------------------------------
-- utility functions

foldrOtrRecipients :: (OpaqueUserId -> ClientId -> Text -> a -> a) -> a -> OtrRecipients -> a
foldrOtrRecipients :: (UserId -> ClientId -> Text -> a -> a) -> a -> OtrRecipients -> a
foldrOtrRecipients f a =
Map.foldrWithKey go a
. userClientMap
Expand Down
5 changes: 5 additions & 0 deletions libs/types-common/src/Data/Domain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import Data.Attoparsec.ByteString ((<?>))
import qualified Data.Attoparsec.ByteString.Char8 as Atto
import Data.Bifunctor (Bifunctor (first))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as Builder
import qualified Data.ByteString.Char8 as BS.Char8
import Data.ByteString.Conversion
import Data.String.Conversions (cs)
import Data.Swagger (ToSchema (..))
import Data.Swagger.Internal.ParamSchema (ToParamSchema (..))
import qualified Data.Text as Text
Expand Down Expand Up @@ -71,6 +73,9 @@ mkDomain = Atto.parseOnly (domainParser <* Atto.endOfInput) . Text.E.encodeUtf8
instance FromByteString Domain where
parser = domainParser

instance ToByteString Domain where
builder = Builder.lazyByteString . cs @Text @LByteString . _domainText

instance FromHttpApiData Domain where
parseUrlPiece = first Text.pack . mkDomain

Expand Down
2 changes: 1 addition & 1 deletion libs/types-common/src/Data/Id.hs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ newtype ClientId = ClientId
{ client :: Text
}
deriving (Eq, Ord, Show, ToByteString, Hashable, NFData, ToJSON, ToJSONKey, Generic)
deriving newtype (ToSchema)
deriving newtype (ToSchema, ToParamSchema, FromHttpApiData)

newClientId :: Word64 -> ClientId
newClientId = ClientId . toStrict . toLazyText . hexadecimal
Expand Down
9 changes: 5 additions & 4 deletions libs/wire-api/src/Wire/API/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ data NewOtrMessage = NewOtrMessage
newOtrTransient :: Bool,
newOtrNativePriority :: Maybe Priority,
newOtrData :: Maybe Text,
newOtrReportMissing :: Maybe [OpaqueUserId]
newOtrReportMissing :: Maybe [UserId]
-- FUTUREWORK: if (and only if) clients can promise this uid list will always exactly
-- be the list of uids we could also extract from the messages' recipients field, we
-- should do the latter, for two reasons: (1) no need for an artificial limit on the
Expand Down Expand Up @@ -157,16 +157,17 @@ instance FromJSON Priority where
--------------------------------------------------------------------------------
-- Recipients

-- FUTUREWORK: Add ToSchema when 'NewOtrMessage' has ToSchema
newtype OtrRecipients = OtrRecipients
{ otrRecipientsMap :: UserClientMap Text
}
deriving stock (Eq, Show)
deriving newtype (ToJSON, FromJSON, Semigroup, Monoid, Arbitrary)

-- FUTUREWORK: Remove when 'NewOtrMessage' has ToSchema
modelOtrRecipients :: Doc.Model
modelOtrRecipients = Doc.defineModel "OtrRecipients" $ do
Doc.description "Recipients of OTR content."
-- FUTUREWORK: is this right?
Doc.property "" (Doc.ref modelOtrClientMap) $
Doc.description "Mapping of user IDs to 'OtrClientMap's."

Expand All @@ -182,10 +183,10 @@ data OtrFilterMissing
OtrReportAllMissing
| -- | Complain only about missing
-- recipients who are /not/ on this list
OtrIgnoreMissing (Set OpaqueUserId)
OtrIgnoreMissing (Set UserId)
| -- | Complain only about missing
-- recipients who /are/ on this list
OtrReportMissing (Set OpaqueUserId)
OtrReportMissing (Set UserId)
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform OtrFilterMissing)

Expand Down
9 changes: 5 additions & 4 deletions libs/wire-api/src/Wire/API/Message/Proto.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,18 @@ import qualified Wire.API.User.Client as Client
-- UserId

newtype UserId = UserId
{ _user :: Required 1 (Value Id.OpaqueUserId)
{ _user :: Required 1 (Value Id.UserId)
}
deriving stock (Eq, Show, Generic)

instance Encode UserId

instance Decode UserId

fromUserId :: Id.OpaqueUserId -> UserId
fromUserId :: Id.UserId -> UserId
fromUserId u = UserId {_user = putField u}

userId :: Functor f => (Id.OpaqueUserId -> f Id.OpaqueUserId) -> UserId -> f UserId
userId :: Functor f => (Id.UserId -> f Id.UserId) -> UserId -> f UserId
userId f c = (\x -> c {_user = x}) <$> field f (_user c)

--------------------------------------------------------------------------------
Expand Down Expand Up @@ -168,6 +168,7 @@ toOtrRecipients =
Msg.OtrRecipients . Client.UserClientMap
. foldl' userEntries mempty
where
userEntries :: Map Id.UserId (Map Id.ClientId Text) -> UserEntry -> Map Id.UserId (Map Id.ClientId Text)
userEntries acc x =
let u = view userEntryId x
c = view userEntryClients x
Expand Down Expand Up @@ -285,7 +286,7 @@ toNewOtrMessage msg =
Msg.newOtrReportMissing = toReportMissing $ view newOtrMessageReportMissing msg
}

toReportMissing :: [UserId] -> Maybe [Id.OpaqueUserId]
toReportMissing :: [UserId] -> Maybe [Id.UserId]
toReportMissing [] = Nothing
toReportMissing us = Just $ view userId <$> us

Expand Down
2 changes: 0 additions & 2 deletions libs/wire-api/src/Wire/API/Swagger.hs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ models =
User.Client.modelSigkeys,
User.Client.modelLocation, -- re-export from types-common
User.Client.modelPubClient,
User.Client.Prekey.modelPrekeyBundle,
User.Client.Prekey.modelClientPrekey,
User.Client.Prekey.modelPrekey,
User.Handle.modelUserHandleInfo,
User.Handle.modelCheckHandles,
Expand Down
95 changes: 91 additions & 4 deletions libs/wire-api/src/Wire/API/User/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
module Wire.API.User.Client
( -- * UserClients
UserClientMap (..),
QualifiedUserClientMap (..),
UserClients (..),
QualifiedUserClients (..),
filterClients,

-- * Client
Expand Down Expand Up @@ -58,31 +60,39 @@ module Wire.API.User.Client
)
where

import Control.Lens ((?~), (^.))
import Data.Aeson
import Data.Domain (Domain)
import qualified Data.HashMap.Strict as HashMap
import Data.Id
import Data.Json.Util
import qualified Data.Map.Strict as Map
import Data.Misc (Latitude (..), Location, Longitude (..), PlainTextPassword (..), latitude, location, longitude, modelLocation)
import Data.Swagger (ToSchema)
import Data.Proxy (Proxy (..))
import Data.Swagger (HasExample (example), NamedSchema (..), ToSchema (..), declareSchema)
import qualified Data.Swagger as Swagger
import qualified Data.Swagger.Build.Api as Doc
import Data.Swagger.Schema (toSchema)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text.E
import Data.Typeable (typeRep)
import Data.UUID (toASCIIBytes)
import Deriving.Swagger (CamelToSnake, ConstructorTagModifier, CustomSwagger, FieldLabelModifier, LabelMapping ((:->)), LabelMappings, LowerCase, StripPrefix, StripSuffix)
import Imports
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..), mapOf', setOf')
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..), generateExample, mapOf', setOf')
import Wire.API.User.Auth (CookieLabel)
import Wire.API.User.Client.Prekey as Prekey

--------------------------------------------------------------------------------
-- UserClientMap

newtype UserClientMap a = UserClientMap
{ userClientMap :: Map OpaqueUserId (Map ClientId a)
{ userClientMap :: Map UserId (Map ClientId a)
}
deriving stock (Eq, Show, Functor, Foldable, Traversable)
deriving newtype (Semigroup, Monoid)

-- FUTUREWORK: Remove when 'NewOtrMessage' has ToSchema
modelOtrClientMap :: Doc.Model
modelOtrClientMap = Doc.defineModel "OtrClientMap" $ do
Doc.description "Map of client IDs to OTR content."
Expand Down Expand Up @@ -115,15 +125,71 @@ instance FromJSON a => FromJSON (UserClientMap a) where
instance Arbitrary a => Arbitrary (UserClientMap a) where
arbitrary = UserClientMap <$> mapOf' arbitrary (mapOf' arbitrary arbitrary)

instance ToSchema (UserClientMap (Maybe Prekey)) where
declareNamedSchema _ = do
mapSch <- declareSchema (Proxy @(Map UserId (Map ClientId (Maybe Prekey))))
let valueTypeName = Text.pack $ show $ typeRep $ Proxy @(Maybe Prekey)
return $
NamedSchema (Just $ "UserClientMap (" <> valueTypeName <> ")") $
mapSch
& example
?~ toJSON
( Map.singleton
(generateExample @UserId)
( Map.singleton
(newClientId 4940483633899001999)
(Just (Prekey (PrekeyId 1) "pQABAQECoQBYIOjl7hw0D8YRNq..."))
)
)

newtype QualifiedUserClientMap a = QualifiedUserClientMap
{ qualifiedUserClientMap :: Map Domain (UserClientMap a)
}
deriving stock (Eq, Show)
deriving newtype (Semigroup, Monoid, ToJSON, FromJSON)

instance Arbitrary a => Arbitrary (QualifiedUserClientMap a) where
arbitrary = QualifiedUserClientMap <$> mapOf' arbitrary arbitrary

instance (Typeable a, ToSchema (UserClientMap a)) => ToSchema (QualifiedUserClientMap a) where
declareNamedSchema _ = do
mapSch <- declareSchema (Proxy @(Map Domain (UserClientMap a)))
let userMapSchema = toSchema (Proxy @(UserClientMap a))
let valueTypeName = Text.pack $ show $ typeRep $ Proxy @a
return $
NamedSchema (Just $ "QualifiedUserClientMap (" <> valueTypeName <> ")") $
mapSch
& Swagger.description ?~ "Map of Domain to (UserMap (" <> valueTypeName <> "))."
& example
?~ toJSON
(Map.singleton ("domain1.example.com" :: Text) (userMapSchema ^. example))

--------------------------------------------------------------------------------
-- UserClients

-- TODO: check if example generated by swagger look okay (probably not)
newtype UserClients = UserClients
{ userClients :: Map OpaqueUserId (Set ClientId)
{ userClients :: Map UserId (Set ClientId)
}
deriving stock (Eq, Show, Generic)
deriving newtype (Semigroup, Monoid)

instance ToSchema UserClients where
declareNamedSchema _ = do
mapSch <- declareSchema (Proxy @(Map UserId (Set ClientId)))
return $
NamedSchema (Just "UserClients") $
mapSch
& Swagger.description ?~ "Map of user id to list of client ids."
& example
?~ toJSON
( Map.fromList
[ (generateExample @UserId, [newClientId 1684636986166846496, newClientId 4940483633899001999]),
(generateExample @UserId, [newClientId 6987438498444556166, newClientId 7940473633839002939])
]
)

-- FUTUREWORK: Remove when 'NewOtrMessage' has ToSchema
modelUserClients :: Doc.Model
modelUserClients =
Doc.defineModel "UserClients" $
Expand All @@ -150,6 +216,27 @@ instance Arbitrary UserClients where
filterClients :: (Set ClientId -> Bool) -> UserClients -> UserClients
filterClients p (UserClients c) = UserClients $ Map.filter p c

newtype QualifiedUserClients = QualifiedUserClients
{ qualifiedUserClients :: Map Domain UserClients
}
deriving stock (Eq, Show, Generic)
deriving newtype (Semigroup, Monoid, FromJSON, ToJSON)

instance Arbitrary QualifiedUserClients where
arbitrary = QualifiedUserClients <$> mapOf' arbitrary arbitrary

instance ToSchema QualifiedUserClients where
declareNamedSchema _ = do
schema <- declareSchema (Proxy @(Map Domain UserClients))
userClientsSchema <- declareSchema (Proxy @UserClients)
return $
NamedSchema (Just "QualifiedUserClients") $
schema
& Swagger.description ?~ "Map of Domain to UserClients"
& example
?~ toJSON
(Map.singleton ("domain1.example.com" :: Text) (userClientsSchema ^. example))

--------------------------------------------------------------------------------
-- Client

Expand Down
43 changes: 23 additions & 20 deletions libs/wire-api/src/Wire/API/User/Client/Prekey.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,23 @@ module Wire.API.User.Client.Prekey
ClientPrekey (..),

-- * Swagger
modelPrekeyBundle,
modelClientPrekey,
modelPrekey,
)
where

import Data.Aeson
import Data.Data (Proxy (Proxy))
import Data.Hashable (hash)
import Data.Id
import Data.Swagger (ToSchema (..))
import qualified Data.Swagger.Build.Api as Doc
import Deriving.Swagger (CustomSwagger (..), FieldLabelModifier, LabelMapping ((:->)), LabelMappings, LowerCase, StripPrefix)
import Imports
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..))

newtype PrekeyId = PrekeyId {keyId :: Word16}
deriving stock (Eq, Ord, Show, Generic)
deriving newtype (ToJSON, FromJSON, Arbitrary)
deriving newtype (ToJSON, FromJSON, Arbitrary, ToSchema)

--------------------------------------------------------------------------------
-- Prekey
Expand All @@ -57,7 +58,9 @@ data Prekey = Prekey
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform Prekey)
deriving (ToSchema) via (CustomSwagger '[FieldLabelModifier (StripPrefix "prekey", LowerCase)] Prekey)

-- FUTUREWORK: Remove when 'NewClient' has ToSchema
modelPrekey :: Doc.Model
modelPrekey = Doc.defineModel "Prekey" $ do
Doc.description "Prekey"
Expand Down Expand Up @@ -88,6 +91,9 @@ newtype LastPrekey = LastPrekey
{unpackLastPrekey :: Prekey}
deriving stock (Eq, Show, Generic)

instance ToSchema LastPrekey where
declareNamedSchema _ = declareNamedSchema (Proxy @Prekey)

instance ToJSON LastPrekey where
toJSON = toJSON . unpackLastPrekey

Expand All @@ -113,19 +119,12 @@ lastPrekey = LastPrekey . Prekey lastPrekeyId
-- PrekeyBundle

data PrekeyBundle = PrekeyBundle
{ prekeyUser :: OpaqueUserId,
{ prekeyUser :: UserId,
prekeyClients :: [ClientPrekey]
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform PrekeyBundle)

modelPrekeyBundle :: Doc.Model
modelPrekeyBundle = Doc.defineModel "PrekeyBundle" $ do
Doc.description "Prekeys of all clients of a single user"
Doc.property "user" Doc.bytes' $
Doc.description "User ID"
Doc.property "clients" (Doc.array (Doc.ref modelClientPrekey)) $
Doc.description "Prekeys of all clients"
deriving (ToSchema) via (CustomSwagger '[FieldLabelModifier (StripPrefix "prekey", LowerCase)] PrekeyBundle)

instance ToJSON PrekeyBundle where
toJSON k =
Expand All @@ -147,14 +146,18 @@ data ClientPrekey = ClientPrekey
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform ClientPrekey)

modelClientPrekey :: Doc.Model
modelClientPrekey = Doc.defineModel "ClientPrekey" $ do
Doc.description "Prekey of a single client"
Doc.property "client" Doc.bytes' $
Doc.description "Client Id"
Doc.property "prekey" (Doc.ref modelPrekey) $
Doc.description "Prekey"
deriving
(ToSchema)
via ( CustomSwagger
'[ FieldLabelModifier
( LabelMappings
'[ "prekeyClient" ':-> "client",
"prekeyData" ':-> "prekey"
]
)
]
ClientPrekey
)

instance ToJSON ClientPrekey where
toJSON k =
Expand Down
Loading