1#![allow(clippy::needless_return)]
21
22mod err;
23
24use clap::{Args, Parser, Subcommand, ValueEnum};
25use miette::{miette, NamedSource, Report, Result, WrapErr};
26use serde::{Deserialize, Serialize};
27use std::{
28 collections::HashMap,
29 fmt::{self, Display},
30 fs::OpenOptions,
31 path::Path,
32 process::{ExitCode, Termination},
33 str::FromStr,
34 time::Instant,
35};
36
37use cedar_policy::*;
38use cedar_policy_formatter::{policies_str_to_pretty, Config};
39
40use crate::err::IntoDiagnostic;
41
42#[derive(Parser)]
44#[command(author, version, about, long_about = None)] pub struct Cli {
46 #[command(subcommand)]
47 pub command: Commands,
48 #[arg(
50 global = true,
51 short = 'f',
52 long = "error-format",
53 env = "CEDAR_ERROR_FORMAT",
54 default_value_t,
55 value_enum
56 )]
57 pub err_fmt: ErrorFormat,
58}
59
60#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ValueEnum)]
61pub enum ErrorFormat {
62 #[default]
65 Human,
66 Plain,
69 Json,
71}
72
73impl Display for ErrorFormat {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(
76 f,
77 "{}",
78 match self {
79 ErrorFormat::Human => "human",
80 ErrorFormat::Plain => "plain",
81 ErrorFormat::Json => "json",
82 }
83 )
84 }
85}
86
87#[derive(Subcommand, Debug)]
88pub enum Commands {
89 Authorize(AuthorizeArgs),
91 Evaluate(EvaluateArgs),
93 Validate(ValidateArgs),
95 CheckParse(CheckParseArgs),
97 Link(LinkArgs),
99 Format(FormatArgs),
101}
102
103#[derive(Args, Debug)]
104pub struct ValidateArgs {
105 #[arg(short, long = "schema", value_name = "FILE")]
107 pub schema_file: String,
108 #[arg(short, long = "policies", value_name = "FILE")]
110 pub policies_file: String,
111}
112
113#[derive(Args, Debug)]
114pub struct CheckParseArgs {
115 #[clap(short, long = "policies", value_name = "FILE")]
117 pub policies_file: Option<String>,
118}
119
120#[derive(Args, Debug)]
122pub struct RequestArgs {
123 #[arg(short, long)]
125 pub principal: Option<String>,
126 #[arg(short, long)]
128 pub action: Option<String>,
129 #[arg(short, long)]
131 pub resource: Option<String>,
132 #[arg(short, long = "context", value_name = "FILE")]
135 pub context_json_file: Option<String>,
136 #[arg(long = "request-json", value_name = "FILE", conflicts_with_all = &["principal", "action", "resource", "context_json_file"])]
141 pub request_json_file: Option<String>,
142}
143
144impl RequestArgs {
145 fn get_request(&self, schema: Option<&Schema>) -> Result<Request> {
147 match &self.request_json_file {
148 Some(jsonfile) => {
149 let jsonstring = std::fs::read_to_string(jsonfile)
150 .into_diagnostic()
151 .wrap_err_with(|| format!("failed to open request-json file {jsonfile}"))?;
152 let qjson: RequestJSON = serde_json::from_str(&jsonstring)
153 .into_diagnostic()
154 .wrap_err_with(|| format!("failed to parse request-json file {jsonfile}"))?;
155 let principal = qjson
156 .principal
157 .map(|s| {
158 s.parse().wrap_err_with(|| {
159 format!("failed to parse principal in {jsonfile} as entity Uid")
160 })
161 })
162 .transpose()?;
163 let action = qjson
164 .action
165 .map(|s| {
166 s.parse().wrap_err_with(|| {
167 format!("failed to parse action in {jsonfile} as entity Uid")
168 })
169 })
170 .transpose()?;
171 let resource = qjson
172 .resource
173 .map(|s| {
174 s.parse().wrap_err_with(|| {
175 format!("failed to parse resource in {jsonfile} as entity Uid")
176 })
177 })
178 .transpose()?;
179 let context = Context::from_json_value(
180 qjson.context,
181 schema.and_then(|s| Some((s, action.as_ref()?))),
182 )
183 .into_diagnostic()
184 .wrap_err_with(|| format!("failed to create a context from {jsonfile}"))?;
185 Ok(Request::new(principal, action, resource, context))
186 }
187 None => {
188 let principal = self
189 .principal
190 .as_ref()
191 .map(|s| {
192 s.parse().wrap_err_with(|| {
193 format!("failed to parse principal {s} as entity Uid")
194 })
195 })
196 .transpose()?;
197 let action = self
198 .action
199 .as_ref()
200 .map(|s| {
201 s.parse()
202 .wrap_err_with(|| format!("failed to parse action {s} as entity Uid"))
203 })
204 .transpose()?;
205 let resource = self
206 .resource
207 .as_ref()
208 .map(|s| {
209 s.parse()
210 .wrap_err_with(|| format!("failed to parse resource {s} as entity Uid"))
211 })
212 .transpose()?;
213 let context: Context = match &self.context_json_file {
214 None => Context::empty(),
215 Some(jsonfile) => match std::fs::OpenOptions::new().read(true).open(jsonfile) {
216 Ok(f) => Context::from_json_file(
217 f,
218 schema.and_then(|s| Some((s, action.as_ref()?))),
219 )
220 .into_diagnostic()
221 .wrap_err_with(|| format!("failed to create a context from {jsonfile}"))?,
222 Err(e) => Err(e).into_diagnostic().wrap_err_with(|| {
223 format!("error while loading context from {jsonfile}")
224 })?,
225 },
226 };
227 Ok(Request::new(principal, action, resource, context))
228 }
229 }
230 }
231}
232
233#[derive(Args, Debug)]
234pub struct AuthorizeArgs {
235 #[command(flatten)]
237 pub request: RequestArgs,
238 #[arg(long = "policies", value_name = "FILE")]
240 pub policies_file: String,
241 #[arg(long = "template-linked", value_name = "FILE")]
243 pub template_linked_file: Option<String>,
244 #[arg(long = "schema", value_name = "FILE")]
248 pub schema_file: Option<String>,
249 #[arg(long = "entities", value_name = "FILE")]
251 pub entities_file: String,
252 #[arg(short, long)]
254 pub verbose: bool,
255 #[arg(short, long)]
257 pub timing: bool,
258}
259
260#[derive(Args, Debug)]
261pub struct LinkArgs {
262 #[arg(short, long)]
264 pub policies_file: String,
265 #[arg(short, long)]
267 pub template_linked_file: String,
268 #[arg(long)]
270 pub template_id: String,
271 #[arg(short, long)]
273 pub new_id: String,
274 #[arg(short, long)]
276 pub arguments: Arguments,
277}
278
279#[derive(Args, Debug)]
280pub struct FormatArgs {
281 #[arg(value_name = "FILE")]
283 pub file_name: Option<String>,
284
285 #[arg(short, long, value_name = "UINT", default_value_t = 80)]
287 pub line_width: usize,
288
289 #[arg(short, long, value_name = "INT", default_value_t = 2)]
291 pub indent_width: isize,
292}
293
294#[derive(Clone, Debug, Deserialize)]
296#[serde(try_from = "HashMap<String,String>")]
297pub struct Arguments {
298 pub data: HashMap<SlotId, String>,
299}
300
301impl TryFrom<HashMap<String, String>> for Arguments {
302 type Error = String;
303
304 fn try_from(value: HashMap<String, String>) -> Result<Self, Self::Error> {
305 Ok(Self {
306 data: value
307 .into_iter()
308 .map(|(k, v)| parse_slot_id(k).map(|slot_id| (slot_id, v)))
309 .collect::<Result<HashMap<SlotId, String>, String>>()?,
310 })
311 }
312}
313
314impl FromStr for Arguments {
315 type Err = serde_json::Error;
316
317 fn from_str(s: &str) -> Result<Self, Self::Err> {
318 serde_json::from_str(s)
319 }
320}
321
322#[derive(Deserialize)]
324struct RequestJSON {
325 #[serde(default)]
327 principal: Option<String>,
328 #[serde(default)]
330 action: Option<String>,
331 #[serde(default)]
333 resource: Option<String>,
334 context: serde_json::Value,
336}
337
338#[derive(Args, Debug)]
339pub struct EvaluateArgs {
340 #[command(flatten)]
342 pub request: RequestArgs,
343 #[arg(long = "schema", value_name = "FILE")]
347 pub schema_file: Option<String>,
348 #[arg(long = "entities", value_name = "FILE")]
351 pub entities_file: Option<String>,
352 #[arg(value_name = "EXPRESSION")]
354 pub expression: String,
355}
356
357#[derive(Eq, PartialEq, Debug)]
358pub enum CedarExitCode {
359 Success,
362 Failure,
364 AuthorizeDeny,
367 ValidationFailure,
370}
371
372impl Termination for CedarExitCode {
373 fn report(self) -> ExitCode {
374 match self {
375 CedarExitCode::Success => ExitCode::SUCCESS,
376 CedarExitCode::Failure => ExitCode::FAILURE,
377 CedarExitCode::AuthorizeDeny => ExitCode::from(2),
378 CedarExitCode::ValidationFailure => ExitCode::from(3),
379 }
380 }
381}
382
383pub fn check_parse(args: &CheckParseArgs) -> CedarExitCode {
384 match read_policy_set(args.policies_file.as_ref()) {
385 Ok(_) => CedarExitCode::Success,
386 Err(e) => {
387 println!("Error: {e:?}");
388 CedarExitCode::Failure
389 }
390 }
391}
392
393pub fn validate(args: &ValidateArgs) -> CedarExitCode {
394 let pset = match read_policy_set(Some(&args.policies_file)) {
395 Ok(pset) => pset,
396 Err(e) => {
397 println!("Error: {e:?}");
398 return CedarExitCode::Failure;
399 }
400 };
401
402 let schema = match read_schema_file(&args.schema_file) {
403 Ok(schema) => schema,
404 Err(e) => {
405 println!("Error: {e:?}");
406 return CedarExitCode::Failure;
407 }
408 };
409
410 let validator = Validator::new(schema);
411 let result = validator.validate(&pset, ValidationMode::default());
412 if result.validation_passed() {
413 println!("Validation Passed");
414 return CedarExitCode::Success;
415 } else {
416 println!("Validation Results:");
417 for note in result.validation_errors() {
418 println!("{}", note);
419 }
420 return CedarExitCode::ValidationFailure;
421 }
422}
423
424pub fn evaluate(args: &EvaluateArgs) -> (CedarExitCode, EvalResult) {
425 println!();
426 let schema = match args.schema_file.as_ref().map(read_schema_file) {
427 None => None,
428 Some(Ok(schema)) => Some(schema),
429 Some(Err(e)) => {
430 println!("Error: {e:?}");
431 return (CedarExitCode::Failure, EvalResult::Bool(false));
432 }
433 };
434 let request = match args.request.get_request(schema.as_ref()) {
435 Ok(q) => q,
436 Err(e) => {
437 println!("Error: {e:?}");
438 return (CedarExitCode::Failure, EvalResult::Bool(false));
439 }
440 };
441 let expr =
442 match Expression::from_str(&args.expression).wrap_err("failed to parse the expression") {
443 Ok(expr) => expr,
444 Err(e) => {
445 println!("Error: {e:?}");
446 return (CedarExitCode::Failure, EvalResult::Bool(false));
447 }
448 };
449 let entities = match &args.entities_file {
450 None => Entities::empty(),
451 Some(file) => match load_entities(file, schema.as_ref()) {
452 Ok(entities) => entities,
453 Err(e) => {
454 println!("Error: {e:?}");
455 return (CedarExitCode::Failure, EvalResult::Bool(false));
456 }
457 },
458 };
459 let entities = match load_actions_from_schema(entities, &schema) {
460 Ok(entities) => entities,
461 Err(e) => {
462 println!("Error: {e:?}");
463 return (CedarExitCode::Failure, EvalResult::Bool(false));
464 }
465 };
466 match eval_expression(&request, &entities, &expr)
467 .into_diagnostic()
468 .wrap_err("failed to evaluate the expression")
469 {
470 Err(e) => {
471 println!("Error: {e:?}");
472 return (CedarExitCode::Failure, EvalResult::Bool(false));
473 }
474 Ok(result) => {
475 println!("{result}");
476 return (CedarExitCode::Success, result);
477 }
478 }
479}
480
481pub fn link(args: &LinkArgs) -> CedarExitCode {
482 if let Err(err) = link_inner(args) {
483 println!("Error: {err:?}");
484 CedarExitCode::Failure
485 } else {
486 CedarExitCode::Success
487 }
488}
489
490fn format_policies_inner(args: &FormatArgs) -> Result<()> {
491 let policies_str = read_from_file_or_stdin(args.file_name.as_ref(), "policy set")?;
492 let config = Config {
493 line_width: args.line_width,
494 indent_width: args.indent_width,
495 };
496 println!("{}", policies_str_to_pretty(&policies_str, &config)?);
497 Ok(())
498}
499
500pub fn format_policies(args: &FormatArgs) -> CedarExitCode {
501 if let Err(err) = format_policies_inner(args) {
502 println!("Error: {err:?}");
503 CedarExitCode::Failure
504 } else {
505 CedarExitCode::Success
506 }
507}
508
509fn create_slot_env(data: &HashMap<SlotId, String>) -> Result<HashMap<SlotId, EntityUid>> {
510 data.iter()
511 .map(|(key, value)| Ok(EntityUid::from_str(value).map(|euid| (key.clone(), euid))?))
512 .collect::<Result<HashMap<SlotId, EntityUid>>>()
513}
514
515fn link_inner(args: &LinkArgs) -> Result<()> {
516 let mut policies = read_policy_set(Some(&args.policies_file))?;
517 let slotenv = create_slot_env(&args.arguments.data)?;
518 policies
519 .link(
520 PolicyId::from_str(&args.template_id)?,
521 PolicyId::from_str(&args.new_id)?,
522 slotenv,
523 )
524 .into_diagnostic()?;
525 let linked = policies
526 .policy(&PolicyId::from_str(&args.new_id)?)
527 .ok_or_else(|| miette!("Failed to add template-linked policy"))?;
528 println!("Template Linked Policy Added: {linked}");
529 let linked = TemplateLinked {
530 template_id: args.template_id.clone(),
531 link_id: args.new_id.clone(),
532 args: args.arguments.data.clone(),
533 };
534
535 update_template_linked_file(&args.template_linked_file, linked)
536}
537
538#[derive(Clone, Serialize, Deserialize, Debug)]
539#[serde(try_from = "LiteralTemplateLinked")]
540#[serde(into = "LiteralTemplateLinked")]
541struct TemplateLinked {
542 template_id: String,
543 link_id: String,
544 args: HashMap<SlotId, String>,
545}
546
547impl TryFrom<LiteralTemplateLinked> for TemplateLinked {
548 type Error = String;
549
550 fn try_from(value: LiteralTemplateLinked) -> Result<Self, Self::Error> {
551 Ok(Self {
552 template_id: value.template_id,
553 link_id: value.link_id,
554 args: value
555 .args
556 .into_iter()
557 .map(|(k, v)| parse_slot_id(k).map(|slot_id| (slot_id, v)))
558 .collect::<Result<HashMap<SlotId, String>, Self::Error>>()?,
559 })
560 }
561}
562
563fn parse_slot_id<S: AsRef<str>>(s: S) -> Result<SlotId, String> {
564 match s.as_ref() {
565 "?principal" => Ok(SlotId::principal()),
566 "?resource" => Ok(SlotId::resource()),
567 _ => Err(format!(
568 "Invalid SlotId! Expected ?principal|?resource, got: {}",
569 s.as_ref()
570 )),
571 }
572}
573
574#[derive(Serialize, Deserialize)]
575struct LiteralTemplateLinked {
576 template_id: String,
577 link_id: String,
578 args: HashMap<String, String>,
579}
580
581impl From<TemplateLinked> for LiteralTemplateLinked {
582 fn from(i: TemplateLinked) -> Self {
583 Self {
584 template_id: i.template_id,
585 link_id: i.link_id,
586 args: i
587 .args
588 .into_iter()
589 .map(|(k, v)| (format!("{k}"), v))
590 .collect(),
591 }
592 }
593}
594
595fn add_template_links_to_set(path: impl AsRef<Path>, policy_set: &mut PolicySet) -> Result<()> {
597 for template_linked in load_liked_file(path)? {
598 let slot_env = create_slot_env(&template_linked.args)?;
599 policy_set
600 .link(
601 PolicyId::from_str(&template_linked.template_id)?,
602 PolicyId::from_str(&template_linked.link_id)?,
603 slot_env,
604 )
605 .into_diagnostic()?;
606 }
607 Ok(())
608}
609
610fn load_liked_file(path: impl AsRef<Path>) -> Result<Vec<TemplateLinked>> {
612 let f = match std::fs::File::open(path) {
613 Ok(f) => f,
614 Err(_) => {
615 return Ok(vec![]);
617 }
618 };
619 if f.metadata()
620 .into_diagnostic()
621 .wrap_err("Failed to read metadata")?
622 .len()
623 == 0
624 {
625 Ok(vec![])
627 } else {
628 serde_json::from_reader(f)
630 .into_diagnostic()
631 .wrap_err("Deserialization error")
632 }
633}
634
635fn update_template_linked_file(path: impl AsRef<Path>, new_linked: TemplateLinked) -> Result<()> {
637 let mut template_linked = load_liked_file(path.as_ref())?;
638 template_linked.push(new_linked);
639 write_template_linked_file(&template_linked, path.as_ref())
640}
641
642fn write_template_linked_file(linked: &[TemplateLinked], path: impl AsRef<Path>) -> Result<()> {
644 let f = OpenOptions::new()
645 .write(true)
646 .truncate(true)
647 .create(true)
648 .open(path)
649 .into_diagnostic()?;
650 serde_json::to_writer(f, linked).into_diagnostic()
651}
652
653pub fn authorize(args: &AuthorizeArgs) -> CedarExitCode {
654 println!();
655 let ans = execute_request(
656 &args.request,
657 &args.policies_file,
658 args.template_linked_file.as_ref(),
659 &args.entities_file,
660 args.schema_file.as_ref(),
661 args.timing,
662 );
663 match ans {
664 Ok(ans) => {
665 let status = match ans.decision() {
666 Decision::Allow => {
667 println!("ALLOW");
668 CedarExitCode::Success
669 }
670 Decision::Deny => {
671 println!("DENY");
672 CedarExitCode::AuthorizeDeny
673 }
674 };
675 if ans.diagnostics().errors().peekable().peek().is_some() {
676 println!();
677 for err in ans.diagnostics().errors() {
678 println!("{err}");
679 }
680 }
681 if args.verbose {
682 println!();
683 if ans.diagnostics().reason().peekable().peek().is_none() {
684 println!("note: no policies applied to this request");
685 } else {
686 println!("note: this decision was due to the following policies:");
687 for reason in ans.diagnostics().reason() {
688 println!(" {}", reason);
689 }
690 println!();
691 }
692 }
693 status
694 }
695 Err(errs) => {
696 for err in errs {
697 println!("{err:?}");
698 }
699 CedarExitCode::Failure
700 }
701 }
702}
703
704fn load_entities(entities_filename: impl AsRef<Path>, schema: Option<&Schema>) -> Result<Entities> {
706 match std::fs::OpenOptions::new()
707 .read(true)
708 .open(entities_filename.as_ref())
709 {
710 Ok(f) => Entities::from_json_file(f, schema)
711 .into_diagnostic()
712 .wrap_err_with(|| {
713 format!(
714 "failed to parse entities from file {}",
715 entities_filename.as_ref().display()
716 )
717 }),
718 Err(e) => Err(e).into_diagnostic().wrap_err_with(|| {
719 format!(
720 "failed to open entities file {}",
721 entities_filename.as_ref().display()
722 )
723 }),
724 }
725}
726
727fn rename_from_id_annotation(ps: PolicySet) -> Result<PolicySet> {
734 let mut new_ps = PolicySet::new();
735 let t_iter = ps.templates().map(|t| match t.annotation("id") {
736 None => Ok(t.clone()),
737 Some(anno) => anno.parse().map(|a| t.new_id(a)),
738 });
739 for t in t_iter {
740 let template = t
741 .into_diagnostic()
742 .wrap_err("failed to parse policy id annotation")?;
743 new_ps
744 .add_template(template)
745 .into_diagnostic()
746 .wrap_err("failed to add template to policy set")?;
747 }
748 let p_iter = ps.policies().map(|p| match p.annotation("id") {
749 None => Ok(p.clone()),
750 Some(anno) => anno.parse().map(|a| p.new_id(a)),
751 });
752 for p in p_iter {
753 let policy = p
754 .into_diagnostic()
755 .wrap_err("failed to parse policy id annotation")?;
756 new_ps
757 .add(policy)
758 .into_diagnostic()
759 .wrap_err("failed to add template to policy set")?;
760 }
761 Ok(new_ps)
762}
763
764fn read_policy_and_links(
765 policies_filename: impl AsRef<Path>,
766 links_filename: Option<impl AsRef<Path>>,
767) -> Result<PolicySet> {
768 let mut pset = read_policy_set(Some(policies_filename.as_ref()))?;
769 if let Some(links_filename) = links_filename {
770 add_template_links_to_set(links_filename.as_ref(), &mut pset)?;
771 }
772 Ok(pset)
773}
774
775fn read_from_file_or_stdin(filename: Option<impl AsRef<Path>>, context: &str) -> Result<String> {
777 let mut src_str = String::new();
778 match filename.as_ref() {
779 Some(path) => {
780 src_str = std::fs::read_to_string(path)
781 .into_diagnostic()
782 .wrap_err_with(|| {
783 format!(
784 "failed to open {} file {}",
785 context,
786 path.as_ref().display()
787 )
788 })?;
789 }
790 None => {
791 std::io::Read::read_to_string(&mut std::io::stdin(), &mut src_str)
792 .into_diagnostic()
793 .wrap_err_with(|| format!("failed to read {} from stdin", context))?;
794 }
795 };
796 Ok(src_str)
797}
798
799fn read_from_file(filename: impl AsRef<Path>, context: &str) -> Result<String> {
801 read_from_file_or_stdin(Some(filename), context)
802}
803
804fn read_policy_set(
805 filename: Option<impl AsRef<Path> + std::marker::Copy>,
806) -> miette::Result<PolicySet> {
807 let context = "policy set";
808 let ps_str = read_from_file_or_stdin(filename, context)?;
809 let ps = PolicySet::from_str(&ps_str)
810 .map_err(|err| {
811 let name = filename.map_or_else(
812 || "<stdin>".to_owned(),
813 |n| n.as_ref().display().to_string(),
814 );
815 Report::new(err).with_source_code(NamedSource::new(name, ps_str))
816 })
817 .wrap_err_with(|| format!("failed to parse {context}"))?;
818 rename_from_id_annotation(ps)
819}
820
821fn read_schema_file(filename: impl AsRef<Path> + std::marker::Copy) -> Result<Schema> {
822 let schema_src = read_from_file(filename, "schema")?;
823 Schema::from_str(&schema_src)
824 .into_diagnostic()
825 .wrap_err_with(|| {
826 format!(
827 "failed to parse schema from file {}",
828 filename.as_ref().display()
829 )
830 })
831}
832
833fn load_actions_from_schema(entities: Entities, schema: &Option<Schema>) -> Result<Entities> {
834 match schema {
835 Some(schema) => match schema.action_entities() {
836 Ok(action_entities) => Entities::from_entities(
837 entities
838 .iter()
839 .cloned()
840 .chain(action_entities.iter().cloned()),
841 )
842 .into_diagnostic()
843 .wrap_err("failed to merge action entities with entity file"),
844 Err(e) => Err(e)
845 .into_diagnostic()
846 .wrap_err("failed to construct action entities"),
847 },
848 None => Ok(entities),
849 }
850}
851
852fn execute_request(
854 request: &RequestArgs,
855 policies_filename: impl AsRef<Path> + std::marker::Copy,
856 links_filename: Option<impl AsRef<Path>>,
857 entities_filename: impl AsRef<Path>,
858 schema_filename: Option<impl AsRef<Path> + std::marker::Copy>,
859 compute_duration: bool,
860) -> Result<Response, Vec<Report>> {
861 let mut errs = vec![];
862 let policies = match read_policy_and_links(policies_filename.as_ref(), links_filename) {
863 Ok(pset) => pset,
864 Err(e) => {
865 errs.push(e);
866 PolicySet::new()
867 }
868 };
869 let schema = match schema_filename.map(read_schema_file) {
870 None => None,
871 Some(Ok(schema)) => Some(schema),
872 Some(Err(e)) => {
873 errs.push(e);
874 None
875 }
876 };
877 let entities = match load_entities(entities_filename, schema.as_ref()) {
878 Ok(entities) => entities,
879 Err(e) => {
880 errs.push(e);
881 Entities::empty()
882 }
883 };
884 let entities = match load_actions_from_schema(entities, &schema) {
885 Ok(entities) => entities,
886 Err(e) => {
887 errs.push(e);
888 Entities::empty()
889 }
890 };
891 match request.get_request(schema.as_ref()) {
892 Ok(request) if errs.is_empty() => {
893 let authorizer = Authorizer::new();
894 let auth_start = Instant::now();
895 let ans = authorizer.is_authorized(&request, &policies, &entities);
896 let auth_dur = auth_start.elapsed();
897 if compute_duration {
898 println!(
899 "Authorization Time (micro seconds) : {}",
900 auth_dur.as_micros()
901 );
902 }
903 Ok(ans)
904 }
905 Ok(_) => Err(errs),
906 Err(e) => {
907 errs.push(e.wrap_err("failed to parse request"));
908 Err(errs)
909 }
910 }
911}