Skip to content

Commit

Permalink
3.3: reject if
Browse files Browse the repository at this point in the history
  • Loading branch information
divarvel committed Dec 1, 2024
1 parent bf5aebe commit 8ccb646
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 27 deletions.
15 changes: 10 additions & 5 deletions biscuit/src/Auth/Biscuit/Datalog/AST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ module Auth.Biscuit.Datalog.AST
, ruleHasNoScope
, ruleHasNoV4Operators
, isCheckOne
, isReject
, renderBlock
, renderAuthorizer
, renderFact
Expand Down Expand Up @@ -453,7 +454,7 @@ makeQueryItem qBody qExpressions qScope =
Just vs -> Failure vs


data CheckKind = One | All
data CheckKind = One | All | Reject
deriving (Eq, Show, Ord, Lift)

data Check' evalCtx ctx = Check
Expand All @@ -475,6 +476,9 @@ type EvalCheck = Check' 'Eval 'Representation
isCheckOne :: Check' evalCtx ctx -> Bool
isCheckOne Check{cKind} = cKind == One

isReject :: Check' evalCtx ctx -> Bool
isReject Check{cKind} = cKind == Reject

data PolicyType = Allow | Deny
deriving (Eq, Show, Ord, Lift)
type Policy' evalCtx ctx = (PolicyType, Query' evalCtx ctx)
Expand Down Expand Up @@ -516,10 +520,11 @@ renderQueryItem QueryItem{..} =

renderCheck :: Check -> Text
renderCheck Check{..} =
let kindToken = case cKind of
One -> "if"
All -> "all"
in "check " <> kindToken <> " " <>
let keyword = case cKind of
One -> "check if"
All -> "check all"
Reject -> "reject if"
in keyword <> " " <>
intercalate "\n or " (renderQueryItem <$> cQueries)

listSymbolsInQueryItem :: QueryItem' evalCtx 'Representation -> Set.Set Text
Expand Down
26 changes: 19 additions & 7 deletions biscuit/src/Auth/Biscuit/Datalog/Executor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,25 @@ countFacts (FactGroup facts) = sum $ Set.size <$> Map.elems facts

checkCheck :: Limits -> Natural -> Natural -> FactGroup -> EvalCheck -> Either String (Validation (NonEmpty Check) ())
checkCheck l blockCount checkBlockId facts c@Check{cQueries,cKind} = do
let isQueryItemOk = case cKind of
One -> isQueryItemSatisfied l blockCount checkBlockId facts
All -> isQueryItemSatisfiedForAllMatches l blockCount checkBlockId facts
hasOkQueryItem <- anyM (fmap isJust . isQueryItemOk) cQueries
pure $ if hasOkQueryItem
then Success ()
else failure (toRepresentation c)
let queryMatchesOne = isQueryItemSatisfied l blockCount checkBlockId facts
let queryMatchesAll = isQueryItemSatisfiedForAllMatches l blockCount checkBlockId facts

case cKind of
One -> do
hasOkQueryItem <- anyM (fmap isJust . queryMatchesOne) cQueries
pure $ if hasOkQueryItem
then Success ()
else failure (toRepresentation c)
All -> do
hasOkQueryItem <- anyM (fmap isJust . queryMatchesAll) cQueries
pure $ if hasOkQueryItem
then Success ()
else failure (toRepresentation c)
Reject -> do
hasOkQueryItem <- anyM (fmap isJust . queryMatchesOne) cQueries
pure $ if not hasOkQueryItem
then Success ()
else failure (toRepresentation c)

