-- | -- Module: Authorize.Macaroon -- License: ISC -- Maintainer: ics@gambolingpangolin.com -- Stability: experimental -- -- This module contains an implementation of macaroons as described in -- . The -- serialization, cryptography, and validation semantics are compatible with -- go-macaroons . module Authorize.Macaroon ( -- * Types MacaroonId (..) , Macaroon , SealedMacaroon (..) , Key (..) , Location -- * Core interface , createMacaroon , addFirstPartyCaveat , addThirdPartyCaveat , extractThirdPartyCaveats , sealMacaroon , createDischargeMacaroon , verify , VerificationFailure (..) ) where import Data.ByteString (ByteString) import Data.List (foldl') import Data.Maybe (isJust) import Authorize.Macaroon.Crypto import Authorize.Macaroon.Types import Authorize.Macaroon.Verify -- | Mint a macaroon createMacaroon :: Key -- ^ signing key -> MacaroonId -- ^ identifier for this macaroon -> Location -- ^ location hint -> [ByteString] -- ^ first party caveats to include -> 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 -> Key -- ^ third party 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 :: Key -- ^ discharge key -> Location -- ^ location hint -> ByteString -- ^ caveat to discharge -> [ByteString] -- ^ additional first party caveats to include -> 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 :: Macaroon -- ^ root macaroon -> [Macaroon] -- ^ discharge macaroons -> SealedMacaroon sealMacaroon m@Macaroon{ macaroonSignature = s } ms = SealedMacaroon m $ bindMacaroon <$> ms where bindMacaroon m'@Macaroon{ macaroonSignature = s' } = m' { macaroonSignature = bindForRequest s s' }