packages feed

biscuit-haskell 0.3.0.0 → 0.3.0.1

raw patch · 16 files changed

+229/−58 lines, 16 filesdep ~base16dep ~megaparsecdep ~mtlbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base16, megaparsec, mtl, template-haskell, validation-selective

API changes (from Hackage documentation)

- Auth.Biscuit.Datalog.AST: instance Auth.Biscuit.Datalog.AST.FromValue Data.ByteString.Internal.ByteString
- Auth.Biscuit.Datalog.AST: instance Auth.Biscuit.Datalog.AST.ToTerm Data.ByteString.Internal.ByteString inSet pof
- Auth.Biscuit.Datalog.Parser: methodParser :: Parser (Expression' 'WithSlices)
+ Auth.Biscuit.Datalog.AST: NotEqual :: Binary
+ Auth.Biscuit.Datalog.AST: instance Auth.Biscuit.Datalog.AST.FromValue Data.ByteString.Internal.Type.ByteString
+ Auth.Biscuit.Datalog.AST: instance Auth.Biscuit.Datalog.AST.ToTerm Data.ByteString.Internal.Type.ByteString inSet pof
+ Auth.Biscuit.Datalog.Parser: intParser :: Parser Int64
+ Auth.Biscuit.Datalog.Parser: methodsParser :: Parser (Expression' 'WithSlices)
+ Auth.Biscuit.Proto: NotEqual :: BinaryKind
+ Auth.Biscuit.Utils: decodeHex :: ByteString -> Either Text ByteString
+ Auth.Biscuit.Utils: encodeHex :: ByteString -> Text
+ Auth.Biscuit.Utils: encodeHex' :: ByteString -> ByteString
- Auth.Biscuit: LInteger :: Int -> Term' (inSet :: IsWithinSet) (pof :: PredicateOrFact) (ctx :: DatalogContext)
+ Auth.Biscuit: LInteger :: Int64 -> Term' (inSet :: IsWithinSet) (pof :: PredicateOrFact) (ctx :: DatalogContext)
- Auth.Biscuit.Datalog.AST: LInteger :: Int -> Term' (inSet :: IsWithinSet) (pof :: PredicateOrFact) (ctx :: DatalogContext)
+ Auth.Biscuit.Datalog.AST: LInteger :: Int64 -> Term' (inSet :: IsWithinSet) (pof :: PredicateOrFact) (ctx :: DatalogContext)
- Auth.Biscuit.Datalog.Parser: binaryMethodParser :: Parser (Expression' 'WithSlices)
+ Auth.Biscuit.Datalog.Parser: binaryMethodParser :: Parser (Expression' 'WithSlices -> Expression' 'WithSlices)
- Auth.Biscuit.Datalog.Parser: unaryMethodParser :: Parser (Expression' 'WithSlices)
+ Auth.Biscuit.Datalog.Parser: unaryMethodParser :: Parser (Expression' 'WithSlices -> Expression' 'WithSlices)

Files

