Skip to content

Commit a1e69bd

Browse files
committed
3.3: support for .try_or()
1 parent 8e4096e commit a1e69bd

File tree

7 files changed

+26
-2
lines changed

7 files changed

+26
-2
lines changed

biscuit/src/Auth/Biscuit/Datalog/AST.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ data Binary =
833833
| Any
834834
| Get
835835
| BinaryFfi Text
836+
| Try
836837
deriving (Eq, Ord, Show, Lift)
837838

838839
data Expression' (ctx :: DatalogContext) =
@@ -937,6 +938,7 @@ renderExpression =
937938
EBinary Any e e' -> rm "any" e e'
938939
EBinary Get e e' -> rm "get" e e'
939940
EBinary (BinaryFfi n) e e' -> rm ("extern::" <> n) e e'
941+
EBinary Try e e' -> rm "try_or" e e'
940942
EClosure ps e -> rC ps e
941943

942944
-- | A biscuit block, containing facts, rules and checks.

biscuit/src/Auth/Biscuit/Datalog/Executor.hs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ evalBinary _ Get (TermMap t) (LString s) = pure . fromMaybe LNull $ t !? StringK
535535
evalBinary _ Get _ _ = Left "Only arrays and maps support `.get()`"
536536
evalBinary _ Any _ _ = Left "internal error: leftover .any()"
537537
evalBinary _ All _ _ = Left "internal error: leftover .all()"
538+
evalBinary _ Try _ _ = Left "internal error: leftover .try_or()"
538539
evalBinary Limits{externFuncs} (BinaryFfi n) l r = runExternFunc externFuncs n l (Just r)
539540

540541
checkedOp :: (Integer -> Integer -> Integer)
@@ -636,6 +637,18 @@ evaluateLazyOr l b lhs' (EClosure [] e) =
636637
_ -> Left "Expected boolean"
637638
evaluateLazyOr _ _ _ _ = Left "Expected closure"
638639

640+
evaluateTry :: Limits
641+
-> Bindings
642+
-> Expression
643+
-> Expression
644+
-> Either String Value
645+
evaluateTry l b (EClosure [] e) e' = do
646+
rhs <- evaluateExpression l b e'
647+
case evaluateExpression l b e of
648+
Right r -> Right r
649+
Left _ -> Right rhs
650+
evaluateTry _ _ _ _ = Left "Expected closure"
651+
639652
-- | Given bindings for variables, reduce an expression to a single
640653
-- datalog value
641654
evaluateExpression :: Limits
@@ -657,5 +670,6 @@ evaluateExpression l b = \case
657670
EBinary All e e' -> do
658671
lhs <- evaluateExpression l b e
659672
evaluateAll l b lhs e'
673+
EBinary Try e e' -> evaluateTry l b e e'
660674
EBinary op e e' -> uncurry (evalBinary l op) =<< join bitraverse (evaluateExpression l b) (e, e')
661675
EClosure _ _ -> Left "Unexpected closure"

biscuit/src/Auth/Biscuit/Datalog/Parser.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,14 +353,17 @@ binaryMethodParser = do
353353
, All <$ chunk "all"
354354
, Get <$ chunk "get"
355355
, BinaryFfi <$> (chunk "extern::" *> identifierParser)
356+
, Try <$ chunk "try_or"
356357
]
357358
_ <- l $ C.char '('
358359
e2 <- case method of
359360
Any -> uncurry EClosure . first pure <$> l closureParser
360361
All -> uncurry EClosure . first pure <$> l closureParser
361362
_ -> l expressionParser
362363
_ <- l $ C.char ')'
363-
pure $ \e1 -> EBinary method e1 e2
364+
pure $ \e1 -> case method of
365+
Try -> EBinary method (EClosure [] e1) e2
366+
_ -> EBinary method e1 e2
364367

365368
unaryMethodParser :: Parser (Expression' 'WithSlices -> Expression' 'WithSlices)
366369
unaryMethodParser = do

biscuit/src/Auth/Biscuit/Proto.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ data BinaryKind =
254254
| Any
255255
| Get
256256
| BinaryFfi
257+
| Try
257258
deriving stock (Show, Enum, Bounded)
258259

259260
data OpBinary = OpBinary

biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ pbToBinary s PB.OpBinary{kind, ffiName} =
482482
PB.All -> Right All
483483
PB.Any -> Right Any
484484
PB.Get -> Right Get
485+
PB.Try -> Right Try
485486
PB.BinaryFfi -> do
486487
nameIdx <- maybeToRight "Missing extern call name" $ PB.getField ffiName
487488
name' <- getSymbol s $ SymbolRef nameIdx
@@ -529,6 +530,7 @@ binaryToPb s = \case
529530
Any -> PB.OpBinary { kind = PB.putField PB.Any, ffiName = PB.putField Nothing }
530531
All -> PB.OpBinary { kind = PB.putField PB.All, ffiName = PB.putField Nothing }
531532
Get -> PB.OpBinary { kind = PB.putField PB.Get, ffiName = PB.putField Nothing }
533+
Try -> PB.OpBinary { kind = PB.putField PB.Try, ffiName = PB.putField Nothing }
532534
BinaryFfi n -> PB.OpBinary
533535
{ kind = PB.putField PB.BinaryFfi
534536
, ffiName = PB.putField . Just . getSymbolRef $ getSymbolCode s n

biscuit/test/Spec/Executor.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ exprEval = do
170170
, ("{1}.intersection({\"test\"})", TermSet (Set.fromList []))
171171
, ("{1}.union({1})", TermSet (Set.fromList [LInteger 1]))
172172
, ("{1}.union({\"test\"})", TermSet (Set.fromList [LInteger 1, LString "test"]))
173+
, ("(true === 12).try_or(42)", LInteger 42)
173174
]
174175

175176
exprEvalError :: TestTree
@@ -186,6 +187,7 @@ exprEvalError = do
186187
, ("\"toto\".matches(\"to\")", "Regex evaluation is disabled")
187188
, ("9223372036854775807 + 1", "integer overflow")
188189
, ("-9223372036854775808 - 1", "integer underflow")
190+
, ("true.try_or(true === 12)", "Equality mismatch") -- the right-hand-side of try_or is eager
189191
]
190192

191193
rulesWithConstraints :: TestTree

biscuit/test/Spec/SampleReader.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ processTestCase :: (String -> IO ())
230230
-> Assertion
231231
processTestCase step rootPk TestCase{..}
232232
| fst filename == "test018_unbound_variables_in_rule.bc" = step "Skipping for now (unbound variables are now caught before evaluation)"
233-
| fst filename `elem` ["test036_secp256r1.bc", "test037_secp256r1_third_party.bc", "test038_try_op.bc"] = step "Skipping for now (not supported yet)"
233+
| fst filename `elem` ["test036_secp256r1.bc", "test037_secp256r1_third_party.bc"] = step "Skipping for now (not supported yet)"
234234
| otherwise = do
235235
step "Parsing "
236236
let vList = Map.toList validations

0 commit comments

Comments
 (0)