module Ecluse.Core.Registry.Request (
sealRequest,
finaliseRequest,
CredentialMapping,
credentialMapping,
credentialRecover,
attachCredential,
authorizationUnder,
artifactRequestByUrl,
joinPath,
parseRequestEither,
) where
import Data.Text qualified as T
import Network.HTTP.Client (Request (decompress, redirectCount, requestHeaders), parseRequest)
import Network.HTTP.Types.Header (
HeaderName,
RequestHeaders,
hAuthorization,
hUserAgent,
)
import Ecluse.Core.BuildIdentity (userAgent)
import Ecluse.Core.Credential (ClientCredential)
import Ecluse.Core.Registry (UrlFormationError (EmptyBaseUrl, UnparseableUrl))
import Ecluse.Core.Text (joinUrlPath)
sealRequest :: Request -> Request
sealRequest :: Request -> Request
sealRequest Request
request =
Request
request
{ redirectCount = 0
, requestHeaders = identify (requestHeaders request)
}
where
identify :: [(HeaderName, ByteString)] -> [(HeaderName, ByteString)]
identify [(HeaderName, ByteString)]
headers
| ((HeaderName, ByteString) -> Bool)
-> [(HeaderName, ByteString)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((HeaderName -> HeaderName -> Bool
forall a. Eq a => a -> a -> Bool
== HeaderName
hUserAgent) (HeaderName -> Bool)
-> ((HeaderName, ByteString) -> HeaderName)
-> (HeaderName, ByteString)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HeaderName, ByteString) -> HeaderName
forall a b. (a, b) -> a
fst) [(HeaderName, ByteString)]
headers = [(HeaderName, ByteString)]
headers
| Bool
otherwise = (HeaderName
hUserAgent, ByteString
userAgent) (HeaderName, ByteString)
-> [(HeaderName, ByteString)] -> [(HeaderName, ByteString)]
forall a. a -> [a] -> [a]
: [(HeaderName, ByteString)]
headers
finaliseRequest :: (Request -> Request) -> Request -> Request
finaliseRequest :: (Request -> Request) -> Request -> Request
finaliseRequest Request -> Request
attach = Request -> Request
sealRequest (Request -> Request) -> (Request -> Request) -> Request -> Request
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Request
attach
data CredentialMapping = CredentialMapping
{ CredentialMapping
-> [(HeaderName, ByteString)] -> Maybe ClientCredential
credentialRecover :: RequestHeaders -> Maybe ClientCredential
,
:: HeaderName
,
CredentialMapping -> ClientCredential -> ByteString
credentialRender :: ClientCredential -> ByteString
}
credentialMapping ::
(RequestHeaders -> Maybe ClientCredential) ->
HeaderName ->
(ClientCredential -> ByteString) ->
CredentialMapping
credentialMapping :: ([(HeaderName, ByteString)] -> Maybe ClientCredential)
-> HeaderName
-> (ClientCredential -> ByteString)
-> CredentialMapping
credentialMapping [(HeaderName, ByteString)] -> Maybe ClientCredential
recover HeaderName
header ClientCredential -> ByteString
render =
CredentialMapping
{ credentialRecover :: [(HeaderName, ByteString)] -> Maybe ClientCredential
credentialRecover = [(HeaderName, ByteString)] -> Maybe ClientCredential
recover
, credentialHeader :: HeaderName
credentialHeader = HeaderName
header
, credentialRender :: ClientCredential -> ByteString
credentialRender = ClientCredential -> ByteString
render
}
attachCredential :: CredentialMapping -> Maybe ClientCredential -> Request -> Request
attachCredential :: CredentialMapping -> Maybe ClientCredential -> Request -> Request
attachCredential CredentialMapping
mapping Maybe ClientCredential
credential = (Request -> Request) -> Request -> Request
finaliseRequest ((Request -> Request) -> Request -> Request)
-> (Request -> Request) -> Request -> Request
forall a b. (a -> b) -> a -> b
$ case Maybe ClientCredential
credential of
Maybe ClientCredential
Nothing -> Request -> Request
forall a. a -> a
id
Just ClientCredential
presented -> \Request
request ->
Request
request
{ requestHeaders =
(credentialHeader mapping, credentialRender mapping presented) : requestHeaders request
}
authorizationUnder :: Text -> RequestHeaders -> Maybe Text
authorizationUnder :: Text -> [(HeaderName, ByteString)] -> Maybe Text
authorizationUnder Text
scheme [(HeaderName, ByteString)]
headers = do
(_, raw) <- ((HeaderName, ByteString) -> Bool)
-> [(HeaderName, ByteString)] -> Maybe (HeaderName, ByteString)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((HeaderName -> HeaderName -> Bool
forall a. Eq a => a -> a -> Bool
== HeaderName
hAuthorization) (HeaderName -> Bool)
-> ((HeaderName, ByteString) -> HeaderName)
-> (HeaderName, ByteString)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (HeaderName, ByteString) -> HeaderName
forall a b. (a, b) -> a
fst) [(HeaderName, ByteString)]
headers
let (presented, rest) = T.break (== ' ') (decodeUtf8 raw)
guard (T.toLower presented == T.toLower scheme)
pure (T.dropWhile (== ' ') rest)
artifactRequestByUrl :: CredentialMapping -> Maybe ClientCredential -> Text -> Either UrlFormationError Request
artifactRequestByUrl :: CredentialMapping
-> Maybe ClientCredential
-> Text
-> Either UrlFormationError Request
artifactRequestByUrl CredentialMapping
mapping Maybe ClientCredential
credential Text
url = do
base <- Text -> Either UrlFormationError Request
parseRequestEither Text
url
pure . attachCredential mapping credential $ base{decompress = const False}
joinPath :: Text -> Text -> Either UrlFormationError Text
joinPath :: Text -> Text -> Either UrlFormationError Text
joinPath Text
baseUrl Text
path
| Text -> Bool
T.null Text
baseUrl = UrlFormationError -> Either UrlFormationError Text
forall a b. a -> Either a b
Left UrlFormationError
EmptyBaseUrl
| Bool
otherwise = Text -> Either UrlFormationError Text
forall a b. b -> Either a b
Right (Text -> Text -> Text
joinUrlPath Text
baseUrl Text
path)
parseRequestEither :: Text -> Either UrlFormationError Request
parseRequestEither :: Text -> Either UrlFormationError Request
parseRequestEither Text
url =
case String -> Maybe Request
forall (m :: * -> *). MonadThrow m => String -> m Request
parseRequest (Text -> String
forall a. ToString a => a -> String
toString Text
url) of
Just Request
request -> Request -> Either UrlFormationError Request
forall a b. b -> Either a b
Right (Request -> Request
sealRequest Request
request)
Maybe Request
Nothing -> UrlFormationError -> Either UrlFormationError Request
forall a b. a -> Either a b
Left (Text -> UrlFormationError
UnparseableUrl Text
url)