Skip to content

Commit

Permalink
Implement query_exactly_one
Browse files Browse the repository at this point in the history
  • Loading branch information
baranyildirim committed Mar 24, 2024
1 parent c94c085 commit a440e73
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
2 changes: 2 additions & 0 deletions biscuit-auth/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ pub enum RunLimit {
TooManyIterations,
#[error("spent too much time verifying")]
Timeout,
#[error("Unexpected query results, expected {0} got {1}")]
UnexpectedQueryResult(usize, usize),
}

#[cfg(test)]
Expand Down
89 changes: 89 additions & 0 deletions biscuit-auth/src/token/authorizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,40 @@ impl Authorizer {
self.query_with_limits(rule, limits)
}

/// Run a query over the authorizer's Datalog engine to gather data.
/// If there is more than one result, this function will throw an error.
///
/// ```rust
/// # use biscuit_auth::KeyPair;
/// # use biscuit_auth::Biscuit;
/// let keypair = KeyPair::new();
/// let mut builder = Biscuit::builder();
/// builder.add_fact("user(\"John Doe\", 42)");
///
/// let biscuit = builder.build(&keypair).unwrap();
///
/// let mut authorizer = biscuit.authorizer().unwrap();
/// let res: (String, i64) = authorizer.query_exactly_one("data($name, $id) <- user($name, $id)").unwrap();
/// assert_eq!(res.0, "John Doe");
/// assert_eq!(res.1, 42);
/// ```
pub fn query_exactly_one<R: TryInto<Rule>, T: TryFrom<Fact, Error = E>, E: Into<error::Token>>(
&mut self,
rule: R,
) -> Result<T, error::Token>
where
error::Token: From<<R as TryInto<Rule>>::Error>,
{
let mut res: Vec<T> = self.query(rule)?;
if res.len() == 1 {
Ok(res.remove(0))
} else {
Err(error::Token::RunLimit(
error::RunLimit::UnexpectedQueryResult(1, res.len()),
))
}
}

/// run a query over the authorizer's Datalog engine to gather data
///
/// this only sees facts from the authorizer and the authority block
Expand Down Expand Up @@ -1518,6 +1552,61 @@ mod tests {
assert_eq!(res[0].0, "John Doe");
}

#[test]
fn query_exactly_one_authorizer_from_token_string() {
use crate::Biscuit;
use crate::KeyPair;
let keypair = KeyPair::new();
let mut builder = Biscuit::builder();
builder.add_fact("user(\"John Doe\")").unwrap();

let biscuit = builder.build(&keypair).unwrap();

let mut authorizer = biscuit.authorizer().unwrap();
let res: (String,) = authorizer
.query_exactly_one("data($name) <- user($name)")
.unwrap();
assert_eq!(res.0, "John Doe");
}

#[test]
fn query_exactly_one_no_results() {
use crate::Biscuit;
use crate::KeyPair;
let keypair = KeyPair::new();
let builder = Biscuit::builder();

let biscuit = builder.build(&keypair).unwrap();

let mut authorizer = biscuit.authorizer().unwrap();
let res: Result<(String,), error::Token> =
authorizer.query_exactly_one("data($name) <- user($name)");
assert_eq!(
res.unwrap_err().to_string(),
"Reached Datalog execution limits"
);
}

#[test]
fn query_exactly_one_too_many_results() {
use crate::Biscuit;
use crate::KeyPair;
let keypair = KeyPair::new();
let mut builder = Biscuit::builder();
builder.add_fact("user(\"John Doe\")").unwrap();
builder.add_fact("user(\"Jane Doe\")").unwrap();

let biscuit = builder.build(&keypair).unwrap();

let mut authorizer = biscuit.authorizer().unwrap();
let res: Result<(String,), error::Token> =
authorizer.query_exactly_one("data($name) <- user($name)");
assert_eq!(
res.unwrap_err().to_string(),
"Reached Datalog execution limits"
);
}

#[test]
fn authorizer_with_scopes() {
let root = KeyPair::new();
Expand Down

0 comments on commit a440e73

Please sign in to comment.