Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 4 format features #146

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 31 additions & 51 deletions authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (v *authorizer) AddPolicy(policy Policy) {
func (v *authorizer) Authorize() error {
// if we load facts from the verifier before
// the token's fact and rules, we might get inconsistent symbols
// token ements should first be converted to builder elements
// token elements should first be converted to builder elements
// with the token's symbol table, then converted back
// with the verifier's symbol table
for _, fact := range *v.biscuit.authority.facts {
Expand All @@ -141,44 +141,15 @@ func (v *authorizer) Authorize() error {
var errs []error

for i, check := range v.checks {
c := check.convert(v.symbols)
successful := false
for _, query := range c.Queries {
res := v.world.QueryRule(query, v.symbols)
if len(*res) != 0 {
successful = true
break
}
}
if !successful {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify check #%d: %s", i, debug.Check(c)))
}
errs = v.applyCheck(&check, errs, v.world, "authorizer", i)
}

for i, check := range v.biscuit.authority.checks {
ch, err := fromDatalogCheck(v.biscuit.symbols, check)
if err != nil {
return fmt.Errorf("biscuit: verification failed: %s", err)
}
c := ch.convert(v.symbols)

successful := false
for _, query := range c.Queries {
res := v.world.QueryRule(query, v.symbols)
if len(*res) != 0 {
successful = true
break
}
}
if !successful {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify block 0 check #%d: %s", i, debug.Check(c)))
}
errs = v.applyCheck(ch, errs, v.world, "block 0", i)
}

policyMatched := false
Expand All @@ -203,7 +174,7 @@ func (v *authorizer) Authorize() error {
}
}

// remove the rules from the vrifier and authority blocks
// remove the rules from the verifier and authority blocks
// so they are not affected by facts created by later blocks
v.world.ResetRules()

Expand Down Expand Up @@ -235,23 +206,7 @@ func (v *authorizer) Authorize() error {
if err != nil {
return fmt.Errorf("biscuit: verification failed: %s", err)
}
c := ch.convert(v.symbols)

successful := false
for _, query := range c.Queries {
res := block_world.QueryRule(query, v.symbols)

if len(*res) != 0 {
successful = true
break
}
}
if !successful {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify block #%d check #%d: %s", i+1, j, debug.Check(c)))
}
errs = v.applyCheck(ch, errs, block_world, fmt.Sprintf("block %d", i+1), j)
}

block_world.ResetRules()
Expand All @@ -277,6 +232,31 @@ func (v *authorizer) Authorize() error {
}
}

func (v *authorizer) applyCheck(ch *Check, errs []error, world *datalog.World, block string, idx int) []error {
c := ch.convert(v.symbols)

successful := false
for _, query := range c.Queries {
res := world.QueryRuleExtended(query, v.symbols, c.CheckKind)
if len(*res) != 0 {
successful = true
debug := datalog.SymbolDebugger{SymbolTable: v.symbols}
querystr := debug.CheckQuery(query)
_ = querystr
resstr := debug.FactSet(res)
_ = resstr
break
}
}
if !successful {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify block %s check #%d: %s", block, idx, debug.Check(c)))
}
return errs
}

func (v *authorizer) Query(rule Rule) (FactSet, error) {
if err := v.world.Run(v.symbols); err != nil {
return nil, err
Expand Down Expand Up @@ -328,7 +308,7 @@ func (v *authorizer) LoadPolicies(authorizerPolicies []byte) error {
}

switch pbPolicies.GetVersion() {
case 3:
case 3, 4:
return v.loadPoliciesV2(pbPolicies)
default:
return fmt.Errorf("verifier: unsupported policies version %d", pbPolicies.GetVersion())
Expand Down
2 changes: 1 addition & 1 deletion converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func protoBlockToTokenBlock(input *pb.Block) (*Block, error) {
}

switch input.GetVersion() {
case 3:
case 3, 4:
facts = make(datalog.FactSet, len(input.FactsV2))
rules = make([]datalog.Rule, len(input.RulesV2))
checks = make([]datalog.Check, len(input.ChecksV2))
Expand Down
45 changes: 42 additions & 3 deletions converters_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ func tokenIDToProtoIDV2(input datalog.Term) (*pb.TermV2, error) {
return nil, errors.New("biscuit: failed to convert token ID to proto ID: set cannot contains variable")
case datalog.TermTypeSet:
return nil, errors.New("biscuit: failed to convert token ID to proto ID: set cannot contains other sets")
default:
// Ignore and continue
}

protoSet := make([]*pb.TermV2, 0, len(datalogSet))
Expand Down Expand Up @@ -222,8 +224,8 @@ func tokenRuleToProtoRuleV2(input datalog.Rule) (*pb.RuleV2, error) {

func protoRuleToTokenRuleV2(input *pb.RuleV2) (*datalog.Rule, error) {
body := make([]datalog.Predicate, len(input.Body))
for i, pb := range input.Body {
b, err := protoPredicateToTokenPredicateV2(pb)
for i, pred := range input.Body {
b, err := protoPredicateToTokenPredicateV2(pred)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -378,6 +380,14 @@ func tokenExprBinaryToProtoExprBinary(op datalog.BinaryOp) (*pb.OpBinary, error)
pbBinaryKind = pb.OpBinary_Intersection
case datalog.BinaryUnion:
pbBinaryKind = pb.OpBinary_Union
case datalog.BinaryBitwiseAnd:
pbBinaryKind = pb.OpBinary_BitwiseAnd
case datalog.BinaryBitwiseOr:
pbBinaryKind = pb.OpBinary_BitwiseOr
case datalog.BinaryBitwiseXor:
pbBinaryKind = pb.OpBinary_BitwiseXor
case datalog.BinaryNotEqual:
pbBinaryKind = pb.OpBinary_NotEqual
default:
return nil, fmt.Errorf("biscuit: unsupported BinaryOpFunc type: %v", op.BinaryOpFunc.Type())
}
Expand All @@ -397,6 +407,8 @@ func protoExprBinaryToTokenExprBinary(op *pb.OpBinary) (datalog.BinaryOpFunc, er
binaryOp = datalog.GreaterOrEqual{}
case pb.OpBinary_Equal:
binaryOp = datalog.Equal{}
case pb.OpBinary_NotEqual:
binaryOp = datalog.NotEqual{}
case pb.OpBinary_Contains:
binaryOp = datalog.Contains{}
case pb.OpBinary_Prefix:
Expand All @@ -421,6 +433,12 @@ func protoExprBinaryToTokenExprBinary(op *pb.OpBinary) (datalog.BinaryOpFunc, er
binaryOp = datalog.Intersection{}
case pb.OpBinary_Union:
binaryOp = datalog.Union{}
case pb.OpBinary_BitwiseAnd:
binaryOp = datalog.BitwiseAnd{}
case pb.OpBinary_BitwiseOr:
binaryOp = datalog.BitwiseOr{}
case pb.OpBinary_BitwiseXor:
binaryOp = datalog.BitwiseXor{}
default:
return nil, fmt.Errorf("biscuit: unsupported proto OpBinary type: %v", op.Kind)
}
Expand All @@ -437,7 +455,17 @@ func tokenCheckToProtoCheckV2(input datalog.Check) (*pb.CheckV2, error) {
pbQueries[i] = q
}

var kind pb.CheckV2_Kind
switch input.CheckKind {
case datalog.CheckKindOne:
kind = pb.CheckV2_One
case datalog.CheckKindAll:
kind = pb.CheckV2_All
default:
return nil, errors.New("unsupported check kind")
}
return &pb.CheckV2{
Kind: &kind,
Queries: pbQueries,
}, nil
}
Expand All @@ -452,7 +480,18 @@ func protoCheckToTokenCheckV2(input *pb.CheckV2) (*datalog.Check, error) {
queries[i] = *q
}

var kind datalog.CheckKind
switch input.GetKind() {
case pb.CheckV2_One:
kind = datalog.CheckKindOne
case pb.CheckV2_All:
kind = datalog.CheckKindAll
default:
return nil, errors.New("unsupported check kind")
}

return &datalog.Check{
Queries: queries,
CheckKind: kind,
Queries: queries,
}, nil
}
4 changes: 2 additions & 2 deletions converters_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ func TestBlockConvertV2(t *testing.T) {
symbols: &datalog.SymbolTable{"a", "b", "c", "d"},
facts: &datalog.FactSet{datalog.Fact{Predicate: predicate}},
rules: []datalog.Rule{*rule},
checks: []datalog.Check{{Queries: []datalog.Rule{*rule}}},
checks: []datalog.Check{{CheckKind: datalog.CheckKindOne, Queries: []datalog.Rule{*rule}}},
context: "context",
version: 3,
}
Expand All @@ -737,7 +737,7 @@ func TestBlockConvertV2(t *testing.T) {
{Predicate: pbPredicate},
},
RulesV2: []*pb.RuleV2{pbRule},
ChecksV2: []*pb.CheckV2{{Queries: []*pb.RuleV2{pbRule}}},
ChecksV2: []*pb.CheckV2{{Kind: pb.CheckV2_One.Enum(), Queries: []*pb.RuleV2{pbRule}}},
Context: &ctx,
Version: proto.Uint32(version),
}
Expand Down
67 changes: 51 additions & 16 deletions datalog/datalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,16 @@ func (e InvalidRuleError) Error() string {
}

func (r Rule) Apply(facts *FactSet, newFacts *FactSet, syms *SymbolTable) error {
return r.ApplyExtended(facts, newFacts, syms, CheckKindOne)
}

func (r Rule) ApplyExtended(facts *FactSet, newFacts *FactSet, syms *SymbolTable, kind CheckKind) error {
// extract all variables from the rule body
variables := make(MatchedVariables)
for _, predicate := range r.Body {
for _, term := range predicate.Terms {
v, ok := term.(Variable)
if !ok {
continue
}
variables[v] = nil
}
}
variables := r.collectVariables()

matchAllExpr := kind == CheckKindAll

combinations := combine(variables, r.Body, r.Expressions, facts, syms)
combinations := combine(variables, r.Body, r.Expressions, facts, syms, matchAllExpr)

for res := range combinations {
if res.error != nil {
Expand All @@ -238,8 +235,30 @@ func (r Rule) Apply(facts *FactSet, newFacts *FactSet, syms *SymbolTable) error
return nil
}

func (r Rule) collectVariables() MatchedVariables {
variables := make(MatchedVariables)
for _, predicate := range r.Body {
for _, term := range predicate.Terms {
v, ok := term.(Variable)
if !ok {
continue
}
variables[v] = nil
}
}
return variables
}

type CheckKind byte

const (
CheckKindOne CheckKind = iota
CheckKindAll
)

type Check struct {
Queries []Rule
CheckKind CheckKind
Queries []Rule
}

type FactSet []Fact
Expand Down Expand Up @@ -443,8 +462,15 @@ func (w *World) Query(pred Predicate) *FactSet {
}

func (w *World) QueryRule(rule Rule, syms *SymbolTable) *FactSet {
return w.QueryRuleExtended(rule, syms, CheckKindOne)
}

func (w *World) QueryRuleExtended(rule Rule, syms *SymbolTable, kind CheckKind) *FactSet {
newFacts := &FactSet{}
rule.Apply(w.facts, newFacts, syms)
err := rule.ApplyExtended(w.facts, newFacts, syms, kind)
if err != nil { // TODO: this check was missing from mainline code.
return &FactSet{}
}
return newFacts
}

Expand Down Expand Up @@ -486,7 +512,7 @@ func (m MatchedVariables) Clone() MatchedVariables {
return res
}

func combine(variables MatchedVariables, predicates []Predicate, expressions []Expression, facts *FactSet, syms *SymbolTable) <-chan struct {
func combine(variables MatchedVariables, predicates []Predicate, expressions []Expression, facts *FactSet, syms *SymbolTable, matchAllExpr bool) <-chan struct {
MatchedVariables
error
} {
Expand Down Expand Up @@ -577,8 +603,17 @@ func combine(variables MatchedVariables, predicates []Predicate, expressions []E
return
}
if !res.Equal(Bool(true)) {
valid = false
break
if !matchAllExpr {
valid = false
break
} else {
c <- struct {
MatchedVariables
error
}{complete_vars, fmt.Errorf("one or more expressions failed to match")}

return
}
}
}

Expand Down
Loading
Loading