Skip to content

Commit

Permalink
Refactor authorizer serialization (#127)
Browse files Browse the repository at this point in the history
This separates the `AuthorizerPolicies`, used to load data in an authorizer to perform an authorization, from the `AuthorizerSnapshot`, that serializes an entire authorizer excution, along with the token's data and runtime limits
  • Loading branch information
Geal authored Jan 29, 2023
1 parent 676e32f commit a93b477
Show file tree
Hide file tree
Showing 9 changed files with 721 additions and 179 deletions.
10 changes: 5 additions & 5 deletions biscuit-auth/src/datalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl AsRef<Term> for Term {
}
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct Predicate {
pub name: SymbolIndex,
pub terms: Vec<Term>,
Expand All @@ -67,7 +67,7 @@ impl AsRef<Predicate> for Predicate {
}
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fact {
pub predicate: Predicate,
}
Expand Down Expand Up @@ -548,7 +548,7 @@ pub fn match_preds(rule_pred: &Predicate, fact_pred: &Predicate) -> bool {
pub struct World {
pub facts: FactSet,
pub rules: RuleSet,
pub iterations: u32,
pub iterations: u64,
}

impl World {
Expand Down Expand Up @@ -675,9 +675,9 @@ impl World {
#[derive(Debug, Clone)]
pub struct RunLimits {
/// maximum number of Datalog facts (memory usage)
pub max_facts: u32,
pub max_facts: u64,
/// maximum number of iterations of the rules applications (prevents degenerate rules)
pub max_iterations: u32,
pub max_iterations: u64,
/// maximum execution time
pub max_time: Duration,
}
Expand Down
3 changes: 2 additions & 1 deletion biscuit-auth/src/datalog/origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::token::Scope;

#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Origin {
inner: BTreeSet<usize>,
pub(crate) inner: BTreeSet<usize>,
}

impl Origin {
Expand Down Expand Up @@ -89,6 +89,7 @@ impl TrustedOrigins {
origins.insert(0);
TrustedOrigins(origins)
}

pub fn from_scopes(
rule_scopes: &[Scope],
default_origins: &TrustedOrigins,
Expand Down
144 changes: 127 additions & 17 deletions biscuit-auth/src/format/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use self::v2::proto_scope_to_token_scope;

use super::schema;
use crate::builder::Convert;
use crate::crypto::PublicKey;
use crate::datalog::*;
use crate::error;
Expand Down Expand Up @@ -112,32 +113,133 @@ pub fn proto_block_to_token_block(
})
}

pub fn authorizer_to_proto_authorizer(input: &AuthorizerPolicies) -> schema::AuthorizerPolicies {
let mut symbols = input.symbols.clone();
let policies = input
.policies
.iter()
.map(|p| v2::policy_to_proto_policy(p, &mut symbols))
.collect();

schema::AuthorizerPolicies {
symbols: symbols.strings(),
pub fn token_block_to_proto_snapshot_block(input: &Block) -> schema::SnapshotBlock {
schema::SnapshotBlock {
context: input.context.clone(),
version: Some(input.version),
facts: input
facts_v2: input
.facts
.iter()
.map(v2::token_fact_to_proto_fact)
.collect(),
rules: input
rules_v2: input
.rules
.iter()
.map(v2::token_rule_to_proto_rule)
.collect(),
checks: input
checks_v2: input
.checks
.iter()
.map(v2::token_check_to_proto_check)
.collect(),
scope: input
.scopes
.iter()
.map(v2::token_scope_to_proto_scope)
.collect(),
external_key: input.external_key.map(|key| key.to_proto()),
}
}

pub fn proto_snapshot_block_to_token_block(
input: &schema::SnapshotBlock,
) -> Result<Block, error::Format> {
let version = input.version.unwrap_or(0);
if !(MIN_SCHEMA_VERSION..=MAX_SCHEMA_VERSION).contains(&version) {
return Err(error::Format::Version {
minimum: crate::token::MIN_SCHEMA_VERSION,
maximum: crate::token::MAX_SCHEMA_VERSION,
actual: version,
});
}

let mut facts = vec![];
let mut rules = vec![];
let mut checks = vec![];
let mut scopes = vec![];
for fact in input.facts_v2.iter() {
facts.push(v2::proto_fact_to_token_fact(fact)?);
}

for rule in input.rules_v2.iter() {
rules.push(v2::proto_rule_to_token_rule(rule, version)?.0);
}

if version == MIN_SCHEMA_VERSION && input.checks_v2.iter().any(|c| c.kind.is_some()) {
return Err(error::Format::DeserializationError(
"deserialization error: v3 blocks must not contain a check kind".to_string(),
));
}

for check in input.checks_v2.iter() {
checks.push(v2::proto_check_to_token_check(check, version)?);
}
for scope in input.scope.iter() {
scopes.push(v2::proto_scope_to_token_scope(scope)?);
}

let context = input.context.clone();

let detected_schema_version = get_schema_version(&facts, &rules, &checks, &scopes);

detected_schema_version.check_compatibility(version)?;

let scopes: Result<Vec<Scope>, _> =
input.scope.iter().map(proto_scope_to_token_scope).collect();

let external_key = match &input.external_key {
None => None,
Some(key) => Some(PublicKey::from_proto(&key)?),
};

Ok(Block {
symbols: SymbolTable::new(),
facts,
rules,
checks,
context,
version,
external_key,
public_keys: PublicKeys::default(),
scopes: scopes?,
})
}
pub fn authorizer_to_proto_authorizer(input: &AuthorizerPolicies) -> schema::AuthorizerPolicies {
let mut symbols = SymbolTable::default();

let facts = input
.facts
.iter()
.map(|f| f.convert(&mut symbols))
.map(|f| v2::token_fact_to_proto_fact(&f))
.collect();

let rules = input
.rules
.iter()
.map(|r| r.convert(&mut symbols))
.map(|r| v2::token_rule_to_proto_rule(&r))
.collect();

let checks = input
.checks
.iter()
.map(|c| c.convert(&mut symbols))
.map(|c| v2::token_check_to_proto_check(&c))
.collect();

let policies = input
.policies
.iter()
.map(|p| v2::policy_to_proto_policy(p, &mut symbols))
.collect();

schema::AuthorizerPolicies {
symbols: symbols.strings(),
version: Some(input.version),
facts,
rules,
checks,
policies,
}
}
Expand All @@ -162,15 +264,24 @@ pub fn proto_authorizer_to_authorizer(
let mut policies = vec![];

for fact in input.facts.iter() {
facts.push(v2::proto_fact_to_token_fact(fact)?);
facts.push(crate::builder::Fact::convert_from(
&v2::proto_fact_to_token_fact(fact)?,
&symbols,
)?);
}

for rule in input.rules.iter() {
rules.push(v2::proto_rule_to_token_rule(rule, version)?.0);
rules.push(crate::builder::Rule::convert_from(
&v2::proto_rule_to_token_rule(rule, version)?.0,
&symbols,
)?);
}

for check in input.checks.iter() {
checks.push(v2::proto_check_to_token_check(check, version)?);
checks.push(crate::builder::Check::convert_from(
&v2::proto_check_to_token_check(check, version)?,
&symbols,
)?);
}

for policy in input.policies.iter() {
Expand All @@ -179,7 +290,6 @@ pub fn proto_authorizer_to_authorizer(

Ok(AuthorizerPolicies {
version,
symbols,
facts,
rules,
checks,
Expand Down
47 changes: 47 additions & 0 deletions biscuit-auth/src/format/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,51 @@ message ThirdPartyBlockRequest {
message ThirdPartyBlockContents {
required bytes payload = 1;
required ExternalSignature externalSignature = 2;
}

message AuthorizerSnapshot {
required RunLimits limits = 1;
required uint64 executionTime = 2;
required AuthorizerWorld world = 3;
}

message RunLimits {
required uint64 maxFacts = 1;
required uint64 maxIterations = 2;
required uint64 maxTime = 3;
}

message AuthorizerWorld {
optional uint32 version = 1;
repeated string symbols = 2;
repeated PublicKey publicKeys = 3;
repeated SnapshotBlock blocks = 4;
required SnapshotBlock authorizerBlock = 5;
repeated Policy authorizerPolicies = 6;
repeated GeneratedFacts generatedFacts = 7;
required uint64 iterations = 8;
}

message Origin {
oneof Content {
Empty authorizer = 1;
uint32 origin = 2;
}
}

message Empty {}

message GeneratedFacts {
repeated Origin origins = 1;
repeated FactV2 facts = 2;
}

message SnapshotBlock {
optional string context = 1;
optional uint32 version = 2;
repeated FactV2 facts_v2 = 3;
repeated RuleV2 rules_v2 = 4;
repeated CheckV2 checks_v2 = 5;
repeated Scope scope = 6;
optional PublicKey externalKey = 7;
}
79 changes: 79 additions & 0 deletions biscuit-auth/src/format/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,82 @@ pub struct ThirdPartyBlockContents {
#[prost(message, required, tag="2")]
pub external_signature: ExternalSignature,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AuthorizerSnapshot {
#[prost(message, required, tag="1")]
pub limits: RunLimits,
#[prost(uint64, required, tag="2")]
pub execution_time: u64,
#[prost(message, required, tag="3")]
pub world: AuthorizerWorld,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RunLimits {
#[prost(uint64, required, tag="1")]
pub max_facts: u64,
#[prost(uint64, required, tag="2")]
pub max_iterations: u64,
#[prost(uint64, required, tag="3")]
pub max_time: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AuthorizerWorld {
#[prost(uint32, optional, tag="1")]
pub version: ::core::option::Option<u32>,
#[prost(string, repeated, tag="2")]
pub symbols: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
#[prost(message, repeated, tag="3")]
pub public_keys: ::prost::alloc::vec::Vec<PublicKey>,
#[prost(message, repeated, tag="4")]
pub blocks: ::prost::alloc::vec::Vec<SnapshotBlock>,
#[prost(message, required, tag="5")]
pub authorizer_block: SnapshotBlock,
#[prost(message, repeated, tag="6")]
pub authorizer_policies: ::prost::alloc::vec::Vec<Policy>,
#[prost(message, repeated, tag="7")]
pub generated_facts: ::prost::alloc::vec::Vec<GeneratedFacts>,
#[prost(uint64, required, tag="8")]
pub iterations: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Origin {
#[prost(oneof="origin::Content", tags="1, 2")]
pub content: ::core::option::Option<origin::Content>,
}
/// Nested message and enum types in `Origin`.
pub mod origin {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Content {
#[prost(message, tag="1")]
Authorizer(super::Empty),
#[prost(uint32, tag="2")]
Origin(u32),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Empty {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GeneratedFacts {
#[prost(message, repeated, tag="1")]
pub origins: ::prost::alloc::vec::Vec<Origin>,
#[prost(message, repeated, tag="2")]
pub facts: ::prost::alloc::vec::Vec<FactV2>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SnapshotBlock {
#[prost(string, optional, tag="1")]
pub context: ::core::option::Option<::prost::alloc::string::String>,
#[prost(uint32, optional, tag="2")]
pub version: ::core::option::Option<u32>,
#[prost(message, repeated, tag="3")]
pub facts_v2: ::prost::alloc::vec::Vec<FactV2>,
#[prost(message, repeated, tag="4")]
pub rules_v2: ::prost::alloc::vec::Vec<RuleV2>,
#[prost(message, repeated, tag="5")]
pub checks_v2: ::prost::alloc::vec::Vec<CheckV2>,
#[prost(message, repeated, tag="6")]
pub scope: ::prost::alloc::vec::Vec<Scope>,
#[prost(message, optional, tag="7")]
pub external_key: ::core::option::Option<PublicKey>,
}
Loading

0 comments on commit a93b477

Please sign in to comment.