From 5a7d75fd0eb589d3accf29d0d5e6d6d3afbbaaa3 Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 21:25:38 +0200 Subject: [PATCH 1/6] cleanup protobuf naming following changes in https://github.com/eclipse-biscuit/biscuit/pull/182 This does not affect serialization --- biscuit/src/Auth/Biscuit/Proto.hs | 62 ++++++++-------- biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs | 82 ++++++++++----------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/biscuit/src/Auth/Biscuit/Proto.hs b/biscuit/src/Auth/Biscuit/Proto.hs index ca8ce22..ec8e63e 100644 --- a/biscuit/src/Auth/Biscuit/Proto.hs +++ b/biscuit/src/Auth/Biscuit/Proto.hs @@ -21,13 +21,13 @@ module Auth.Biscuit.Proto , Block (..) , Scope (..) , ScopeType (..) - , FactV2 (..) - , RuleV2 (..) + , Fact (..) + , Rule (..) , CheckKind (..) - , CheckV2 (..) - , PredicateV2 (..) - , TermV2 (..) - , ExpressionV2 (..) + , Check (..) + , Predicate (..) + , Term (..) + , Expression (..) , TermSet (..) , TermArray (..) , TermMap (..) @@ -103,14 +103,14 @@ data PublicKey = PublicKey deriving anyclass (Decode, Encode) data Block = Block { - symbols :: Repeated 1 (Value Text) - , context :: Optional 2 (Value Text) - , version :: Optional 3 (Value Int32) - , facts_v2 :: Repeated 4 (Message FactV2) - , rules_v2 :: Repeated 5 (Message RuleV2) - , checks_v2 :: Repeated 6 (Message CheckV2) - , scope :: Repeated 7 (Message Scope) - , pksTable :: Repeated 8 (Message PublicKey) + symbols :: Repeated 1 (Value Text) + , context :: Optional 2 (Value Text) + , version :: Optional 3 (Value Int32) + , facts :: Repeated 4 (Message Fact) + , rules :: Repeated 5 (Message Rule) + , checks :: Repeated 6 (Message Check) + , scope :: Repeated 7 (Message Scope) + , pksTable :: Repeated 8 (Message PublicKey) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) @@ -125,15 +125,15 @@ data Scope = deriving stock (Generic, Show) deriving anyclass (Decode, Encode) -newtype FactV2 = FactV2 - { predicate :: Required 1 (Message PredicateV2) +newtype Fact = Fact + { predicate :: Required 1 (Message Predicate) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) -data RuleV2 = RuleV2 - { head :: Required 1 (Message PredicateV2) - , body :: Repeated 2 (Message PredicateV2) - , expressions :: Repeated 3 (Message ExpressionV2) +data Rule = Rule + { head :: Required 1 (Message Predicate) + , body :: Repeated 2 (Message Predicate) + , expressions :: Repeated 3 (Message Expression) , scope :: Repeated 4 (Message Scope) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) @@ -144,19 +144,19 @@ data CheckKind = | Reject deriving stock (Show, Enum, Bounded) -data CheckV2 = CheckV2 - { queries :: Repeated 1 (Message RuleV2) +data Check = Check + { queries :: Repeated 1 (Message Rule) , kind :: Optional 2 (Enumeration CheckKind) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) -data PredicateV2 = PredicateV2 +data Predicate = Predicate { name :: Required 1 (Value Int64) - , terms :: Repeated 2 (Message TermV2) + , terms :: Repeated 2 (Message Term) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) -data TermV2 = +data Term = TermVariable (Required 1 (Value Int64)) | TermInteger (Required 2 (Value Int64)) | TermString (Required 3 (Value Int64)) @@ -176,12 +176,12 @@ data Empty = Empty {} newtype TermSet = TermSet - { set :: Repeated 1 (Message TermV2) + { set :: Repeated 1 (Message Term) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) newtype TermArray = TermArray - { array :: Repeated 1 (Message TermV2) + { array :: Repeated 1 (Message Term) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) @@ -193,7 +193,7 @@ data MapKey = data MapEntry = MapEntry { key :: Required 1 (Message MapKey) - , value :: Required 2 (Message TermV2) + , value :: Required 2 (Message Term) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) @@ -202,13 +202,13 @@ newtype TermMap = TermMap } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) -newtype ExpressionV2 = ExpressionV2 +newtype Expression = Expression { ops :: Repeated 1 (Message Op) } deriving stock (Generic, Show) deriving anyclass (Decode, Encode) data Op = - OpVValue (Required 1 (Message TermV2)) + OpVValue (Required 1 (Message Term)) | OpVUnary (Required 2 (Message OpUnary)) | OpVBinary (Required 3 (Message OpBinary)) | OpVClosure (Required 4 (Message OpClosure)) @@ -254,7 +254,7 @@ data BinaryKind = | Any | Get | BinaryFfi - | Try + | TryOr deriving stock (Show, Enum, Bounded) data OpBinary = OpBinary diff --git a/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs b/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs index 7a0a5ae..9d604c4 100644 --- a/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs +++ b/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs @@ -130,9 +130,9 @@ pbToBlock ePk PB.Block{..} = do bVersion = PB.getField version lift $ do let s = symbolsForCurrentBlock - bFacts <- traverse (pbToFact s) $ PB.getField facts_v2 - bRules <- traverse (pbToRule s) $ PB.getField rules_v2 - bChecks <- traverse (pbToCheck s) $ PB.getField checks_v2 + bFacts <- traverse (pbToFact s) $ PB.getField facts + bRules <- traverse (pbToRule s) $ PB.getField rules + bChecks <- traverse (pbToCheck s) $ PB.getField checks bScope <- Set.fromList <$> traverse (pbToScope s) (PB.getField scope) let v6Plus = or [ any isReject bChecks @@ -190,37 +190,37 @@ blockToPb hasExternalPk existingSymbols b@Block{..} = ] bSymbols = buildSymbolTable existingSymbols b s = reverseSymbols $ addFromBlock existingSymbols bSymbols - symbols = PB.putField $ getSymbolList bSymbols - context = PB.putField bContext - facts_v2 = PB.putField $ factToPb s <$> bFacts - rules_v2 = PB.putField $ ruleToPb s <$> bRules - checks_v2 = PB.putField $ checkToPb s <$> bChecks - scope = PB.putField $ scopeToPb s <$> Set.toList bScope - pksTable = PB.putField $ publicKeyToPb <$> getPkList bSymbols - version = if | v6Plus -> 6 - | v5Plus -> 5 - | v4Plus -> 4 - | otherwise -> 3 + symbols = PB.putField $ getSymbolList bSymbols + context = PB.putField bContext + facts = PB.putField $ factToPb s <$> bFacts + rules = PB.putField $ ruleToPb s <$> bRules + checks = PB.putField $ checkToPb s <$> bChecks + scope = PB.putField $ scopeToPb s <$> Set.toList bScope + pksTable = PB.putField $ publicKeyToPb <$> getPkList bSymbols + version = if | v6Plus -> 6 + | v5Plus -> 5 + | v4Plus -> 4 + | otherwise -> 3 in ((bSymbols, version), PB.Block {version = PB.putField $ Just $ fromIntegral version, ..}) -pbToFact :: Symbols -> PB.FactV2 -> Either String Fact -pbToFact s PB.FactV2{predicate} = do +pbToFact :: Symbols -> PB.Fact -> Either String Fact +pbToFact s PB.Fact{predicate} = do let pbName = PB.getField $ PB.name $ PB.getField predicate pbTerms = PB.getField $ PB.terms $ PB.getField predicate name <- getSymbol s $ SymbolRef pbName terms <- traverse (pbToValue s) pbTerms pure Predicate{..} -factToPb :: ReverseSymbols -> Fact -> PB.FactV2 +factToPb :: ReverseSymbols -> Fact -> PB.Fact factToPb s Predicate{..} = let - predicate = PB.PredicateV2 + predicate = PB.Predicate { name = PB.putField $ getSymbolRef $ getSymbolCode s name , terms = PB.putField $ valueToPb s <$> terms } - in PB.FactV2{predicate = PB.putField predicate} + in PB.Fact{predicate = PB.putField predicate} -pbToRule :: Symbols -> PB.RuleV2 -> Either String Rule +pbToRule :: Symbols -> PB.Rule -> Either String Rule pbToRule s pbRule = do let pbHead = PB.getField $ PB.head pbRule pbBody = PB.getField $ PB.body pbRule @@ -234,17 +234,17 @@ pbToRule s pbRule = do Failure vs -> Left $ "Unbound variables in rule: " <> T.unpack (T.intercalate ", " $ NE.toList vs) Success r -> pure r -ruleToPb :: ReverseSymbols -> Rule -> PB.RuleV2 +ruleToPb :: ReverseSymbols -> Rule -> PB.Rule ruleToPb s Rule{..} = - PB.RuleV2 + PB.Rule { head = PB.putField $ predicateToPb s rhead , body = PB.putField $ predicateToPb s <$> body , expressions = PB.putField $ expressionToPb s <$> expressions , scope = PB.putField $ scopeToPb s <$> Set.toList scope } -pbToCheck :: Symbols -> PB.CheckV2 -> Either String Check -pbToCheck s PB.CheckV2{queries,kind} = do +pbToCheck :: Symbols -> PB.Check -> Either String Check +pbToCheck s PB.Check{queries,kind} = do let toCheck Rule{body,expressions,scope} = QueryItem{qBody = body, qExpressions = expressions, qScope = scope} rules <- traverse (pbToRule s) $ PB.getField queries let cQueries = toCheck <$> rules @@ -255,7 +255,7 @@ pbToCheck s PB.CheckV2{queries,kind} = do Nothing -> CheckOne pure Check{..} -checkToPb :: ReverseSymbols -> Check -> PB.CheckV2 +checkToPb :: ReverseSymbols -> Check -> PB.Check checkToPb s Check{..} = let dummyHead = Predicate "query" [] toQuery QueryItem{..} = @@ -268,7 +268,7 @@ checkToPb s Check{..} = CheckOne -> Nothing CheckAll -> Just PB.CheckAll Reject -> Just PB.Reject - in PB.CheckV2 { queries = PB.putField $ toQuery <$> cQueries + in PB.Check { queries = PB.putField $ toQuery <$> cQueries , kind = PB.putField pbKind } @@ -286,7 +286,7 @@ scopeToPb s = \case Previous -> PB.ScType $ PB.putField PB.ScopePrevious BlockId pk -> PB.ScBlock $ PB.putField $ getPublicKeyCode s pk -pbToPredicate :: Symbols -> PB.PredicateV2 -> Either String (Predicate' 'InPredicate 'Representation) +pbToPredicate :: Symbols -> PB.Predicate -> Either String (Predicate' 'InPredicate 'Representation) pbToPredicate s pbPredicate = do let pbName = PB.getField $ PB.name pbPredicate pbTerms = PB.getField $ PB.terms pbPredicate @@ -294,9 +294,9 @@ pbToPredicate s pbPredicate = do terms <- traverse (pbToTerm s) pbTerms pure Predicate{..} -predicateToPb :: ReverseSymbols -> Predicate -> PB.PredicateV2 +predicateToPb :: ReverseSymbols -> Predicate -> PB.Predicate predicateToPb s Predicate{..} = - PB.PredicateV2 + PB.Predicate { name = PB.putField $ getSymbolRef $ getSymbolCode s name , terms = PB.putField $ termToPb s <$> terms } @@ -304,7 +304,7 @@ predicateToPb s Predicate{..} = pbTimeToUtcTime :: Int64 -> UTCTime pbTimeToUtcTime = posixSecondsToUTCTime . fromIntegral -pbToTerm :: Symbols -> PB.TermV2 -> Either String Term +pbToTerm :: Symbols -> PB.Term -> Either String Term pbToTerm s = \case PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f) @@ -317,7 +317,7 @@ pbToTerm s = \case PB.TermTermMap f -> TermMap . Map.fromList <$> traverse (pbToMapEntry s) (PB.getField . PB.map $ PB.getField f) PB.TermNull _ -> pure LNull -termToPb :: ReverseSymbols -> Term -> PB.TermV2 +termToPb :: ReverseSymbols -> Term -> PB.Term termToPb s = \case Variable n -> PB.TermVariable $ PB.putField $ getSymbolRef $ getSymbolCode s n LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v @@ -348,7 +348,7 @@ pbToMapEntry s PB.MapEntry{key,value} = do v <- pbToValue s $ PB.getField value pure (k, v) -pbToValue :: Symbols -> PB.TermV2 -> Either String Value +pbToValue :: Symbols -> PB.Term -> Either String Value pbToValue s = \case PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f) @@ -361,7 +361,7 @@ pbToValue s = \case PB.TermTermMap f -> TermMap . Map.fromList <$> traverse (pbToMapEntry s) (PB.getField . PB.map $ PB.getField f) PB.TermNull _ -> pure LNull -valueToPb :: ReverseSymbols -> Value -> PB.TermV2 +valueToPb :: ReverseSymbols -> Value -> PB.Term valueToPb s = \case LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v LString v -> PB.TermString $ PB.putField $ getSymbolRef $ getSymbolCode s v @@ -376,7 +376,7 @@ valueToPb s = \case Variable v -> absurd v Antiquote v -> absurd v -pbToSetValue :: Symbols -> PB.TermV2 -> Either String (Term' 'WithinSet 'InFact 'Representation) +pbToSetValue :: Symbols -> PB.Term -> Either String (Term' 'WithinSet 'InFact 'Representation) pbToSetValue s = \case PB.TermInteger f -> pure $ LInteger $ fromIntegral $ PB.getField f PB.TermString f -> LString <$> getSymbol s (SymbolRef $ PB.getField f) @@ -389,7 +389,7 @@ pbToSetValue s = \case PB.TermTermArray _ -> Left "Arrays can’t appear in sets" PB.TermTermMap _ -> Left "Maps can’t appear in sets" -setValueToPb :: ReverseSymbols -> Term' 'WithinSet 'InFact 'Representation -> PB.TermV2 +setValueToPb :: ReverseSymbols -> Term' 'WithinSet 'InFact 'Representation -> PB.Term setValueToPb s = \case LInteger v -> PB.TermInteger $ PB.putField $ fromIntegral v LString v -> PB.TermString $ PB.putField $ getSymbolRef $ getSymbolCode s v @@ -404,15 +404,15 @@ setValueToPb s = \case Variable v -> absurd v Antiquote v -> absurd v -pbToExpression :: Symbols -> PB.ExpressionV2 -> Either String Expression -pbToExpression s PB.ExpressionV2{ops} = do +pbToExpression :: Symbols -> PB.Expression -> Either String Expression +pbToExpression s PB.Expression{ops} = do parsedOps <- traverse (pbToOp s) $ PB.getField ops fromStack parsedOps -expressionToPb :: ReverseSymbols -> Expression -> PB.ExpressionV2 +expressionToPb :: ReverseSymbols -> Expression -> PB.Expression expressionToPb s e = let ops = opToPb s <$> toStack e - in PB.ExpressionV2 { ops = PB.putField ops } + in PB.Expression { ops = PB.putField ops } pbToOp :: Symbols -> PB.Op -> Either String Op pbToOp s = \case @@ -482,7 +482,7 @@ pbToBinary s PB.OpBinary{kind, ffiName} = PB.All -> Right All PB.Any -> Right Any PB.Get -> Right Get - PB.Try -> Right Try + PB.TryOr -> Right Try PB.BinaryFfi -> do nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName name' <- getSymbol s $ SymbolRef nameIdx @@ -530,7 +530,7 @@ binaryToPb s = \case Any -> PB.OpBinary { kind = PB.putField PB.Any, ffiName = PB.putField Nothing } All -> PB.OpBinary { kind = PB.putField PB.All, ffiName = PB.putField Nothing } Get -> PB.OpBinary { kind = PB.putField PB.Get, ffiName = PB.putField Nothing } - Try -> PB.OpBinary { kind = PB.putField PB.Try, ffiName = PB.putField Nothing } + Try -> PB.OpBinary { kind = PB.putField PB.TryOr, ffiName = PB.putField Nothing } BinaryFfi n -> PB.OpBinary { kind = PB.putField PB.BinaryFfi , ffiName = PB.putField . Just . getSymbolRef $ getSymbolCode s n From bcc4ee21f7b731af2ec29925fbf8057271073690 Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 21:38:51 +0200 Subject: [PATCH 2/6] fix: reject non-ffi unary/binary pb operations with a defined ffi name --- biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs | 85 +++++++++++---------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs b/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs index 9d604c4..9fd6724 100644 --- a/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs +++ b/biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs @@ -429,15 +429,19 @@ opToPb s = \case COp p os -> PB.OpVClosure $ PB.putField $ closureToPb s p os pbToUnary :: Symbols -> PB.OpUnary -> Either String Unary -pbToUnary s PB.OpUnary{kind,ffiName} = case PB.getField kind of - PB.Negate -> Right Negate - PB.Parens -> Right Parens - PB.Length -> Right Length - PB.TypeOf -> Right TypeOf - PB.UnaryFfi -> do - nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName - name' <- getSymbol s $ SymbolRef nameIdx - pure $ UnaryFfi name' +pbToUnary s PB.OpUnary{kind,ffiName} = + let noFfi = case PB.getField ffiName of + Just _ -> const $ Left "FFI named set on a regular operation " + Nothing -> Right + in case PB.getField kind of + PB.Negate -> noFfi Negate + PB.Parens -> noFfi Parens + PB.Length -> noFfi Length + PB.TypeOf -> noFfi TypeOf + PB.UnaryFfi -> do + nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName + name' <- getSymbol s $ SymbolRef nameIdx + pure $ UnaryFfi name' unaryToPb :: ReverseSymbols -> Unary -> PB.OpUnary unaryToPb s = \case @@ -453,36 +457,39 @@ unaryToPb s = \case pbToBinary :: Symbols -> PB.OpBinary -> Either String Binary pbToBinary s PB.OpBinary{kind, ffiName} = - case PB.getField kind of - PB.LessThan -> Right LessThan - PB.GreaterThan -> Right GreaterThan - PB.LessOrEqual -> Right LessOrEqual - PB.GreaterOrEqual -> Right GreaterOrEqual - PB.Equal -> Right Equal - PB.Contains -> Right Contains - PB.Prefix -> Right Prefix - PB.Suffix -> Right Suffix - PB.Regex -> Right Regex - PB.Add -> Right Add - PB.Sub -> Right Sub - PB.Mul -> Right Mul - PB.Div -> Right Div - PB.And -> Right And - PB.Or -> Right Or - PB.Intersection -> Right Intersection - PB.Union -> Right Union - PB.BitwiseAnd -> Right BitwiseAnd - PB.BitwiseOr -> Right BitwiseOr - PB.BitwiseXor -> Right BitwiseXor - PB.NotEqual -> Right NotEqual - PB.HeterogeneousEqual -> Right HeterogeneousEqual - PB.HeterogeneousNotEqual -> Right HeterogeneousNotEqual - PB.LazyAnd -> Right LazyAnd - PB.LazyOr -> Right LazyOr - PB.All -> Right All - PB.Any -> Right Any - PB.Get -> Right Get - PB.TryOr -> Right Try + let noFfi = case PB.getField ffiName of + Just _ -> const $ Left "FFI named set on a regular operation " + Nothing -> Right + in case PB.getField kind of + PB.LessThan -> noFfi LessThan + PB.GreaterThan -> noFfi GreaterThan + PB.LessOrEqual -> noFfi LessOrEqual + PB.GreaterOrEqual -> noFfi GreaterOrEqual + PB.Equal -> noFfi Equal + PB.Contains -> noFfi Contains + PB.Prefix -> noFfi Prefix + PB.Suffix -> noFfi Suffix + PB.Regex -> noFfi Regex + PB.Add -> noFfi Add + PB.Sub -> noFfi Sub + PB.Mul -> noFfi Mul + PB.Div -> noFfi Div + PB.And -> noFfi And + PB.Or -> noFfi Or + PB.Intersection -> noFfi Intersection + PB.Union -> noFfi Union + PB.BitwiseAnd -> noFfi BitwiseAnd + PB.BitwiseOr -> noFfi BitwiseOr + PB.BitwiseXor -> noFfi BitwiseXor + PB.NotEqual -> noFfi NotEqual + PB.HeterogeneousEqual -> noFfi HeterogeneousEqual + PB.HeterogeneousNotEqual -> noFfi HeterogeneousNotEqual + PB.LazyAnd -> noFfi LazyAnd + PB.LazyOr -> noFfi LazyOr + PB.All -> noFfi All + PB.Any -> noFfi Any + PB.Get -> noFfi Get + PB.TryOr -> noFfi Try PB.BinaryFfi -> do nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName name' <- getSymbol s $ SymbolRef nameIdx From f06785e8fa15ac935d2945210d71c8c71a40d65a Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 22:03:31 +0200 Subject: [PATCH 3/6] update readmes --- README.md | 9 ++++++--- biscuit-servant/README.md | 2 +- biscuit-wai/README.md | 21 +++++++++++++++++++++ biscuit/README.md | 10 ++++++---- 4 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 biscuit-wai/README.md diff --git a/README.md b/README.md index 03c4ffa..6b6e64e 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,18 @@ You will find below the main lib and its companions: * [biscuit](./biscuit/) — Main library, providing minting and signature verification of biscuit tokens, as well as a datalog engine allowing to compute the validity of a token in a given context * [biscuit-servant](./biscuit-servant) — Servant combinators, for a smooth integration in your API +* [biscuit-wai](./biscuit-wai) — WAI middlewares for protecting WAI applications with biscuits ## Supported biscuit versions -The core library supports [`v3` and `v4` biscuits][spec] (both open and sealed). +The core library supports [`v3.0` to `v3.3` biscuits][spec] (both open and sealed). + +Only `ed25519` signatures are supported. `p256r1` signatures are not supported yet. [CI-badge]: https://img.shields.io/github/actions/workflow/status/biscuit-auth/biscuit-haskell/github-actions.yml?style=flat-square&branch=main -[CI-url]: https://github.com/biscuit-auth/biscuit-haskell/actions +[CI-url]: https://github.com/eclipse-biscuit/biscuit-haskell/actions [Hackage]: https://img.shields.io/hackage/v/biscuit-haskell?color=purple&style=flat-square [hackage-url]: https://hackage.haskell.org/package/biscuit-haskell [gcouprie]: https://github.com/geal [biscuit]: https://biscuitsec.org -[spec]: https://github.com/biscuit-auth/biscuit/blob/master/SPECIFICATIONS.md +[spec]: https://github.com/eclipse-biscuit/biscuit/blob/main/SPECIFICATIONS.md diff --git a/biscuit-servant/README.md b/biscuit-servant/README.md index cc7c090..da78450 100644 --- a/biscuit-servant/README.md +++ b/biscuit-servant/README.md @@ -6,7 +6,7 @@ ## Usage -```Haskell +```haskell type AppM = WithAuthorizer Handler type API = RequireBiscuit :> ProtectedAPI diff --git a/biscuit-wai/README.md b/biscuit-wai/README.md new file mode 100644 index 0000000..e3e9955 --- /dev/null +++ b/biscuit-wai/README.md @@ -0,0 +1,21 @@ + + +# biscuit-wai [![Hackage][hackage]][hackage-url] + +> **WAI middlewares to enable biscuit validation in your WAI applications** + +## Usage + +```haskell +import Network.WAI (Application) +import Network.Wai.Middleware.Biscuit (parseBiscuit, getBiscuit) +import Auth.Biscuit (PublicKey) + +app :: PublicKey -> Application +app publicKey req respond = parseBiscuit publicKey $ do + let verifiedBiscuit = getBiscuit req + in error "TODO: authorize biscuit and return a response" +``` + +[Hackage]: https://img.shields.io/hackage/v/biscuit-wai?color=purple&style=flat-square +[hackage-url]: https://hackage.haskell.org/package/biscuit-wai diff --git a/biscuit/README.md b/biscuit/README.md index 5fc89c2..c1a9489 100644 --- a/biscuit/README.md +++ b/biscuit/README.md @@ -6,7 +6,9 @@ Main library for biscuit tokens support, providing minting and signature verific ## Supported biscuit versions -The core library supports [`v2` biscuits][v2spec] (both open and sealed). +The core library supports [`v3.0` to `v3.3` biscuits][spec] (both open and sealed). + +Only `ed25519` signatures are supported. `p256r1` signatures are not supported yet. ## How to use this library @@ -103,7 +105,7 @@ creation = do [gcouprie]: https://github.com/geal [biscuit]: https://www.clever-cloud.com/blog/engineering/2021/04/12/introduction-to-biscuit/ [biscuittutorial]: https://www.clever-cloud.com/blog/engineering/2021/04/15/biscuit-tutorial/ -[v2spec]: https://github.com/CleverCloud/biscuit/blob/2.0/SPECIFICATIONS.md +[spec]: https://github.com/eclipse-biscuit/biscuit/blob/main/SPECIFICATIONS.md [quasiquotes]: https://wiki.haskell.org/Quasiquotation -[biscuitexample]: https://github.com/biscuit-auth/biscuit-haskell/blob/main/biscuit/src/Auth/Biscuit/Example.hs -[packagedoc]: https://hackage.haskell.org/package/biscuit-haskell-0.1.0.0/docs/Auth-Biscuit.html +[biscuitexample]: https://github.com/eclipse-biscuit/biscuit-haskell/blob/main/biscuit/src/Auth/Biscuit/Example.hs +[packagedoc]: https://hackage.haskell.org/package/biscuit-haskell-0.4.0.0/docs/Auth-Biscuit.html From 21a3e4c9b1746e8a2f6fe7986e5432d998472901 Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 22:08:51 +0200 Subject: [PATCH 4/6] Update changelogs --- biscuit-servant/ChangeLog.md | 4 ++++ biscuit/ChangeLog.md | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/biscuit-servant/ChangeLog.md b/biscuit-servant/ChangeLog.md index e200d7c..cd21164 100644 --- a/biscuit-servant/ChangeLog.md +++ b/biscuit-servant/ChangeLog.md @@ -1,5 +1,9 @@ # Changelog for biscuit-servant +## 0.5.0.0 (not released yet) + +- use biscuit-haskell 0.5.0.0 + ## 0.4.0.0 - use biscuit-haskell 0.4.0.0 diff --git a/biscuit/ChangeLog.md b/biscuit/ChangeLog.md index af68a58..273149a 100644 --- a/biscuit/ChangeLog.md +++ b/biscuit/ChangeLog.md @@ -1,5 +1,12 @@ # Changelog for biscuit-haskell +## 0.5.0.0 (not released yet) + +- biscuit v3.3 support +- drop support for GHC <9.6 +- support for GHC 9.6, 9.8, 9.10, 9.12 +- dependencies update (including removal of cryptonite in favor of crypton) + ## 0.4.0.0 - abort authorization on evaluation error as mandated by the spec From fefc7eeec3a7a92f2f02309e1d80ad7abe676d6b Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 22:13:11 +0200 Subject: [PATCH 5/6] Add `CONTRIBUTING.md` --- CONTRIBUTING.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6fc08d7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing + +This repository is part of the [eclipse-biscuit](https://projects.eclipse.org/projects/technology.biscuit) project. It is licensed under `BSD-3-Clause` and requires contributors to sign the [Eclipse Contributor Agreement](https://www.eclipse.org/legal/ECA.php) (see #eclipse-contributor-agreement below). + +## Before contributing + +This is an implementation of the biscuit specification. All changes pertaining to the specification must be discussed in the [specification repo](https://github.com/eclipse-biscuit/biscuit) first. + +Unless you’re fixing a trivial issue, it is a good idea to start by opening an issue, in order to gather feedback about the changes you have in mind. + +You can also ask questions on the [implementers matrix chat](https://matrix.to/#/!suybZZpOqChdTNuIVA:matrix.org). + +## Code organization + +- `biscuit` provides core support for biscuit-auth +- `biscuit-servant` provides support for protecting servant applications +- `biscuit-wai` provides WAI middlewares for protecting WAI applications + +## Project communication + +The discussion space is a [matrix chat](https://matrix.to/#/!MXwhyfCFLLCfHSYJxg:matrix.org). +Updates are also posted on [bluesky](https://bsky.app/profile/biscuitsec.org) and [mastodon](https://hachyderm.io/@biscuitauth). + +Security issues can be reported via [github](https://github.com/eclipse-biscuit/biscuit-haskell/security). + +## Eclipse Contributor Agreement + +From the [ECA FAQ](https://www.eclipse.org/legal/eca/faq/): + +The purpose of the ECA is to provide a written record that you have agreed to provide your contributions of code and documentation under the licenses used by the Eclipse project(s) you're contributing to. It also makes it clear that you are promising that what you are contributing to Eclipse is code that you wrote, and you have the necessary rights to contribute it to our projects. And finally, it documents a commitment from you that your open source contributions will be permanently on the public record. + +In order to sign the ECA, you can follow these steps: + +- [Create an account](https://dev.eclipse.org/site_login/createaccount.php) on dev.eclipse.org +- Open your [Account Settings tab](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings), enter your GitHub ID and click Update Account +- Read and [sign the ECA](https://dev.eclipse.org/site_login/myaccount.php#open_tab_cla) +- Use the exact same email address for your Eclipse account and your commit author. From 69cb1146fb87eaf804c05cfe02e831a37b8e290d Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Mon, 7 Jul 2025 22:18:07 +0200 Subject: [PATCH 6/6] fix: remove unneeded test dependency --- biscuit/biscuit-haskell.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/biscuit/biscuit-haskell.cabal b/biscuit/biscuit-haskell.cabal index 60e1dcd..5bfe74a 100644 --- a/biscuit/biscuit-haskell.cabal +++ b/biscuit/biscuit-haskell.cabal @@ -96,7 +96,6 @@ test-suite biscuit-haskell-test , bytestring , cereal , containers - , cryptonite , lens , lens-aeson , megaparsec