macaroon-shop-0.1.0.1: src/Authorize/Macaroon.hs
-- |
-- Module: Authorize.Macaroon
-- License: ISC
-- Maintainer: ics@gambolingpangolin.com
-- Stability: experimental
--
-- This module contains an implementation of macaroons as described in
-- <http://theory.stanford.edu/~ataly/Papers/macaroons.pdf>. The
-- serialization, cryptography, and validation semantics are compatible with
-- go-macaroons <https://github.com/go-macaroon/macaroon>.
module Authorize.Macaroon (
-- * Types
MacaroonId (..),
Macaroon,
SealedMacaroon (..),
Key (..),
Location,
-- * Core interface
createMacaroon,
addFirstPartyCaveat,
addThirdPartyCaveat,
extractThirdPartyCaveats,
sealMacaroon,
createDischargeMacaroon,
verify,
VerificationFailure (..),
) where
import Authorize.Macaroon.Crypto (
bindForRequest,
createSignature,
deriveKey,
encryptKey,
updateSignature,
)
import Authorize.Macaroon.Types (
Caveat (Caveat, caveatContent, caveatKeyId),
Key (..),
Location,
Macaroon (Macaroon, caveats, macaroonSignature),
MacaroonId (..),
SealedMacaroon (..),
)
import Authorize.Macaroon.Verify (
VerificationFailure (..),
verify,
)
import Data.ByteString (ByteString)
import Data.List (foldl')
import Data.Maybe (isJust)
-- | Mint a macaroon
createMacaroon ::
-- | signing key
Key ->
-- | identifier for this macaroon
MacaroonId ->
-- | location hint
Location ->
-- | first party caveats to include
[ByteString] ->
Macaroon
createMacaroon k mid loc = foldl' addFirstPartyCaveat m0
where
m0 = Macaroon loc mid [] $ createSignature (deriveKey k) mid
-- | A first party caveat corresponds to a proposition that might or might not
-- hold in the validation context of the macaroon.
addFirstPartyCaveat :: Macaroon -> ByteString -> Macaroon
addFirstPartyCaveat m = addCaveat m . Caveat mempty Nothing
-- | A third party caveat links the macaroon to an additional key, and must be
-- discharged by a supplementary macaroon in order to validate.
addThirdPartyCaveat ::
Macaroon ->
-- | third party key
Key ->
Location ->
ByteString ->
IO Macaroon
addThirdPartyCaveat m ck loc c =
addC <$> encryptKey (macaroonSignature m) (deriveKey ck)
where
addC k = addCaveat m $ Caveat loc (Just k) c
addCaveat :: Macaroon -> Caveat -> Macaroon
addCaveat m c@Caveat{caveatKeyId = k, caveatContent = cc} =
m
{ caveats = caveats m <> [c]
, macaroonSignature = updateSignature (macaroonSignature m) k cc
}
-- | Get the third party caveats encoded in the macaroon
extractThirdPartyCaveats :: Macaroon -> [ByteString]
extractThirdPartyCaveats = fmap caveatContent . filter isThirdParty . caveats
isThirdParty :: Caveat -> Bool
isThirdParty = isJust . caveatKeyId
-- | Mint a macaroon discharging a third party caveat
createDischargeMacaroon ::
-- | discharge key
Key ->
-- | location hint
Location ->
-- | caveat to discharge
ByteString ->
-- | additional first party caveats to include
[ByteString] ->
Macaroon
createDischargeMacaroon k l c = createMacaroon k (MacaroonId c) l
-- | In order to secure discharge macaroons, they must be bound to the root macaroon before transmission.
sealMacaroon ::
-- | root macaroon
Macaroon ->
-- | discharge macaroons
[Macaroon] ->
SealedMacaroon
sealMacaroon m@Macaroon{macaroonSignature = s} ms =
SealedMacaroon m $ bindMacaroon <$> ms
where
bindMacaroon m'@Macaroon{macaroonSignature = s'} =
m'{macaroonSignature = bindForRequest s s'}