biscuit-haskell.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0  name:           biscuit-haskell-version:        0.3.0.0+version:        0.3.0.1 category:       Security synopsis:       Library support for the Biscuit security token description:    Please see the README on GitHub at <https://github.com/biscuit-auth/biscuit-haskell#readme>@@ -13,7 +13,7 @@ license:        BSD3 license-file:   LICENSE build-type:     Simple-tested-with:    GHC ==8.10.7 || == 9.0.2 || ==9.2.4+tested-with:    GHC ==9.0.2 || ==9.2.4 || ==9.6.5 || ==9.8.2 extra-source-files:     README.md     ChangeLog.md@@ -49,24 +49,24 @@   build-depends:     base                 >= 4.7 && <5,     async                ^>= 2.2,-    base16               ^>= 0.3,+    base16               >= 0.3 && <2.0,     bytestring           >= 0.10 && <0.12,     text                 >= 1.2 && <3,     containers           ^>= 0.6,     cryptonite           >= 0.27 && < 0.31,     memory               >= 0.15 && < 0.19,-    template-haskell     >= 2.16 && < 2.19,+    template-haskell     >= 2.16 && < 2.22,     base64               ^>= 0.4,     cereal               ^>= 0.5,-    mtl                  ^>= 2.2,+    mtl                  >= 2.2 && < 2.4,     parser-combinators   >= 1.2 && < 1.4,     protobuf             ^>= 0.2,     random               >= 1.0 && < 1.3,     regex-tdfa           ^>= 1.3,     th-lift-instances    ^>= 0.1,     time                 ^>= 1.9,-    validation-selective ^>= 0.1,-    megaparsec           ^>= 9.2+    validation-selective >= 0.1 && < 0.3,+    megaparsec           >= 9.2 && < 9.7   default-language: Haskell2010  test-suite biscuit-haskell-test@@ -89,7 +89,7 @@       async     , aeson     , base >=4.7 && <5-    , base16 ^>=0.3+    , base16 >=0.3 && <2.0     , base64     , biscuit-haskell     , bytestring
src/Auth/Biscuit.hs view
@@ -108,7 +108,6 @@ import           Control.Monad.Identity              (runIdentity) import           Data.Bifunctor                      (first) import           Data.ByteString                     (ByteString)-import qualified Data.ByteString.Base16              as Hex import qualified Data.ByteString.Base64.URL          as B64 import           Data.Foldable                       (toList) import           Data.Set                            (Set)@@ -159,6 +158,7 @@                                                       queryAuthorizerFacts,                                                       queryRawBiscuitFacts,                                                       seal, serializeBiscuit)+import Auth.Biscuit.Utils                            (decodeHex, encodeHex') import qualified Data.Text                           as Text  @@ -249,7 +249,7 @@  -- | Decode a base16-encoded bytestring, reporting errors via `MonadFail` fromHex :: MonadFail m => ByteString -> m ByteString-fromHex = either (fail . Text.unpack) pure . Hex.decodeBase16+fromHex = either (fail . Text.unpack) pure . decodeHex  -- $keypairs --@@ -273,11 +273,11 @@  -- | Serialize a 'SecretKey' to a hex-encoded bytestring serializeSecretKeyHex :: SecretKey -> ByteString-serializeSecretKeyHex = Hex.encodeBase16' . skBytes+serializeSecretKeyHex = encodeHex' . skBytes  -- | Serialize a 'PublicKey' to a hex-encoded bytestring serializePublicKeyHex :: PublicKey -> ByteString-serializePublicKeyHex = Hex.encodeBase16' . pkBytes+serializePublicKeyHex = encodeHex' . pkBytes  -- | Read a 'SecretKey' from raw bytes parseSecretKey :: ByteString -> Maybe SecretKey
src/Auth/Biscuit/Datalog/AST.hs view
@@ -112,9 +112,9 @@ import           Control.Applicative        ((<|>)) import           Control.Monad              ((<=<)) import           Data.ByteString            (ByteString)-import           Data.ByteString.Base16     as Hex import           Data.Foldable              (fold, toList) import           Data.Function              (on)+import           Data.Int                   (Int64) import           Data.List.NonEmpty         (NonEmpty, nonEmpty) import           Data.Map.Strict            (Map) import qualified Data.Map.Strict            as Map@@ -133,6 +133,7 @@ import           Validation                 (Validation (..), failure)  import           Auth.Biscuit.Crypto        (PublicKey, pkBytes)+import           Auth.Biscuit.Utils         (encodeHex)  data IsWithinSet = NotWithinSet | WithinSet data DatalogContext@@ -197,7 +198,7 @@ data Term' (inSet :: IsWithinSet) (pof :: PredicateOrFact) (ctx :: DatalogContext) =     Variable (VariableType inSet pof)   -- ^ A variable (eg. @$0@)-  | LInteger Int+  | LInteger Int64   -- ^ An integer literal (eg. @42@)   | LString Text   -- ^ A string literal (eg. @"file1"@)@@ -267,10 +268,10 @@   fromValue :: Value -> Maybe t  instance ToTerm Int inSet pof where-  toTerm = LInteger+  toTerm = LInteger . fromIntegral  instance FromValue Int where-  fromValue (LInteger v) = Just v+  fromValue (LInteger v) = Just $ fromIntegral v   fromValue _            = Nothing  instance ToTerm Integer inSet pof where@@ -346,7 +347,7 @@   LInteger int  -> pack $ show int   LString str   -> pack $ show str   LDate time    -> pack $ formatTime defaultTimeLocale "%FT%T%Q%Ez" time-  LBytes bs     -> "hex:" <> Hex.encodeBase16 bs+  LBytes bs     -> "hex:" <> encodeHex bs   LBool True    -> "true"   LBool False   -> "false"   TermSet terms -> set terms@@ -595,6 +596,7 @@   EBinary BitwiseAnd _ _ -> False   EBinary BitwiseOr _ _  -> False   EBinary BitwiseXor _ _ -> False+  EBinary NotEqual   _ _ -> False   EBinary _ l r -> expressionHasNoV4Operators l && expressionHasNoV4Operators r   _ -> True @@ -677,6 +679,7 @@   | BitwiseAnd   | BitwiseOr   | BitwiseXor+  | NotEqual   deriving (Eq, Ord, Show, Lift)  data Expression' (ctx :: DatalogContext) =@@ -758,6 +761,7 @@         EBinary BitwiseAnd e e'     -> rOp "&" e e'         EBinary BitwiseOr e e'      -> rOp "|" e e'         EBinary BitwiseXor e e'     -> rOp "^" e e'+        EBinary NotEqual e e'       -> rOp "!=" e e'  -- | A biscuit block, containing facts, rules and checks. --@@ -820,7 +824,7 @@   let renderScopeElem = \case         OnlyAuthority -> "authority"         Previous      -> "previous"-        BlockId bs    -> "ed25519/" <> Hex.encodeBase16 (pkBytes bs)+        BlockId bs    -> "ed25519/" <> encodeHex (pkBytes bs)    in intercalate ", " . Set.toList . Set.map renderScopeElem  renderBlock :: Block -> Text
src/Auth/Biscuit/Datalog/Executor.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE NamedFieldPuns             #-} {-# LANGUAGE OverloadedStrings          #-} {-# LANGUAGE TupleSections              #-}+{-# LANGUAGE TypeApplications           #-} {-|   Module      : Auth.Biscuit.Datalog.Executor   Copyright   : © Clément Delafargue, 2021@@ -42,6 +43,7 @@ import qualified Data.ByteString          as ByteString import           Data.Foldable            (fold) import           Data.Functor.Compose     (Compose (..))+import           Data.Int                 (Int64) import           Data.List.NonEmpty       (NonEmpty) import qualified Data.List.NonEmpty       as NE import           Data.Map.Strict          (Map, (!?))@@ -370,9 +372,9 @@ evalUnary Parens t = pure t evalUnary Negate (LBool b) = pure (LBool $ not b) evalUnary Negate _ = Left "Only booleans support negation"-evalUnary Length (LString t) = pure . LInteger $ Text.length t-evalUnary Length (LBytes bs) = pure . LInteger $ ByteString.length bs-evalUnary Length (TermSet s) = pure . LInteger $ Set.size s+evalUnary Length (LString t) = pure . LInteger . fromIntegral $ Text.length t+evalUnary Length (LBytes bs) = pure . LInteger . fromIntegral $ ByteString.length bs+evalUnary Length (TermSet s) = pure . LInteger . fromIntegral $ Set.size s evalUnary Length _ = Left "Only strings, bytes and sets support `.length()`"  evalBinary :: Limits -> Binary -> Value -> Value -> Either String Value@@ -384,6 +386,13 @@ evalBinary _ Equal (LBool t) (LBool t')       = pure $ LBool (t == t') evalBinary _ Equal (TermSet t) (TermSet t')   = pure $ LBool (t == t') evalBinary _ Equal _ _                        = Left "Equality mismatch"+evalBinary _ NotEqual (LInteger i) (LInteger i') = pure $ LBool (i /= i')+evalBinary _ NotEqual (LString t) (LString t')   = pure $ LBool (t /= t')+evalBinary _ NotEqual (LDate t) (LDate t')       = pure $ LBool (t /= t')+evalBinary _ NotEqual (LBytes t) (LBytes t')     = pure $ LBool (t /= t')+evalBinary _ NotEqual (LBool t) (LBool t')       = pure $ LBool (t /= t')+evalBinary _ NotEqual (TermSet t) (TermSet t')   = pure $ LBool (t /= t')+evalBinary _ NotEqual _ _                        = Left "Inequity mismatch" evalBinary _ LessThan (LInteger i) (LInteger i') = pure $ LBool (i < i') evalBinary _ LessThan (LDate t) (LDate t')       = pure $ LBool (t < t') evalBinary _ LessThan _ _                        = Left "< mismatch"@@ -405,15 +414,15 @@                                                                | otherwise    = Left "Regex evaluation is disabled" evalBinary _ Regex _ _                       = Left "Only strings support `.matches()`" -- num operations-evalBinary _ Add (LInteger i) (LInteger i') = pure $ LInteger (i + i')+evalBinary _ Add (LInteger i) (LInteger i') = LInteger <$> checkedOp (+) i i' evalBinary _ Add (LString t) (LString t') = pure $ LString (t <> t') evalBinary _ Add _ _ = Left "Only integers and strings support addition"-evalBinary _ Sub (LInteger i) (LInteger i') = pure $ LInteger (i - i')+evalBinary _ Sub (LInteger i) (LInteger i') = LInteger <$> checkedOp (-) i i' evalBinary _ Sub _ _ = Left "Only integers support subtraction"-evalBinary _ Mul (LInteger i) (LInteger i') = pure $ LInteger (i * i')+evalBinary _ Mul (LInteger i) (LInteger i') = LInteger <$> checkedOp (*) i i' evalBinary _ Mul _ _ = Left "Only integers support multiplication" evalBinary _ Div (LInteger _) (LInteger 0) = Left "Divide by 0"-evalBinary _ Div (LInteger i) (LInteger i') = pure $ LInteger (i `div` i')+evalBinary _ Div (LInteger i) (LInteger i') = LInteger <$> checkedOp div i i' evalBinary _ Div _ _ = Left "Only integers support division" -- bitwise operations evalBinary _ BitwiseAnd (LInteger i) (LInteger i') = pure $ LInteger (i .&. i')@@ -438,6 +447,17 @@ evalBinary _ Intersection _ _ = Left "Only sets support `.intersection()`" evalBinary _ Union (TermSet t) (TermSet t') = pure $ TermSet (Set.union t t') evalBinary _ Union _ _ = Left "Only sets support `.union()`"++checkedOp :: (Integer -> Integer -> Integer)+          -> Int64 -> Int64+          -> Either String Int64+checkedOp f a b =+  let result = f (fromIntegral a) (fromIntegral b)+   in if result < fromIntegral (minBound @Int64)+      then Left "integer underflow"+      else if result > fromIntegral (maxBound @Int64)+      then Left "integer overflow"+      else Right (fromIntegral result)  regexMatch :: Text -> Text -> Either String Value regexMatch text regexT = do
src/Auth/Biscuit/Datalog/Parser.hs view
@@ -15,14 +15,16 @@ import           Auth.Biscuit.Crypto            (PublicKey,                                                  readEd25519PublicKey) import           Auth.Biscuit.Datalog.AST+import           Auth.Biscuit.Utils             (decodeHex) import           Control.Monad                  (join) import qualified Control.Monad.Combinators.Expr as Expr import           Data.Bifunctor import           Data.ByteString                (ByteString)-import           Data.ByteString.Base16         as Hex import qualified Data.ByteString.Char8          as C8 import           Data.Char import           Data.Either                    (partitionEithers)+import           Data.Function                  ((&))+import           Data.Int                       (Int64) import           Data.List.NonEmpty             (NonEmpty) import qualified Data.List.NonEmpty             as NE import           Data.Map.Strict                (Map)@@ -128,7 +130,7 @@   , TermSet <$> parseSet <?> "set (eg. [1,2,3])"   , LBytes <$> (chunk "hex:" *> hexParser) <?> "hex-encoded bytestring (eg. hex:00ff99)"   , LDate <$> rfc3339DateParser <?> "RFC3339-formatted timestamp (eg. 2022-11-29T00:00:00Z)"-  , LInteger <$> L.signed C.space L.decimal <?> "(signed) integer"+  , LInteger <$> intParser <?> "(signed) integer"   , LString . T.pack <$> (C.char '"' *> manyTill L.charLiteral (C.char '"')) <?> "string literal"   , LBool <$> choice [ True <$ chunk "true"                      , False <$ chunk "false"@@ -136,17 +138,25 @@           <?> "boolean value (eg. true or false)"   ] +intParser :: Parser Int64+intParser = do+  integer :: Integer <- L.signed C.space L.decimal <?> "(signed) integer"+  if integer < fromIntegral (minBound @Int64)+     || integer > fromIntegral (maxBound @Int64)+  then fail "integer literals must fit in the int64 range"+  else pure $ fromIntegral integer+ hexParser :: Parser ByteString hexParser = do   (sp, hexStr) <- getSpan $ C8.pack <$> some C.hexDigitChar-  case Hex.decodeBase16 hexStr of+  case decodeHex hexStr of     Left e   -> registerError (InvalidBs e) sp     Right bs -> pure bs  publicKeyParser :: Parser PublicKey publicKeyParser = do   (sp, hexStr) <- getSpan $ C8.pack <$> (chunk "ed25519/" *> some C.hexDigitChar)-  case Hex.decodeBase16 hexStr of+  case decodeHex hexStr of     Left e -> registerError (InvalidPublicKey e) sp     Right bs -> case readEd25519PublicKey bs of       Nothing -> registerError (InvalidPublicKey "Invalid ed25519 public key") sp@@ -218,8 +228,7 @@  expressionParser :: Parser (Expression' 'WithSlices) expressionParser =-  let base = choice [ try binaryMethodParser-                    , try unaryMethodParser+  let base = choice [ try methodsParser                     , exprTerm                     ]    in Expr.makeExprParser base table@@ -246,14 +255,14 @@         , infixN  "<"  LessThan         , infixN  ">"  GreaterThan         , infixN  "==" Equal+        , infixN  "!=" NotEqual         ]       , [ infixL "&&" And ]       , [ infixL "||" Or ]       ] -binaryMethodParser :: Parser (Expression' 'WithSlices)+binaryMethodParser :: Parser (Expression' 'WithSlices -> Expression' 'WithSlices) binaryMethodParser = do-  e1 <- exprTerm   _ <- C.char '.'   method <- choice     [ Contains     <$ chunk "contains"@@ -266,18 +275,20 @@   _ <- l $ C.char '('   e2 <- l expressionParser   _ <- l $ C.char ')'-  pure $ EBinary method e1 e2+  pure $ \e1 -> EBinary method e1 e2 -unaryMethodParser :: Parser (Expression' 'WithSlices)+unaryMethodParser :: Parser (Expression' 'WithSlices -> Expression' 'WithSlices) unaryMethodParser = do-  e1 <- exprTerm   _ <- C.char '.'   method <- Length <$ chunk "length"   _ <- l $ chunk "()"-  pure $ EUnary method e1+  pure $ EUnary method -methodParser :: Parser (Expression' 'WithSlices)-methodParser = binaryMethodParser <|> unaryMethodParser+methodsParser :: Parser (Expression' 'WithSlices)+methodsParser = do+  e1 <- exprTerm+  methods <- some (try binaryMethodParser <|> unaryMethodParser)+  pure $ foldl (&) e1 methods  unaryParens :: Parser (Expression' 'WithSlices) unaryParens = do
src/Auth/Biscuit/Datalog/ScopedExecutor.hs view
@@ -30,7 +30,7 @@                                                 gets, lift, put) import           Data.Bifunctor                (first) import           Data.ByteString               (ByteString)-import           Data.Foldable                 (fold, traverse_)+import           Data.Foldable                 (traverse_) import           Data.List                     (genericLength) import           Data.List.NonEmpty            (NonEmpty) import qualified Data.List.NonEmpty            as NE@@ -146,7 +146,7 @@       firstBlock = fst' authority       otherBlocks = fst' <$> blocks       allBlocks = zip [0..] (firstBlock : otherBlocks) <> [(sBlockCount, vBlock authorizer)]-      (sRules, sFacts) = revocationWorld <> fold (uncurry collectWorld . fmap (toEvaluation externalKeys) <$> allBlocks)+      (sRules, sFacts) = revocationWorld <> foldMap (uncurry collectWorld . fmap (toEvaluation externalKeys)) allBlocks    in ComputeState         { sLimits = limits         , sRules
src/Auth/Biscuit/Proto.hs view
@@ -208,6 +208,7 @@   | BitwiseAnd   | BitwiseOr   | BitwiseXor+  | NotEqual   deriving stock (Show, Enum, Bounded)  newtype OpBinary = OpBinary
src/Auth/Biscuit/ProtoBufAdapter.hs view
@@ -389,6 +389,7 @@   PB.BitwiseAnd     -> BitwiseAnd   PB.BitwiseOr      -> BitwiseOr   PB.BitwiseXor     -> BitwiseXor+  PB.NotEqual       -> NotEqual  binaryToPb :: Binary -> PB.OpBinary binaryToPb = PB.OpBinary . PB.putField . \case@@ -412,7 +413,7 @@   BitwiseAnd     -> PB.BitwiseAnd   BitwiseOr      -> PB.BitwiseOr   BitwiseXor     -> PB.BitwiseXor-+  NotEqual       -> PB.NotEqual  pbToThirdPartyBlockRequest :: PB.ThirdPartyBlockRequest -> Either String (Crypto.PublicKey, [Crypto.PublicKey]) pbToThirdPartyBlockRequest PB.ThirdPartyBlockRequest{previousPk, pkTable} = do
src/Auth/Biscuit/Token.hs view
@@ -66,7 +66,6 @@ import           Data.Bifunctor                      (first) import           Data.ByteString                     (ByteString) import qualified Data.ByteString.Base64.URL          as B64-import           Data.Foldable                       (fold) import           Data.List.NonEmpty                  (NonEmpty ((:|))) import qualified Data.List.NonEmpty                  as NE import           Data.Set                            (Set)@@ -221,7 +220,7 @@   let ePks = externalKeys b       getBlock ((_, block), _, _, _) = block       allBlocks = zip [0..] $ getBlock <$> authority : blocks-      (_, sFacts) = fold (uncurry collectWorld . fmap (toEvaluation ePks) <$> allBlocks)+      (_, sFacts) = foldMap (uncurry collectWorld . fmap (toEvaluation ePks)) allBlocks    in queryAvailableFacts ePks sFacts  -- | Query the facts generated by the authority and authorizer blocks
src/Auth/Biscuit/Utils.hs view
@@ -1,13 +1,46 @@-{-|-  Module      : Auth.Biscuit.Utils-  Copyright   : © Clément Delafargue, 2021-  License     : MIT-  Maintainer  : clement@delafargue.name--}+{-# LANGUAGE CPP #-}++-- |+--  Module      : Auth.Biscuit.Utils+--  Copyright   : © Clément Delafargue, 2021+--  License     : MIT+--  Maintainer  : clement@delafargue.name module Auth.Biscuit.Utils-  ( maybeToRight-  , rightToMaybe-  ) where+  ( maybeToRight,+    rightToMaybe,+    encodeHex,+    encodeHex',+    decodeHex,+  )+where++#if MIN_VERSION_base16(1,0,0)+import qualified Data.Base16.Types as Hex+#endif+import Data.ByteString (ByteString)+import qualified Data.ByteString.Base16 as Hex+import Data.Text (Text)++encodeHex :: ByteString -> Text+#if MIN_VERSION_base16(1,0,0)+encodeHex = Hex.extractBase16 . Hex.encodeBase16+#else+encodeHex = Hex.encodeBase16+#endif++encodeHex' :: ByteString -> ByteString+#if MIN_VERSION_base16(1,0,0)+encodeHex' = Hex.extractBase16 . Hex.encodeBase16'+#else+encodeHex' = Hex.encodeBase16'+#endif++decodeHex :: ByteString -> Either Text ByteString+#if MIN_VERSION_base16(1,0,0)+decodeHex = Hex.decodeBase16Untyped+#else+decodeHex = Hex.decodeBase16+#endif  -- | Exactly like `maybeToRight` from the `either` package, -- but without the dependency footprint
test/Spec/Executor.hs view
@@ -178,6 +178,8 @@   testGroup "Expressions evaluation (expected errors)" $ evalFail <$>     [ ("1 / 0", "Divide by 0")     , ("\"toto\".matches(\"to\")", "Regex evaluation is disabled")+    , ("9223372036854775807 + 1", "integer overflow")+    , ("-9223372036854775808 - 1", "integer underflow")     ]  rulesWithConstraints :: TestTree
test/Spec/Parser.hs view
@@ -89,6 +89,8 @@   [ testCase "String" $ parseTerm "\"file1 a hello - 123_\"" @?= Right (LString "file1 a hello - 123_")   , testCase "Positive integer" $ parseTerm "123" @?= Right (LInteger 123)   , testCase "Negative integer" $ parseTerm "-42" @?= Right (LInteger (-42))+  , testCase "Positive integer (too large)" $ isLeft (parseTerm "9223372036854775808") @? "Integer literals must fit in the int64 range"+  , testCase "Negative integer (too small)" $ isLeft (parseTerm "-9223372036854775809") @? "Integer literals must fit in the int64 range"   , testCase "Date" $ parseTerm "2019-12-02T13:49:53Z" @?=         Right (LDate $ read "2019-12-02 13:49:53 UTC")   , testCase "Variable" $ parseTerm "$1" @?= Right (Variable "1")@@ -210,6 +212,12 @@                  (EValue (Variable "0"))                  (EValue (LInteger 1))                  )+  , testCase "int comparison (NEQ)" $+      parseExpression "$0 != 1" @?=+        Right (EBinary NotEqual+                 (EValue (Variable "0"))+                 (EValue (LInteger 1))+                 )   , testCase "negative int comparison (GTE)" $       parseExpression "$0 >= -1234" @?=         Right (EBinary GreaterOrEqual@@ -222,6 +230,12 @@                  (EValue (Variable "0"))                  (EValue (LString "abc"))                  )+  , testCase "string comparison (NEQ)" $+      parseExpression "$0 != \"abc\"" @?=+        Right (EBinary NotEqual+                 (EValue (Variable "0"))+                 (EValue (LString "abc"))+                 )   , testCase "string comparison (starts_with)" $       parseExpression "$0.starts_with(\"abc\")" @?=         Right (EBinary Prefix@@ -277,6 +291,17 @@                     (EValue $ LInteger 10)                  )                  (EValue $ LInteger 2000)+              )+  , testCase "chained method calls" $+      parseExpression "$var.intersection([1]).union([2]).length()" @?=+        Right (EUnary Length+                 (EBinary Union+                    ( EBinary Intersection+                        (EValue $ Variable "var")+                        (EValue $ TermSet [LInteger 1])+                    )+                    (EValue $ TermSet [LInteger 2])+                 )               )   , operatorPrecedences   ]
test/Spec/SampleReader.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds          #-} {-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DeriveFunctor      #-} {-# LANGUAGE DeriveGeneric      #-} {-# LANGUAGE DeriveTraversable  #-} {-# LANGUAGE DerivingStrategies #-}@@ -40,6 +41,7 @@                                                 ResultError (..)) import           Auth.Biscuit.Datalog.Parser   (authorizerParser, blockParser) import           Auth.Biscuit.Token+import           Auth.Biscuit.Utils            (encodeHex)  import           Spec.Parser                   (parseAuthorizer, parseBlock) @@ -93,7 +95,7 @@ data RustResult e a   = Err e   | Ok a-  deriving stock (Generic, Eq, Show)+  deriving stock (Generic, Eq, Show, Functor)  instance Bifunctor RustResult where   bimap f g = \case@@ -268,7 +270,7 @@   pols <- either (assertFailure . show) pure $ parseAuthorizer $ foldMap (<> ";") (policies w)   res <- authorizeBiscuit b (authorizer_code <> pols)   checkResult compareExecErrors result res-  let revocationIds = Hex.encodeBase16 <$> toList (getRevocationIds b)+  let revocationIds = encodeHex <$> toList (getRevocationIds b)   step "Comparing revocation ids"   revocation_ids @?= revocationIds @@ -313,7 +315,7 @@              }           , result = Ok 0           , authorizer_code = authorizer-          , revocation_ids = Hex.encodeBase16 <$> toList (getRevocationIds biscuit)+          , revocation_ids = encodeHex <$> toList (getRevocationIds biscuit)           }   BS.writeFile ("test/samples/current/" <> filename) (serialize biscuit)   let token = mkBlockDesc <$> getAuthority biscuit :| getBlocks biscuit
test/samples/current/samples.json view
@@ -979,12 +979,13 @@             "b",             "de",             "abcD12",+            "abcD12x",             "abc",             "def"           ],           "public_keys": [],           "external_key": null,-          "code": "check if true;\ncheck if !false;\ncheck if !false && true;\ncheck if false or true;\ncheck if (true || false) && true;\ncheck if 1 < 2;\ncheck if 2 > 1;\ncheck if 1 <= 2;\ncheck if 1 <= 1;\ncheck if 2 >= 1;\ncheck if 2 >= 2;\ncheck if 3 == 3;\ncheck if 1 + 2 * 3 - 4 / 2 == 5;\ncheck if 1 | 2 ^ 3 == 0;\ncheck if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\");\ncheck if \"aaabde\".matches(\"a*c?.e\");\ncheck if \"aaabde\".contains(\"abd\");\ncheck if \"aaabde\" == \"aaa\" + \"b\" + \"de\";\ncheck if \"abcD12\" == \"abcD12\";\ncheck if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z;\ncheck if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z;\ncheck if hex:12ab == hex:12ab;\ncheck if [1, 2].contains(2);\ncheck if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z);\ncheck if [false, true].contains(true);\ncheck if [\"abc\", \"def\"].contains(\"abc\");\ncheck if [hex:12ab, hex:34de].contains(hex:34de);\n"+          "code": "check if true;\ncheck if !false;\ncheck if !false && true;\ncheck if false or true;\ncheck if (true || false) && true;\ncheck if 1 < 2;\ncheck if 2 > 1;\ncheck if 1 <= 2;\ncheck if 1 <= 1;\ncheck if 2 >= 1;\ncheck if 2 >= 2;\ncheck if 3 == 3;\ncheck if 1 != 3;\ncheck if 1 + 2 * 3 - 4 / 2 == 5;\ncheck if 1 | 2 ^ 3 == 0;\ncheck if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\");\ncheck if \"aaabde\".matches(\"a*c?.e\");\ncheck if \"aaabde\".contains(\"abd\");\ncheck if \"aaabde\" == \"aaa\" + \"b\" + \"de\";\ncheck if \"abcD12\" == \"abcD12\";\ncheck if \"abcD12x\" != \"abcD12\";\ncheck if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z;\ncheck if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z;\ncheck if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z;\ncheck if hex:12ab == hex:12ab;\ncheck if hex:12abcd != hex:12ab;\ncheck if [1, 2].contains(2);\ncheck if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z);\ncheck if [false, true].contains(true);\ncheck if [\"abc\", \"def\"].contains(\"abc\");\ncheck if [hex:12ab, hex:34de].contains(hex:34de);\ncheck if [1, 2] == [1, 2];\ncheck if [1, 4] != [1, 2];\n"         }       ],       "validations": {@@ -999,8 +1000,10 @@               "check if \"aaabde\".contains(\"abd\")",               "check if \"aaabde\".matches(\"a*c?.e\")",               "check if \"abcD12\" == \"abcD12\"",+              "check if \"abcD12x\" != \"abcD12\"",               "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")",               "check if (true || false) && true",+              "check if 1 != 3",               "check if 1 + 2 * 3 - 4 / 2 == 5",               "check if 1 < 2",               "check if 1 <= 1",@@ -1015,14 +1018,18 @@               "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z",               "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z",               "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z",+              "check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z",               "check if 3 == 3",               "check if [\"abc\", \"def\"].contains(\"abc\")",+              "check if [1, 2] == [1, 2]",               "check if [1, 2].contains(2)",+              "check if [1, 4] != [1, 2]",               "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)",               "check if [false, true].contains(true)",               "check if [hex:12ab, hex:34de].contains(hex:34de)",               "check if false or true",               "check if hex:12ab == hex:12ab",+              "check if hex:12abcd != hex:12ab",               "check if true"             ],             "policies": [@@ -1034,7 +1041,7 @@           },           "authorizer_code": "allow if true;\n",           "revocation_ids": [-            "1472c34b2854a24f5023f5c9a2ecead444b1ddb9c94ffc42f3ed1f062745d3983e8392cd0a94ec3b00e355f00fa5980e187bb86a625e289e2e723f373e3bcd05"+            "3e51db5f0453929a596485b59e89bf628a301a33d476132c48a1c0a208805809f15bdf99593733c1b5f30e8c1f473ee2f78042f81fd0557081bafb5370e65d0c"           ]         }       }@@ -1579,6 +1586,72 @@             "539cff0f5c311dcac843a9e6c8bb445aff0d6510bfa9b17d5350747be92dc365217e89e1d733f3ead1ecc05f287f312c41831338708e788503b55517af3ad000",             "5b10f7a7b4487f4421cf7f7f6d00b24a7a71939037b65b2e44241909564082a3e1e70cf7d866eb96f0a5119b9ea395adb772faaa33252fa62a579eb15a108a0b",             "3905351588cdfc4433b510cc1ed9c11ca5c1a7bd7d9cef338bcd3f6d374c711f34edd83dd0d53c25b63bf05b49fc78addceb47905d5495580c2fd36c11bc1e0a"+          ]+        }+      }+    },+    {+      "title": "integer wraparound",+      "filename": "test027_integer_wraparound.bc",+      "token": [+        {+          "symbols": [],+          "public_keys": [],+          "external_key": null,+          "code": "check if true || 10000000000 * 10000000000 != 0;\ncheck if true || 9223372036854775807 + 1 != 0;\ncheck if true || -9223372036854775808 - 1 != 0;\n"+        }+      ],+      "validations": {+        "": {+          "world": {+            "facts": [],+            "rules": [],+            "checks": [+              "check if true || -9223372036854775808 - 1 != 0",+              "check if true || 10000000000 * 10000000000 != 0",+              "check if true || 9223372036854775807 + 1 != 0"+            ],+            "policies": [+              "allow if true"+            ]+          },+          "result": {+            "Err": {+              "FailedLogic": {+                "Unauthorized": {+                  "policy": {+                    "Allow": 0+                  },+                  "checks": [+                    {+                      "Block": {+                        "block_id": 0,+                        "check_id": 0,+                        "rule": "check if true || 10000000000 * 10000000000 != 0"+                      }+                    },+                    {+                      "Block": {+                        "block_id": 0,+                        "check_id": 1,+                        "rule": "check if true || 9223372036854775807 + 1 != 0"+                      }+                    },+                    {+                      "Block": {+                        "block_id": 0,+                        "check_id": 2,+                        "rule": "check if true || -9223372036854775808 - 1 != 0"+                      }+                    }+                  ]+                }+              }+            }+          },+          "authorizer_code": "allow if true;\n",+          "revocation_ids": [+            "70d8941198ab5daa445a11357994d93278876ee95b6500f4c4a265ad668a0111440942b762e02513e471d40265d586ea76209921068524f588dc46eb4260db07"           ]         }       }
test/samples/current/test017_expressions.bc view

binary file changed (1385 → 1609 bytes)

+ test/samples/current/test027_integer_wraparound.bc view

binary file changed (absent → 329 bytes)