Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
84 changes: 52 additions & 32 deletions authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,51 @@ func (v *authorizer) AddPolicy(policy Policy) {
v.policies = append(v.policies, policy)
}

func (v *authorizer) evaluateCheckWithWorld(check datalog.Check, world *datalog.World, symbols *datalog.SymbolTable) error {
switch check.Kind {
case datalog.CheckKindOne:
// Original behavior: succeeds if any query succeeds
for _, query := range check.Queries {
res := world.QueryRule(query, symbols)
if len(*res) != 0 {
return nil // Check passed
}
}
// All queries failed
return fmt.Errorf("check failed")

case datalog.CheckKindAll:
// Check all: succeeds if all results that match the body also succeed the expressions
for _, query := range check.Queries {
res := world.QueryRule(query, symbols)
if len(*res) == 0 {
return fmt.Errorf("check all failed: no matches found")
}
// All matches must satisfy the expressions - this is already handled by QueryRule
// since it evaluates both predicates and expressions
}
return nil

case datalog.CheckKindReject:
// Reject if: succeeds if no set of facts matches the body and expressions
for _, query := range check.Queries {
res := world.QueryRule(query, symbols)
if len(*res) != 0 {
return fmt.Errorf("reject if failed: found matching facts")
}
}
return nil

default:
return fmt.Errorf("unknown check kind: %d", check.Kind)
}
}

func (v *authorizer) evaluateCheck(check datalog.Check, symbols *datalog.SymbolTable) error {
return v.evaluateCheckWithWorld(check, v.world, symbols)
}


func (v *authorizer) Authorize() error {
// if we load facts from the verifier before
// the token's fact and rules, we might get inconsistent symbols
Expand Down Expand Up @@ -145,19 +190,11 @@ func (v *authorizer) Authorize() 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 {
if err := v.evaluateCheck(c, v.symbols); err != nil {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify check #%d: %s", i, debug.Check(c)))
errs = append(errs, fmt.Errorf("failed to verify check #%d: %s - %v", i, debug.Check(c), err))
}
}

Expand All @@ -168,19 +205,11 @@ func (v *authorizer) Authorize() error {
}
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 {
if err := v.evaluateCheck(c, v.symbols); err != nil {
debug := datalog.SymbolDebugger{
SymbolTable: v.symbols,
}
errs = append(errs, fmt.Errorf("failed to verify block 0 check #%d: %s", i, debug.Check(c)))
errs = append(errs, fmt.Errorf("failed to verify block 0 check #%d: %s - %v", i, debug.Check(c), err))
}
}

Expand Down Expand Up @@ -240,20 +269,11 @@ func (v *authorizer) Authorize() error {
}
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 {
if err := v.evaluateCheckWithWorld(c, block_world, v.symbols); err != nil {
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 = append(errs, fmt.Errorf("failed to verify block #%d check #%d: %s - %v", i+1, j, debug.Check(c), err))
}
}

Expand Down Expand Up @@ -331,7 +351,7 @@ func (v *authorizer) LoadPolicies(authorizerPolicies []byte) error {
}

switch pbPolicies.GetVersion() {
case 3:
case 3, 4, 5:
return v.loadPoliciesV2(pbPolicies)
default:
return fmt.Errorf("verifier: unsupported policies version %d", pbPolicies.GetVersion())
Expand Down
71 changes: 64 additions & 7 deletions converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,52 @@ func tokenBlockToProtoBlock(input *Block) (*pb.Block, error) {
}
}

// Convert public keys
publicKeys := input.publicKeys
if publicKeys != nil {
out.PublicKeys = make([]*pb.PublicKey, len(publicKeys))
for i, key := range publicKeys {
pbKey, err := tokenPublicKeyToProtoPublicKey(key)
if err != nil {
return nil, err
}
out.PublicKeys[i] = pbKey
}
}

return out, nil
}

func tokenPublicKeyToProtoPublicKey(input PublicKey) (*pb.PublicKey, error) {
var algorithm pb.PublicKey_Algorithm
switch input.Algorithm {
case "Ed25519":
algorithm = pb.PublicKey_Ed25519
default:
return nil, fmt.Errorf("unsupported public key algorithm: %s", input.Algorithm)
}

return &pb.PublicKey{
Algorithm: &algorithm,
Key: input.Key,
}, nil
}

func protoPublicKeyToTokenPublicKey(input *pb.PublicKey) (PublicKey, error) {
var algorithm string
switch input.GetAlgorithm() {
case pb.PublicKey_Ed25519:
algorithm = "Ed25519"
default:
return PublicKey{}, fmt.Errorf("unsupported public key algorithm: %d", input.GetAlgorithm())
}

return PublicKey{
Algorithm: algorithm,
Key: input.Key,
}, nil
}