checkPolicy :: Limits -> Natural -> FactGroup -> EvalPolicy -> Either String (Maybe (Either MatchedQuery MatchedQuery))
checkPolicy l blockCount facts (pType, query) = do
Expand Down
1 change: 1 addition & 0 deletions biscuit/src/Auth/Biscuit/Datalog/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ checkParser :: Bool -> Parser (Check' 'Repr 'WithSlices)
checkParser inAuthorizer = do
cKind <- l $ choice [ One <$ chunk "check if"
, All <$ chunk "check all"
, Reject <$ chunk "reject if"
]
cQueries <- queryParser inAuthorizer
pure Check{..}
Expand Down
1 change: 1 addition & 0 deletions biscuit/src/Auth/Biscuit/Proto.hs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ data RuleV2 = RuleV2
data CheckKind =
One
| All
| Reject
deriving stock (Show, Enum, Bounded)

data CheckV2 = CheckV2
Expand Down
38 changes: 24 additions & 14 deletions biscuit/src/Auth/Biscuit/ProtoBufAdapter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ pbToBlock ePk PB.Block{..} = do
bRules <- traverse (pbToRule s) $ PB.getField rules_v2
bChecks <- traverse (pbToCheck s) $ PB.getField checks_v2
bScope <- Set.fromList <$> traverse (pbToScope s) (PB.getField scope)
let v5Plus = isJust ePk
let v6Plus = any isReject bChecks
v5Plus = isJust ePk
v4Plus = not $ and
[ Set.null bScope
, all ruleHasNoScope bRules
Expand All @@ -141,18 +142,25 @@ pbToBlock ePk PB.Block{..} = do
, all ruleHasNoV4Operators bRules
, all (queryHasNoV4Operators . cQueries) bChecks
]
case (bVersion, v4Plus, v5Plus) of
(Just 5, _, _) -> pure Block {..}
(Just 4, _, False) -> pure Block {..}
(Just 4, _, True) ->
case (bVersion, v4Plus, v5Plus, v6Plus) of
(Just 6, _, _, _) -> pure Block {..}
(Just 5, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 5."
(Just 5, _, _, _) -> pure Block {..}
(Just 4, _, False, False) -> pure Block {..}
(Just 4, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 4."
(Just 4, _, True, False) ->
Left "Biscuit v5 features are present, but the block version is 4."
(Just 3, False, False) -> pure Block {..}
(Just 3, True, False) ->
(Just 3, False, False, False) -> pure Block {..}
(Just 3, True, False, False) ->
Left "Biscuit v4 features are present, but the block version is 3."
(Just 3, _, True) ->
(Just 3, _, True, False) ->
Left "Biscuit v5 features are present, but the block version is 3."
(Just 3, _, _, True) ->
Left "Biscuit v6 features are present, but the block version is 3."
_ ->
Left $ "Unsupported biscuit version: " <> maybe "0" show bVersion <> ". Only versions 3 and 4 are supported"
Left $ "Unsupported biscuit version: " <> maybe "0" show bVersion <> ". Only versions 3 to 6 are supported"

-- | Turn a biscuit block into a protobuf block, for serialization,
-- along with the newly defined symbols
Expand Down Expand Up @@ -227,9 +235,10 @@ pbToCheck s PB.CheckV2{queries,kind} = do
rules <- traverse (pbToRule s) $ PB.getField queries
let cQueries = toCheck <$> rules
let cKind = case PB.getField kind of
Just PB.All -> All
Just PB.One -> One
Nothing -> One
Just PB.All -> All
Just PB.One -> One
Just PB.Reject -> Reject
Nothing -> One
pure Check{..}

checkToPb :: ReverseSymbols -> Check -> PB.CheckV2
Expand All @@ -242,8 +251,9 @@ checkToPb s Check{..} =
, scope = qScope
}
pbKind = case cKind of
One -> Nothing
All -> Just PB.All
One -> Nothing
All -> Just PB.All
Reject -> Just PB.Reject
in PB.CheckV2 { queries = PB.putField $ toQuery <$> cQueries
, kind = PB.putField pbKind
}
Expand Down
21 changes: 21 additions & 0 deletions biscuit/test/Spec/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ checkParsing = testGroup "check blocks"
{ cQueries = [QueryItem [] [EValue $ LBool True] []]
, cKind = All
}
, testCase "Simple reject if" $
parseCheck "reject if true" @?=
Right Check
{ cQueries = [QueryItem [] [EValue $ LBool True] []]
, cKind = Reject
}
, testCase "Multiple groups" $
parseCheck
"check if fact($var), $var === true or \
Expand Down Expand Up @@ -444,6 +450,21 @@ checkParsing = testGroup "check blocks"
]
, cKind = All
}
, testCase "Multiple reject if groups" $
parseCheck
"reject if fact($var), $var === true or \
\other($var), $var === 2" @?=
Right Check
{ cQueries =
[ QueryItem [Predicate "fact" [Variable "var"]]
[EBinary Equal (EValue (Variable "var")) (EValue (LBool True))]
[]
, QueryItem [Predicate "other" [Variable "var"]]
[EBinary Equal (EValue (Variable "var")) (EValue (LInteger 2))]
[]
]
, cKind = Reject
}
, testCase "Multiple groups, scoped" $
parseCheck
"check if fact($var), $var === true trusting previous or \
Expand Down
2 changes: 1 addition & 1 deletion biscuit/test/Spec/SampleReader.hs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ processTestCase step rootPk TestCase{..} =
if fst filename == "test018_unbound_variables_in_rule.bc"
then
step "Skipping for now (unbound variables are now caught before evaluation)"
else if fst filename `elem` ["test029_reject_if.bc", "test030_null.bc", "test031_heterogeneous_equal.bc", "test032_laziness_closures.bc", "test033_typeof.bc", "test034_array_map.bc", "test035_ffi.bc", "test036_secp256r1.bc"]
else if fst filename `elem` ["test030_null.bc", "test031_heterogeneous_equal.bc", "test032_laziness_closures.bc", "test033_typeof.bc", "test034_array_map.bc", "test035_ffi.bc", "test036_secp256r1.bc"]
then
step "Skipping for now (not supported yet)"
else do
Expand Down

0 comments on commit 8ccb646

Please sign in to comment.