module Ecluse.Core.Server.Pipeline.Shared (
edgeTokenMatches,
forwardedToken,
integrityMissing,
integrityBelowFloor,
trustedIntegrityMissing,
trustedIntegrityBelowFloor,
hRetryAfter,
shedStatus,
shedRetryAfter,
) where
import Data.Text qualified as T
import Network.HTTP.Types (Header, HeaderName, Status, hAuthorization, status503)
import Network.Wai (Request, requestHeaders)
import Ecluse.Core.Credential (Secret, mkSecret)
import Ecluse.Core.Server.Admission.Weighted (admissionWaitMicros)
import Ecluse.Core.Server.Response (
RejectReason (BelowIntegrityFloor, MissingIntegrity),
Rejection (Rejection),
ServeDecision (Reject),
)
hRetryAfter :: HeaderName
hRetryAfter :: HeaderName
hRetryAfter = HeaderName
"Retry-After"
shedStatus :: Status
shedStatus :: Status
shedStatus = Status
status503
shedRetryAfter :: Header
shedRetryAfter :: Header
shedRetryAfter = (HeaderName
hRetryAfter, Int -> ByteString
forall b a. (Show a, IsString b) => a -> b
show (Int
admissionWaitMicros Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
1_000_000))
edgeTokenMatches :: Maybe Secret -> Maybe Secret -> Bool
edgeTokenMatches :: Maybe Secret -> Maybe Secret -> Bool
edgeTokenMatches Maybe Secret
expected Maybe Secret
forwarded = case Maybe Secret
expected of
Maybe Secret
Nothing -> Bool
True
Just Secret
want -> Maybe Secret
forwarded Maybe Secret -> Maybe Secret -> Bool
forall a. Eq a => a -> a -> Bool
== Secret -> Maybe Secret
forall a. a -> Maybe a
Just Secret
want
forwardedToken :: Request -> Maybe Secret
forwardedToken :: Request -> Maybe Secret
forwardedToken Request
request = do
(_, raw) <- (Header -> Bool) -> [Header] -> Maybe Header
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) -> (Header -> HeaderName) -> Header -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Header -> HeaderName
forall a b. (a, b) -> a
fst) (Request -> [Header]
requestHeaders Request
request)
let value = ByteString -> Text
forall a b. ConvertUtf8 a b => b -> a
decodeUtf8 ByteString
raw
(scheme, rest) = T.break (== ' ') value
guard (T.toLower scheme == "bearer")
let token = (Char -> Bool) -> Text -> Text
T.dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ') Text
rest
guard (not (T.null token))
pure (mkSecret token)
integrityMissing :: ServeDecision
integrityMissing :: ServeDecision
integrityMissing =
Rejection -> ServeDecision
Reject (RejectReason -> Text -> Rejection
Rejection RejectReason
MissingIntegrity Text
"this version carries no integrity digest and cannot be served from a public upstream")
integrityBelowFloor :: ServeDecision
integrityBelowFloor :: ServeDecision
integrityBelowFloor =
Rejection -> ServeDecision
Reject (RejectReason -> Text -> Rejection
Rejection RejectReason
BelowIntegrityFloor Text
"this version's integrity digest is weaker than the configured minimum and cannot be served from a public upstream")
trustedIntegrityMissing :: ServeDecision
trustedIntegrityMissing :: ServeDecision
trustedIntegrityMissing =
Rejection -> ServeDecision
Reject (RejectReason -> Text -> Rejection
Rejection RejectReason
MissingIntegrity Text
"this private version carries no integrity digest and was not served")
trustedIntegrityBelowFloor :: ServeDecision
trustedIntegrityBelowFloor :: ServeDecision
trustedIntegrityBelowFloor =
Rejection -> ServeDecision
Reject (RejectReason -> Text -> Rejection
Rejection RejectReason
BelowIntegrityFloor Text
"this private version's integrity digest is weaker than the configured trusted minimum and was not served")