-
Notifications
You must be signed in to change notification settings - Fork 325
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Servant Cookie combinator * Parse authorization header * Remove confusing type synonyms * Create schemas for Wire.API.User.Auth types * Fix build and golden tests * Add Access(..) * Add mkSomeAccess and mkSomeCookie & adapt * Optional response headers in MultiVerb * Replace SomeCookie with UserTokenCookie * Finish servantification of access endpoint * Servantify send-login-code * Servantify login endpoint * Servantify logout endpoint * Servantify change-self-email endpoint * Servantify list-cookies endpoint * Servantify remove-cookies endpoint * Change status code to 200 * Servantify legalhold-login endpoint * Servantify sso-login endpoint * Servantify login-code endpoint * Servantify reauthenticate endpoint * Fix build * Add access_token query parameter * Parse cookies leniently * Adapt integration test to new error codes * Add CanThrow annotations * Document Bearer token in Swagger * Add CHANGELOG entry * Revert "Adapt integration test to new error codes" This reverts commit 165340a. * Make servant cookie parser lenient * More leniency in Servant parsers * Adapt some tests * Remove redundant Brig error * Redundant brackets * lbl → label * Reformat long line * Remove empty routes * Apply hlint suggestions * Regenerate nix derivations Co-authored-by: Stefan Matting <stefan@wire.com>
- Loading branch information
1 parent
eba4b7f
commit 526544d
Showing
48 changed files
with
1,455 additions
and
1,119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Convert brig's auth endpoints to servant |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
-- This file is part of the Wire Server implementation. | ||
-- | ||
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com> | ||
-- | ||
-- This program is free software: you can redistribute it and/or modify it under | ||
-- the terms of the GNU Affero General Public License as published by the Free | ||
-- Software Foundation, either version 3 of the License, or (at your option) any | ||
-- later version. | ||
-- | ||
-- This program is distributed in the hope that it will be useful, but WITHOUT | ||
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||
-- details. | ||
-- | ||
-- You should have received a copy of the GNU Affero General Public License along | ||
-- with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
module Wire.API.Routes.Bearer where | ||
|
||
import Control.Lens ((<>~)) | ||
import qualified Data.ByteString as BS | ||
import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap | ||
import Data.Metrics.Servant | ||
import Data.Swagger hiding (Header) | ||
import qualified Data.Text.Encoding as T | ||
import Imports | ||
import Servant | ||
import Servant.Swagger | ||
|
||
newtype Bearer a = Bearer {unBearer :: a} | ||
|
||
instance FromHttpApiData a => FromHttpApiData (Bearer a) where | ||
parseHeader h = case BS.splitAt 7 h of | ||
("Bearer ", suffix) -> Bearer <$> parseHeader suffix | ||
_ -> Left "Invalid authorization scheme" | ||
parseUrlPiece = parseHeader . T.encodeUtf8 | ||
|
||
type BearerHeader a = Header' '[Lenient] "Authorization" (Bearer a) | ||
|
||
type BearerQueryParam = | ||
QueryParam' | ||
[Lenient, Description "Access token"] | ||
"access_token" | ||
|
||
instance HasSwagger api => HasSwagger (Bearer a :> api) where | ||
toSwagger _ = | ||
toSwagger (Proxy @api) | ||
& security <>~ [SecurityRequirement $ InsOrdHashMap.singleton "ZAuth" []] | ||
|
||
instance RoutesToPaths api => RoutesToPaths (Bearer a :> api) where | ||
getRoutes = getRoutes @api | ||
|
||
instance | ||
( HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters, | ||
FromHttpApiData a, | ||
HasServer api ctx | ||
) => | ||
HasServer (Bearer a :> api) ctx | ||
where | ||
type ServerT (Bearer a :> api) m = Maybe (Either Text a) -> ServerT api m | ||
|
||
route _ ctx action = | ||
route | ||
(Proxy @(BearerHeader a :> BearerQueryParam a :> api)) | ||
ctx | ||
(fmap (\f u v -> f (fmap (fmap unBearer) u <|> v)) action) | ||
hoistServerWithContext _ ctx f h = hoistServerWithContext (Proxy @api) ctx f . h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
-- This file is part of the Wire Server implementation. | ||
-- | ||
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com> | ||
-- | ||
-- This program is free software: you can redistribute it and/or modify it under | ||
-- the terms of the GNU Affero General Public License as published by the Free | ||
-- Software Foundation, either version 3 of the License, or (at your option) any | ||
-- later version. | ||
-- | ||
-- This program is distributed in the hope that it will be useful, but WITHOUT | ||
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||
-- details. | ||
-- | ||
-- You should have received a copy of the GNU Affero General Public License along | ||
-- with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
module Wire.API.Routes.Cookies where | ||
|
||
import Data.List.NonEmpty (NonEmpty (..)) | ||
import qualified Data.Map as M | ||
import Data.Metrics.Servant | ||
import Data.SOP | ||
import qualified Data.Text as T | ||
import qualified Data.Text.Encoding as T | ||
import GHC.TypeLits | ||
import Imports | ||
import Servant | ||
import Servant.Swagger | ||
import Web.Cookie (parseCookies) | ||
|
||
data (:::) a b | ||
|
||
-- | A combinator to extract cookies from an HTTP request. The recommended way | ||
-- to use this combinator is to specify it exactly once in the description of | ||
-- an endpoint, passing a list of pairs of cookie name and type, separated by | ||
-- '(:::)'. Cookies are always optional. | ||
-- | ||
-- For example: | ||
-- @@ | ||
-- Cookies '["foo" ::: Int64, "bar" ::: Text] | ||
-- @@ | ||
-- results in a cookie with name "foo" containing a 64-bit integer, and a | ||
-- cookie with name "bar" containing an arbitrary text value. | ||
data Cookies (cs :: [*]) | ||
|
||
type CookieHeader cs = Header "Cookie" (CookieTuple cs) | ||
|
||
-- CookieTypes = map snd | ||
type family CookieTypes (cs :: [*]) :: [*] | ||
|
||
type instance CookieTypes '[] = '[] | ||
|
||
type instance CookieTypes ((label ::: x) ': cs) = ([Either Text x] ': CookieTypes cs) | ||
|
||
newtype CookieTuple cs = CookieTuple {unCookieTuple :: NP I (CookieTypes cs)} | ||
|
||
type CookieMap = Map ByteString (NonEmpty ByteString) | ||
|
||
instance HasSwagger api => HasSwagger (Cookies cs :> api) where | ||
toSwagger _ = toSwagger (Proxy @api) | ||
|
||
class CookieArgs (cs :: [*]) where | ||
-- example: AddArgs ["foo" :: Foo, "bar" :: Bar] a = Foo -> Bar -> a | ||
type AddArgs cs a :: * | ||
|
||
uncurryArgs :: AddArgs cs a -> CookieTuple cs -> a | ||
mapArgs :: (a -> b) -> AddArgs cs a -> AddArgs cs b | ||
mkTuple :: CookieMap -> Either Text (CookieTuple cs) | ||
emptyTuple :: CookieTuple cs | ||
|
||
instance CookieArgs '[] where | ||
type AddArgs '[] a = a | ||
uncurryArgs a _ = a | ||
mapArgs h = h | ||
mkTuple _ = pure emptyTuple | ||
emptyTuple = CookieTuple Nil | ||
|
||
instance | ||
( CookieArgs cs, | ||
KnownSymbol label, | ||
FromHttpApiData x | ||
) => | ||
CookieArgs ((label ::: (x :: *)) ': cs) | ||
where | ||
type AddArgs ((label ::: x) ': cs) a = [Either Text x] -> AddArgs cs a | ||
uncurryArgs f (CookieTuple (I x :* xs)) = uncurryArgs @cs (f x) (CookieTuple xs) | ||
mapArgs h f = mapArgs @cs h . f | ||
mkTuple m = do | ||
let k = T.pack (symbolVal (Proxy @label)) | ||
bs <- pure . maybe [] toList $ M.lookup (T.encodeUtf8 k) m | ||
let vs = map parseHeader bs | ||
CookieTuple t <- mkTuple @cs m | ||
pure (CookieTuple (I vs :* t)) | ||
emptyTuple = CookieTuple (I [] :* unCookieTuple (emptyTuple @cs)) | ||
|
||
mkCookieMap :: [(ByteString, ByteString)] -> CookieMap | ||
mkCookieMap = foldr (\(k, v) -> M.insertWith (<>) k (pure v)) mempty | ||
|
||
instance CookieArgs cs => FromHttpApiData (CookieTuple cs) where | ||
parseHeader = mkTuple . mkCookieMap . parseCookies | ||
parseUrlPiece = parseHeader . T.encodeUtf8 | ||
|
||
instance | ||
( HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters, | ||
CookieArgs cs, | ||
HasServer api ctx | ||
) => | ||
HasServer (Cookies cs :> api) ctx | ||
where | ||
type ServerT (Cookies cs :> api) m = AddArgs cs (ServerT api m) | ||
|
||
route _ ctx action = | ||
route | ||
(Proxy @(CookieHeader cs :> api)) | ||
ctx | ||
( fmap | ||
(\f -> uncurryArgs f . fromMaybe emptyTuple) | ||
action | ||
) | ||
hoistServerWithContext _ ctx f = mapArgs @cs (hoistServerWithContext (Proxy @api) ctx f) | ||
|
||
instance RoutesToPaths api => RoutesToPaths (Cookies cs :> api) where | ||
getRoutes = getRoutes @api |
Oops, something went wrong.