func protoBlockToTokenBlock(input *pb.Block) (*Block, error) {
symbols := datalog.SymbolTable(input.Symbols)

Expand All @@ -80,7 +123,7 @@ func protoBlockToTokenBlock(input *pb.Block) (*Block, error) {
}

switch input.GetVersion() {
case 3:
case 3, 4, 5:
facts = make(datalog.FactSet, len(input.FactsV2))
rules = make([]datalog.Rule, len(input.RulesV2))
checks = make([]datalog.Check, len(input.ChecksV2))
Expand Down Expand Up @@ -112,13 +155,27 @@ func protoBlockToTokenBlock(input *pb.Block) (*Block, error) {
return nil, fmt.Errorf("biscuit: failed to convert proto block to token block: unsupported version: %d", input.GetVersion())
}

// Convert public keys
var publicKeys []PublicKey
if input.PublicKeys != nil {
publicKeys = make([]PublicKey, len(input.PublicKeys))
for i, pbKey := range input.PublicKeys {
key, err := protoPublicKeyToTokenPublicKey(pbKey)
if err != nil {
return nil, err
}
publicKeys[i] = key
}
}

return &Block{
symbols: &symbols,
facts: &facts,
rules: rules,
checks: checks,
context: input.GetContext(),
version: input.GetVersion(),
symbols: &symbols,
facts: &facts,
rules: rules,
checks: checks,
context: input.GetContext(),
version: input.GetVersion(),
publicKeys: publicKeys,
}, nil
}

Expand Down
35 changes: 35 additions & 0 deletions converters_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,32 @@ func protoExprBinaryToTokenExprBinary(op *pb.OpBinary) (datalog.BinaryOpFunc, er
return binaryOp, nil
}

func datalogCheckKindToProtoCheckKind(kind datalog.CheckKind) pb.CheckV2_Kind {
switch kind {
case datalog.CheckKindOne:
return pb.CheckV2_One
case datalog.CheckKindAll:
return pb.CheckV2_All
case datalog.CheckKindReject:
return pb.CheckV2_Reject
default:
return pb.CheckV2_One
}
}

func protoCheckKindToDatalogCheckKind(kind pb.CheckV2_Kind) datalog.CheckKind {
switch kind {
case pb.CheckV2_One:
return datalog.CheckKindOne
case pb.CheckV2_All:
return datalog.CheckKindAll
case pb.CheckV2_Reject:
return datalog.CheckKindReject
default:
return datalog.CheckKindOne
}
}

func tokenCheckToProtoCheckV2(input datalog.Check) (*pb.CheckV2, error) {
pbQueries := make([]*pb.RuleV2, len(input.Queries))
for i, query := range input.Queries {
Expand All @@ -440,8 +466,10 @@ func tokenCheckToProtoCheckV2(input datalog.Check) (*pb.CheckV2, error) {
pbQueries[i] = q
}

kind := datalogCheckKindToProtoCheckKind(input.Kind)
return &pb.CheckV2{
Queries: pbQueries,
Kind: &kind,
}, nil
}

Expand All @@ -455,7 +483,14 @@ func protoCheckToTokenCheckV2(input *pb.CheckV2) (*datalog.Check, error) {
queries[i] = *q
}

kind := datalog.CheckKindOne
if input.Kind != nil {
kind = protoCheckKindToDatalogCheckKind(*input.Kind)
}

return &datalog.Check{
Queries: queries,
Kind: kind,
}, nil
}

4 changes: 2 additions & 2 deletions converters_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,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{{Queries: []datalog.Rule{*rule}, Kind: datalog.CheckKindOne}},
context: "context",
version: 3,
}
Expand All @@ -740,7 +740,7 @@ func TestBlockConvertV2(t *testing.T) {
{Predicate: pbPredicate},
},
RulesV2: []*pb.RuleV2{pbRule},
ChecksV2: []*pb.CheckV2{{Queries: []*pb.RuleV2{pbRule}}},
ChecksV2: []*pb.CheckV2{{Queries: []*pb.RuleV2{pbRule}, Kind: pb.CheckV2_One.Enum()}},
Context: &ctx,
Version: proto.Uint32(version),
}
Expand Down
9 changes: 9 additions & 0 deletions datalog/datalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,17 @@ func (r Rule) Apply(facts *FactSet, newFacts *FactSet, syms *SymbolTable) error
return nil
}

type CheckKind byte

const (
CheckKindOne CheckKind = iota
CheckKindAll
CheckKindReject
)

type Check struct {
Queries []Rule
Kind CheckKind
}

type FactSet []Fact
Expand Down
10 changes: 5 additions & 5 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func ExampleBiscuit() {
right("/a/file1.txt", {write});
right("/a/file2.txt", {read});
right("/a/file3.txt", {write});
`, map[string]biscuit.Term{"read": biscuit.String("read"), "write": biscuit.String("write")})
`, parser.ParametersMap{"read": biscuit.String("read"), "write": biscuit.String("write")})
if err != nil {
panic(fmt.Errorf("failed to parse authority block: %v", err))
}
Expand Down Expand Up @@ -50,7 +50,7 @@ func ExampleBiscuit() {

block, err := parser.FromStringBlockWithParams(`
check if resource($file), operation($permission), [{read}].contains($permission);`,
map[string]biscuit.Term{"read": biscuit.String("read")})
parser.ParametersMap{"read": biscuit.String("read")})

if err != nil {
panic(fmt.Errorf("failed to parse block: %v", err))
Expand Down Expand Up @@ -84,7 +84,7 @@ func ExampleBiscuit() {
resource({res});
operation({op});
allow if right({res}, {op});
`, map[string]biscuit.Term{"res": biscuit.String("/a/file1.txt"), "op": biscuit.String("read")})
`, parser.ParametersMap{"res": biscuit.String("/a/file1.txt"), "op": biscuit.String("read")})

if err != nil {
panic(fmt.Errorf("failed to parse authorizer: %v", err))
Expand All @@ -107,7 +107,7 @@ func ExampleBiscuit() {
resource({res});
operation({op});
allow if right({res}, {op});
`, map[string]biscuit.Term{"res": biscuit.String("/a/file1.txt"), "op": biscuit.String("write")})
`, parser.ParametersMap{"res": biscuit.String("/a/file1.txt"), "op": biscuit.String("write")})

if err != nil {
panic(fmt.Errorf("failed to parse authorizer: %v", err))
Expand All @@ -121,7 +121,7 @@ func ExampleBiscuit() {
}

// Output: Token1 length: 251
// Token2 length: 433
// Token2 length: 435
// allowed to read /a/file1.txt
// forbidden to write /a/file1.txt
}
Loading