use std::{path::Path, time::Instant};
use cedar_policy::{Authorizer, Decision, Entities, PolicySet, Response};
use clap::Args;
use miette::Report;
use crate::{load_entities, CedarExitCode, OptionalSchemaArgs, PoliciesArgs, RequestArgs};
#[derive(Args, Debug)]
pub struct AuthorizeArgs {
#[command(flatten)]
pub request: RequestArgs,
#[command(flatten)]
pub policies: PoliciesArgs,
#[command(flatten)]
pub schema: OptionalSchemaArgs,
#[arg(long = "entities", value_name = "FILE")]
pub entities_file: String,
#[arg(short, long)]
pub verbose: bool,
#[arg(short, long)]
pub timing: bool,
}
pub fn authorize(args: &AuthorizeArgs) -> CedarExitCode {
println!();
let ans = execute_request(
&args.request,
&args.policies,
&args.entities_file,
&args.schema,
args.timing,
);
match ans {
Ok(ans) => {
let status = match ans.decision() {
Decision::Allow => {
println!("ALLOW");
CedarExitCode::Success
}
Decision::Deny => {
println!("DENY");
CedarExitCode::AuthorizeDeny
}
};
if ans.diagnostics().errors().peekable().peek().is_some() {
println!();
for err in ans.diagnostics().errors() {
println!("{err}");
}
}
if args.verbose {
println!();
if ans.diagnostics().reason().peekable().peek().is_none() {
println!("note: no policies applied to this request");
} else {
println!("note: this decision was due to the following policies:");
for reason in ans.diagnostics().reason() {
println!(" {reason}");
}
println!();
}
}
status
}
Err(errs) => {
for err in errs {
println!("{err:?}");
}
CedarExitCode::Failure
}
}
}
fn execute_request(
request: &RequestArgs,
policies: &PoliciesArgs,
entities_filename: impl AsRef<Path>,
schema: &OptionalSchemaArgs,
compute_duration: bool,
) -> Result<Response, Vec<Report>> {
let mut errs = vec![];
let policies = match policies.get_policy_set() {
Ok(pset) => pset,
Err(e) => {
errs.push(e);
PolicySet::new()
}
};
let schema = match schema.get_schema() {
Ok(opt) => opt,
Err(e) => {
errs.push(e);
None
}
};
let entities = match load_entities(entities_filename, schema.as_ref()) {
Ok(entities) => entities,
Err(e) => {
errs.push(e);
Entities::empty()
}
};
match request.get_request(schema.as_ref()) {
Ok(request) if errs.is_empty() => {
let authorizer = Authorizer::new();
let auth_start = Instant::now();
let ans = authorizer.is_authorized(&request, &policies, &entities);
let auth_dur = auth_start.elapsed();
if compute_duration {
println!(
"Authorization Time (micro seconds) : {}",
auth_dur.as_micros()
);
}
Ok(ans)
}
Ok(_) => Err(errs),
Err(e) => {
errs.push(e.wrap_err("failed to parse request"));
Err(errs)
}
}
}