1extern crate biscuit_auth as biscuit;
2
3use crate::verify::{biscuit_key_from_string, ServiceNode};
4
5use biscuit::macros::{biscuit, check, rule};
6use biscuit::BiscuitBuilder;
7use chrono::Utc;
8use hessra_token_core::{Biscuit, KeyPair, TokenTimeConfig};
9use std::error::Error;
10use tracing::info;
11
12pub struct HessraAuthorization {
42 subject: Option<String>,
43 resource: Option<String>,
44 operation: Option<String>,
45 time_config: TokenTimeConfig,
46 service_chain_nodes: Option<Vec<ServiceNode>>,
47 multi_party_nodes: Option<Vec<ServiceNode>>,
48 domain: Option<String>,
49}
50
51impl HessraAuthorization {
52 pub fn new(
63 subject: String,
64 resource: String,
65 operation: String,
66 time_config: TokenTimeConfig,
67 ) -> Self {
68 Self {
69 subject: Some(subject),
70 resource: Some(resource),
71 operation: Some(operation),
72 time_config,
73 service_chain_nodes: None,
74 multi_party_nodes: None,
75 domain: None,
76 }
77 }
78
79 pub fn service_chain(mut self, nodes: Vec<ServiceNode>) -> Self {
90 self.service_chain_nodes = Some(nodes);
91 self
92 }
93
94 pub fn multi_party(mut self, nodes: Vec<ServiceNode>) -> Self {
106 self.multi_party_nodes = Some(nodes);
107 self
108 }
109
110 pub fn domain_restricted(mut self, domain: String) -> Self {
120 self.domain = Some(domain);
121 self
122 }
123
124 pub fn issue(self, keypair: &KeyPair) -> Result<String, Box<dyn Error>> {
132 let start_time = self
133 .time_config
134 .start_time
135 .unwrap_or_else(|| Utc::now().timestamp());
136 let expiration = start_time + self.time_config.duration;
137
138 let domain = self.domain;
139
140 let subject = self.subject.ok_or("Token requires subject")?;
142 let resource = self.resource.ok_or("Token requires resource")?;
143 let operation = self.operation.ok_or("Token requires operation")?;
144
145 let mut biscuit_builder = biscuit!(
147 r#"
148 right({subject}, {resource}, {operation});
149 check if subject($sub), resource($res), operation($op), right($sub, $res, $op);
150 check if time($time), $time < {expiration};
151 "#
152 );
153
154 if let Some(domain) = domain {
156 biscuit_builder = biscuit_builder.check(check!(
157 r#"
158 check if domain({domain});
159 "#
160 ))?;
161 }
162
163 if let Some(nodes) = self.service_chain_nodes {
165 for node in nodes {
166 let component = node.component;
167 let public_key = biscuit_key_from_string(node.public_key)?;
168 biscuit_builder = biscuit_builder.rule(rule!(
169 r#"
170 node($s, {component}) <- service($s) trusting {public_key};
171 "#
172 ))?;
173 }
174 }
175
176 if let Some(nodes) = self.multi_party_nodes {
178 for node in nodes {
179 let component = node.component;
180 let public_key = biscuit_key_from_string(node.public_key)?;
181 biscuit_builder = biscuit_builder.check(check!(
182 r#"
183 check if namespace({component}) trusting {public_key};
184 "#
185 ))?;
186 }
187 }
188
189 let biscuit = biscuit_builder.build(keypair)?;
191 info!("biscuit (authority): {}", biscuit);
192 let token = biscuit.to_base64()?;
193 Ok(token)
194 }
195}
196
197fn _create_base_biscuit_builder(
213 subject: String,
214 resource: String,
215 operation: String,
216) -> Result<BiscuitBuilder, Box<dyn Error>> {
217 create_base_biscuit_builder_with_time(subject, resource, operation, TokenTimeConfig::default())
218}
219
220fn create_base_biscuit_builder_with_time(
237 subject: String,
238 resource: String,
239 operation: String,
240 time_config: TokenTimeConfig,
241) -> Result<BiscuitBuilder, Box<dyn Error>> {
242 let start_time = time_config
243 .start_time
244 .unwrap_or_else(|| Utc::now().timestamp());
245 let expiration = start_time + time_config.duration;
246
247 let biscuit_builder = biscuit!(
248 r#"
249 right({subject}, {resource}, {operation});
250 check if subject($sub), resource($res), operation($op), right($sub, $res, $op);
251 check if time($time), $time < {expiration};
252 "#
253 );
254
255 Ok(biscuit_builder)
256}
257
258pub fn create_raw_biscuit(
278 subject: String,
279 resource: String,
280 operation: String,
281 key: KeyPair,
282 time_config: TokenTimeConfig,
283) -> Result<Biscuit, Box<dyn Error>> {
284 let biscuit = create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?
285 .build(&key)?;
286
287 info!("biscuit (authority): {}", biscuit);
288
289 Ok(biscuit)
290}
291
292pub fn create_biscuit(
310 subject: String,
311 resource: String,
312 operation: String,
313 key: KeyPair,
314 time_config: TokenTimeConfig,
315) -> Result<Vec<u8>, Box<dyn Error>> {
316 let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
317 let token = biscuit.to_vec()?;
318 Ok(token)
319}
320
321fn create_base64_biscuit(
339 subject: String,
340 resource: String,
341 operation: String,
342 key: KeyPair,
343 time_config: TokenTimeConfig,
344) -> Result<String, Box<dyn Error>> {
345 let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
346 let token = biscuit.to_base64()?;
347 Ok(token)
348}
349
350pub fn create_token(
368 subject: String,
369 resource: String,
370 operation: String,
371 key: KeyPair,
372) -> Result<String, Box<dyn Error>> {
373 create_base64_biscuit(
374 subject,
375 resource,
376 operation,
377 key,
378 TokenTimeConfig::default(),
379 )
380}
381
382pub fn create_token_with_time(
400 subject: String,
401 resource: String,
402 operation: String,
403 key: KeyPair,
404 time_config: TokenTimeConfig,
405) -> Result<String, Box<dyn Error>> {
406 create_base64_biscuit(subject, resource, operation, key, time_config)
407}
408
409pub fn create_service_chain_biscuit(
428 subject: String,
429 resource: String,
430 operation: String,
431 key: KeyPair,
432 nodes: &Vec<ServiceNode>,
433 time_config: TokenTimeConfig,
434) -> Result<Vec<u8>, Box<dyn Error>> {
435 let biscuit =
436 create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
437 let token = biscuit.to_vec()?;
438 Ok(token)
439}
440
441fn create_base64_service_chain_biscuit(
460 subject: String,
461 resource: String,
462 operation: String,
463 key: KeyPair,
464 nodes: &Vec<ServiceNode>,
465 time_config: TokenTimeConfig,
466) -> Result<String, Box<dyn Error>> {
467 let biscuit =
468 create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
469 let token = biscuit.to_base64()?;
470 Ok(token)
471}
472
473pub fn create_raw_service_chain_biscuit(
493 subject: String,
494 resource: String,
495 operation: String,
496 key: KeyPair,
497 nodes: &Vec<ServiceNode>,
498 time_config: TokenTimeConfig,
499) -> Result<Biscuit, Box<dyn Error>> {
500 create_service_chain_biscuit_with_time(subject, resource, operation, key, nodes, time_config)
501}
502
503pub fn create_service_chain_token(
523 subject: String,
524 resource: String,
525 operation: String,
526 key: KeyPair,
527 nodes: &Vec<ServiceNode>,
528) -> Result<String, Box<dyn Error>> {
529 create_base64_service_chain_biscuit(
530 subject,
531 resource,
532 operation,
533 key,
534 nodes,
535 TokenTimeConfig::default(),
536 )
537}
538
539pub fn create_service_chain_biscuit_with_time(
558 subject: String,
559 resource: String,
560 operation: String,
561 key: KeyPair,
562 nodes: &Vec<ServiceNode>,
563 time_config: TokenTimeConfig,
564) -> Result<Biscuit, Box<dyn Error>> {
565 let service = resource.clone();
566 let mut biscuit_builder =
567 create_base_biscuit_builder_with_time(subject, service, operation, time_config)?;
568
569 for node in nodes {
571 let component = node.component.clone();
572 let public_key = biscuit_key_from_string(node.public_key.clone())?;
573 biscuit_builder = biscuit_builder.rule(rule!(
574 r#"
575 node($s, {component}) <- service($s) trusting {public_key};
576 "#
577 ))?;
578 }
579
580 let biscuit = biscuit_builder.build(&key)?;
581
582 info!("biscuit (authority): {}", biscuit);
583
584 Ok(biscuit)
585}
586
587pub fn create_service_chain_token_with_time(
607 subject: String,
608 resource: String,
609 operation: String,
610 key: KeyPair,
611 nodes: &Vec<ServiceNode>,
612 time_config: TokenTimeConfig,
613) -> Result<String, Box<dyn Error>> {
614 let biscuit = create_service_chain_biscuit_with_time(
615 subject,
616 resource,
617 operation,
618 key,
619 nodes,
620 time_config,
621 )?;
622 let token = biscuit.to_base64()?;
623 Ok(token)
624}
625
626pub fn create_raw_multi_party_biscuit(
642 subject: String,
643 resource: String,
644 operation: String,
645 key: KeyPair,
646 multi_party_nodes: &Vec<ServiceNode>,
647) -> Result<Biscuit, Box<dyn Error>> {
648 create_multi_party_biscuit_with_time(
649 subject,
650 resource,
651 operation,
652 key,
653 multi_party_nodes,
654 TokenTimeConfig::default(),
655 )
656}
657
658pub fn create_multi_party_biscuit(
679 subject: String,
680 resource: String,
681 operation: String,
682 key: KeyPair,
683 multi_party_nodes: &Vec<ServiceNode>,
684) -> Result<Vec<u8>, Box<dyn Error>> {
685 let biscuit =
686 create_raw_multi_party_biscuit(subject, resource, operation, key, multi_party_nodes)?;
687 let token = biscuit.to_vec()?;
688 Ok(token)
689}
690
691fn create_base64_multi_party_biscuit(
712 subject: String,
713 resource: String,
714 operation: String,
715 key: KeyPair,
716 multi_party_nodes: &Vec<ServiceNode>,
717 time_config: TokenTimeConfig,
718) -> Result<String, Box<dyn Error>> {
719 let biscuit = create_multi_party_biscuit_with_time(
720 subject,
721 resource,
722 operation,
723 key,
724 multi_party_nodes,
725 time_config,
726 )?;
727 let token = biscuit.to_base64()?;
728 Ok(token)
729}
730
731pub fn create_multi_party_token(
752 subject: String,
753 resource: String,
754 operation: String,
755 key: KeyPair,
756 multi_party_nodes: &Vec<ServiceNode>,
757) -> Result<String, Box<dyn Error>> {
758 create_base64_multi_party_biscuit(
759 subject,
760 resource,
761 operation,
762 key,
763 multi_party_nodes,
764 TokenTimeConfig::default(),
765 )
766}
767
768pub fn create_multi_party_biscuit_with_time(
790 subject: String,
791 resource: String,
792 operation: String,
793 key: KeyPair,
794 multi_party_nodes: &Vec<ServiceNode>,
795 time_config: TokenTimeConfig,
796) -> Result<Biscuit, Box<dyn Error>> {
797 let mut biscuit_builder =
798 create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?;
799
800 for node in multi_party_nodes {
801 let component = node.component.clone();
802 let public_key = biscuit_key_from_string(node.public_key.clone())?;
803 biscuit_builder = biscuit_builder.check(check!(
804 r#"
805 check if namespace({component}) trusting {public_key};
806 "#
807 ))?;
808 }
809
810 let biscuit = biscuit_builder.build(&key)?;
811
812 info!("biscuit (authority): {}", biscuit);
813
814 Ok(biscuit)
815}
816
817pub fn create_multi_party_token_with_time(
818 subject: String,
819 resource: String,
820 operation: String,
821 key: KeyPair,
822 multi_party_nodes: &Vec<ServiceNode>,
823 time_config: TokenTimeConfig,
824) -> Result<String, Box<dyn Error>> {
825 let biscuit = create_multi_party_biscuit_with_time(
826 subject,
827 resource,
828 operation,
829 key,
830 multi_party_nodes,
831 time_config,
832 )?;
833 let token = biscuit.to_base64()?;
834 Ok(token)
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840 use crate::verify::{verify_biscuit_local, verify_service_chain_biscuit_local};
841 use biscuit::macros::block;
842 use biscuit::Biscuit;
843 #[test]
844 fn test_create_biscuit() {
845 let subject = "test@test.com".to_owned();
846 let resource: String = "res1".to_string();
847 let operation = "read".to_string();
848 let root = KeyPair::new();
849 let public_key = root.public();
850 let token = create_biscuit(
851 subject.clone(),
852 resource.clone(),
853 operation.clone(),
854 root,
855 TokenTimeConfig::default(),
856 )
857 .unwrap();
858
859 let res = verify_biscuit_local(token, public_key, subject, resource, operation);
860 assert!(res.is_ok());
861 }
862
863 #[test]
864 fn test_biscuit_operations() {
865 let subject = "test@test.com".to_owned();
866 let resource = "res1".to_string();
867 let operation = "read".to_string();
868 let root = KeyPair::new();
869 let public_key = root.public();
870
871 let read_token = create_biscuit(
873 subject.clone(),
874 resource.clone(),
875 operation.clone(),
876 root,
877 TokenTimeConfig::default(),
878 )
879 .unwrap();
880
881 let res = verify_biscuit_local(
882 read_token.clone(),
883 public_key,
884 subject.clone(),
885 resource.clone(),
886 operation.clone(),
887 );
888 assert!(res.is_ok());
889
890 let root = KeyPair::new();
891 let public_key = root.public();
892
893 let write_token = create_biscuit(
895 subject.clone(),
896 resource.clone(),
897 "write".to_string(),
898 root,
899 TokenTimeConfig::default(),
900 )
901 .unwrap();
902
903 let res = verify_biscuit_local(
904 write_token.clone(),
905 public_key,
906 subject.clone(),
907 resource.clone(),
908 "write".to_string(),
909 );
910 assert!(res.is_ok());
911
912 let res = verify_biscuit_local(
914 read_token,
915 public_key,
916 subject.clone(),
917 resource.clone(),
918 "write".to_string(),
919 );
920 assert!(res.is_err());
921
922 let res = verify_biscuit_local(
924 write_token,
925 public_key,
926 subject.clone(),
927 resource.clone(),
928 "read".to_string(),
929 );
930 assert!(res.is_err());
931 }
932
933 #[test]
934 fn test_biscuit_expiration() {
935 let subject = "test@test.com".to_owned();
936 let resource = "res1".to_string();
937 let operation = "read".to_string();
938 let root = KeyPair::new();
939 let public_key = root.public();
940 let token = create_biscuit(
942 subject.clone(),
943 resource.clone(),
944 operation.clone(),
945 root,
946 TokenTimeConfig::default(),
947 )
948 .unwrap();
949
950 let res = verify_biscuit_local(
951 token.clone(),
952 public_key,
953 subject.clone(),
954 resource.clone(),
955 operation.clone(),
956 );
957 assert!(res.is_ok());
958
959 let root = KeyPair::new();
961 let token = create_biscuit(
962 subject.clone(),
963 resource.clone(),
964 operation.clone(),
965 root,
966 TokenTimeConfig {
967 start_time: Some(Utc::now().timestamp() - 301),
968 duration: 300,
969 },
970 )
971 .unwrap();
972 let res = verify_biscuit_local(token, public_key, subject, resource, operation);
973 assert!(res.is_err());
974 }
975
976 #[test]
977 fn test_custom_token_time_config() {
978 let subject = "test@test.com".to_owned();
979 let resource = "res1".to_string();
980 let operation = "read".to_string();
981 let root = KeyPair::new();
982 let public_key = root.public();
983
984 let past_time = Utc::now().timestamp() - 3600;
986 let time_config = TokenTimeConfig {
987 start_time: Some(past_time),
988 duration: 7200, };
990
991 let token = create_biscuit(
992 subject.clone(),
993 resource.clone(),
994 operation.clone(),
995 root,
996 time_config,
997 )
998 .unwrap();
999
1000 let res = verify_biscuit_local(
1002 token.clone(),
1003 public_key,
1004 subject.clone(),
1005 resource.clone(),
1006 operation.clone(),
1007 );
1008 assert!(res.is_ok());
1009 }
1010
1011 #[test]
1012 fn test_service_chain_biscuit() {
1013 let subject = "test@test.com".to_owned();
1014 let resource = "res1".to_string();
1015 let operation = "read".to_string();
1016 let root = KeyPair::new();
1017 let public_key = root.public();
1018 let chain_key = KeyPair::new();
1019 let chain_public_key = hex::encode(chain_key.public().to_bytes());
1020 let chain_public_key = format!("ed25519/{}", chain_public_key);
1021 let chain_node = ServiceNode {
1022 component: "edge_function".to_string(),
1023 public_key: chain_public_key.clone(),
1024 };
1025 let nodes = vec![chain_node];
1026 let token = create_service_chain_biscuit(
1027 subject.clone(),
1028 resource.clone(),
1029 operation.clone(),
1030 root,
1031 &nodes,
1032 TokenTimeConfig::default(),
1033 );
1034 if let Err(e) = &token {
1035 println!("Error: {}", e);
1036 }
1037 assert!(token.is_ok());
1038 let token = token.unwrap();
1039 let res = verify_biscuit_local(
1040 token.clone(),
1041 public_key,
1042 subject.clone(),
1043 resource.clone(),
1044 operation.clone(),
1045 );
1046 assert!(res.is_ok());
1047 let biscuit = Biscuit::from(&token, public_key).unwrap();
1048 let third_party_request = biscuit.third_party_request().unwrap();
1049 let third_party_block = block!(
1050 r#"
1051 service("res1");
1052 "#
1053 );
1054 let third_party_block = third_party_request
1055 .create_block(&chain_key.private(), third_party_block)
1056 .unwrap();
1057 let attested_biscuit = biscuit
1058 .append_third_party(chain_key.public(), third_party_block)
1059 .unwrap();
1060 let attested_token = attested_biscuit.to_vec().unwrap();
1061 let res = verify_service_chain_biscuit_local(
1062 attested_token,
1063 public_key,
1064 subject.clone(),
1065 resource.clone(),
1066 operation.clone(),
1067 nodes,
1068 None,
1069 );
1070 assert!(res.is_ok());
1071 }
1072
1073 #[test]
1074 fn test_service_chain_biscuit_with_component_name() {
1075 let subject = "test@test.com".to_owned();
1076 let resource = "res1".to_string();
1077 let root = KeyPair::new();
1078 let public_key = root.public();
1079
1080 let chain_key1 = KeyPair::new();
1082 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
1083 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
1084 let chain_node1 = ServiceNode {
1085 component: "edge_function".to_string(),
1086 public_key: chain_public_key1.clone(),
1087 };
1088
1089 let chain_key2 = KeyPair::new();
1090 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
1091 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
1092 let chain_node2 = ServiceNode {
1093 component: "middleware".to_string(),
1094 public_key: chain_public_key2.clone(),
1095 };
1096
1097 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
1098
1099 let token = create_service_chain_biscuit(
1101 subject.clone(),
1102 resource.clone(),
1103 "read".to_string(),
1104 root,
1105 &nodes,
1106 TokenTimeConfig::default(),
1107 );
1108 assert!(token.is_ok());
1109 let token = token.unwrap();
1110
1111 let biscuit = Biscuit::from(&token, public_key).unwrap();
1113 let third_party_request = biscuit.third_party_request().unwrap();
1114 let third_party_block = block!(
1115 r#"
1116 service("res1");
1117 "#
1118 );
1119 let third_party_block = third_party_request
1120 .create_block(&chain_key1.private(), third_party_block)
1121 .unwrap();
1122 let attested_biscuit = biscuit
1123 .append_third_party(chain_key1.public(), third_party_block)
1124 .unwrap();
1125 let attested_token = attested_biscuit.to_vec().unwrap();
1126
1127 let res = verify_service_chain_biscuit_local(
1131 attested_token.clone(),
1132 public_key,
1133 subject.clone(),
1134 resource.clone(),
1135 "read".to_string(),
1136 nodes.clone(),
1137 Some("edge_function".to_string()),
1138 );
1139 assert!(res.is_ok());
1141
1142 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
1144
1145 let res = verify_service_chain_biscuit_local(
1147 attested_token.clone(),
1148 public_key,
1149 subject.clone(),
1150 resource.clone(),
1151 "read".to_string(),
1152 nodes.clone(),
1153 Some("middleware".to_string()),
1154 );
1155 assert!(res.is_ok());
1156 }
1157
1158 #[test]
1159 fn test_service_chain_biscuit_with_nonexistent_component() {
1160 let subject = "test@test.com".to_owned();
1161 let resource = "res1".to_string();
1162 let root = KeyPair::new();
1163 let public_key = root.public();
1164 let chain_key = KeyPair::new();
1165 let chain_public_key = hex::encode(chain_key.public().to_bytes());
1166 let chain_public_key = format!("ed25519/{}", chain_public_key);
1167 let chain_node = ServiceNode {
1168 component: "edge_function".to_string(),
1169 public_key: chain_public_key.clone(),
1170 };
1171 let nodes = vec![chain_node];
1172 let token = create_service_chain_biscuit(
1173 subject.clone(),
1174 resource.clone(),
1175 "read".to_string(),
1176 root,
1177 &nodes,
1178 TokenTimeConfig::default(),
1179 );
1180 assert!(token.is_ok());
1181 let token = token.unwrap();
1182
1183 let biscuit = Biscuit::from(&token, public_key).unwrap();
1184 let third_party_request = biscuit.third_party_request().unwrap();
1185 let third_party_block = block!(
1186 r#"
1187 service("res1");
1188 "#
1189 );
1190 let third_party_block = third_party_request
1191 .create_block(&chain_key.private(), third_party_block)
1192 .unwrap();
1193 let attested_biscuit = biscuit
1194 .append_third_party(chain_key.public(), third_party_block)
1195 .unwrap();
1196 let attested_token = attested_biscuit.to_vec().unwrap();
1197
1198 let res = verify_service_chain_biscuit_local(
1200 attested_token,
1201 public_key,
1202 subject.clone(),
1203 resource.clone(),
1204 "read".to_string(),
1205 nodes.clone(),
1206 Some("nonexistent_component".to_string()),
1207 );
1208 assert!(res.is_err());
1209
1210 let err = res.unwrap_err().to_string();
1212 assert!(err.contains("nonexistent_component"));
1213 }
1214
1215 #[test]
1216 fn test_service_chain_biscuit_with_multiple_nodes() {
1217 let subject = "test@test.com".to_owned();
1218 let resource = "res1".to_string();
1219 let root = KeyPair::new();
1220 let public_key = root.public();
1221
1222 let chain_key1 = KeyPair::new();
1224 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
1225 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
1226 let chain_node1 = ServiceNode {
1227 component: "edge_function".to_string(),
1228 public_key: chain_public_key1.clone(),
1229 };
1230
1231 let chain_key2 = KeyPair::new();
1232 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
1233 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
1234 let chain_node2 = ServiceNode {
1235 component: "middleware".to_string(),
1236 public_key: chain_public_key2.clone(),
1237 };
1238
1239 let chain_key3 = KeyPair::new();
1240 let chain_public_key3 = hex::encode(chain_key3.public().to_bytes());
1241 let chain_public_key3 = format!("ed25519/{}", chain_public_key3);
1242 let chain_node3 = ServiceNode {
1243 component: "backend".to_string(),
1244 public_key: chain_public_key3.clone(),
1245 };
1246
1247 let nodes = vec![
1249 chain_node1.clone(),
1250 chain_node2.clone(),
1251 chain_node3.clone(),
1252 ];
1253 let token = create_service_chain_biscuit(
1254 subject.clone(),
1255 resource.clone(),
1256 "read".to_string(),
1257 root,
1258 &nodes,
1259 TokenTimeConfig::default(),
1260 );
1261 assert!(token.is_ok());
1262 let token = token.unwrap();
1263
1264 println!("Created initial token");
1265
1266 let biscuit = Biscuit::from(&token, public_key).unwrap();
1268 let third_party_request1 = biscuit.third_party_request().unwrap();
1269 let third_party_block1 = block!(
1270 r#"
1271 service("res1");
1272 "#
1273 );
1274 let third_party_block1 = third_party_request1
1275 .create_block(&chain_key1.private(), third_party_block1)
1276 .unwrap();
1277 let attested_biscuit1 = biscuit
1278 .append_third_party(chain_key1.public(), third_party_block1)
1279 .unwrap();
1280
1281 let all_nodes = vec![
1283 chain_node1.clone(),
1284 chain_node2.clone(),
1285 chain_node3.clone(),
1286 ];
1287 let attested_token1 = attested_biscuit1.to_vec().unwrap();
1288
1289 let res = verify_service_chain_biscuit_local(
1292 attested_token1.clone(),
1293 public_key,
1294 subject.clone(),
1295 resource.clone(),
1296 "read".to_string(),
1297 all_nodes.clone(),
1298 Some("middleware".to_string()),
1299 );
1300 assert!(res.is_ok());
1301
1302 let res = verify_service_chain_biscuit_local(
1306 attested_token1.clone(),
1307 public_key,
1308 subject.clone(),
1309 resource.clone(),
1310 "read".to_string(),
1311 all_nodes.clone(),
1312 Some("backend".to_string()),
1313 );
1314 assert!(res.is_err());
1315 }
1316
1317 #[test]
1318 fn test_service_chain_biscuit_with_custom_time() {
1319 let subject = "test@test.com".to_owned();
1320 let resource = "res1".to_string();
1321 let root = KeyPair::new();
1322 let public_key = root.public();
1323 let chain_key = KeyPair::new();
1324 let chain_public_key = hex::encode(chain_key.public().to_bytes());
1325 let chain_public_key = format!("ed25519/{}", chain_public_key);
1326 let chain_node = ServiceNode {
1327 component: "edge_function".to_string(),
1328 public_key: chain_public_key.clone(),
1329 };
1330 let nodes = vec![chain_node];
1331
1332 let valid_token = create_service_chain_biscuit_with_time(
1334 subject.clone(),
1335 resource.clone(),
1336 "read".to_string(),
1337 root,
1338 &nodes,
1339 TokenTimeConfig::default(),
1340 );
1341 assert!(valid_token.is_ok());
1342 let valid_token = valid_token.unwrap().to_vec().unwrap();
1343
1344 let res = verify_biscuit_local(
1346 valid_token.clone(),
1347 public_key,
1348 subject.clone(),
1349 resource.clone(),
1350 "read".to_string(),
1351 );
1352 assert!(res.is_ok());
1353
1354 let expired_time_config = TokenTimeConfig {
1356 start_time: Some(Utc::now().timestamp() - 360), duration: 300, };
1359
1360 let root2 = KeyPair::new();
1362 let public_key2 = root2.public();
1363
1364 let expired_token = create_service_chain_biscuit_with_time(
1365 subject.clone(),
1366 resource.clone(),
1367 "read".to_string(),
1368 root2,
1369 &nodes,
1370 expired_time_config,
1371 );
1372 assert!(expired_token.is_ok());
1373 let expired_token = expired_token.unwrap().to_vec().unwrap();
1374
1375 let res = verify_biscuit_local(
1377 expired_token,
1378 public_key2,
1379 subject,
1380 resource,
1381 "read".to_string(),
1382 );
1383 assert!(res.is_err());
1384 }
1385
1386 #[test]
1387 fn test_multi_party_biscuit_helper_functions() {
1388 let subject = "test@test.com".to_owned();
1389 let resource = "res1".to_string();
1390 let operation = "read".to_string();
1391 let root = KeyPair::new();
1392
1393 let multi_party_key = KeyPair::new();
1395 let multi_party_public_key = hex::encode(multi_party_key.public().to_bytes());
1396 let multi_party_public_key = format!("ed25519/{}", multi_party_public_key);
1397 let multi_party_node = ServiceNode {
1398 component: "approval_service".to_string(),
1399 public_key: multi_party_public_key.clone(),
1400 };
1401 let nodes = vec![multi_party_node];
1402
1403 let token_string = create_multi_party_token(
1405 subject.clone(),
1406 resource.clone(),
1407 operation.clone(),
1408 root,
1409 &nodes,
1410 );
1411 assert!(token_string.is_ok());
1412
1413 let root2 = KeyPair::new();
1415 let binary_token = create_multi_party_biscuit(
1416 subject.clone(),
1417 resource.clone(),
1418 operation.clone(),
1419 root2,
1420 &nodes,
1421 );
1422 assert!(binary_token.is_ok());
1423
1424 let root3 = KeyPair::new();
1426 let raw_biscuit = create_raw_multi_party_biscuit(
1427 subject.clone(),
1428 resource.clone(),
1429 operation.clone(),
1430 root3,
1431 &nodes,
1432 );
1433 assert!(raw_biscuit.is_ok());
1434
1435 let custom_time_config = TokenTimeConfig {
1437 start_time: Some(Utc::now().timestamp()),
1438 duration: 600, };
1440 let root4 = KeyPair::new();
1441 let custom_time_token = create_multi_party_token_with_time(
1442 subject.clone(),
1443 resource.clone(),
1444 operation.clone(),
1445 root4,
1446 &nodes,
1447 custom_time_config,
1448 );
1449 assert!(custom_time_token.is_ok());
1450
1451 let root5 = KeyPair::new();
1453 let custom_time_biscuit = create_multi_party_biscuit_with_time(
1454 subject.clone(),
1455 resource.clone(),
1456 operation.clone(),
1457 root5,
1458 &nodes,
1459 custom_time_config,
1460 );
1461 assert!(custom_time_biscuit.is_ok());
1462 }
1463
1464 #[test]
1465 fn test_basic_authorization_with_domain_restriction() {
1466 let subject = "alice".to_string();
1467 let resource = "resource1".to_string();
1468 let operation = "read".to_string();
1469 let domain = "myapp.hessra.dev".to_string();
1470 let keypair = KeyPair::new();
1471 let public_key = keypair.public();
1472
1473 let token = HessraAuthorization::new(
1475 subject.clone(),
1476 resource.clone(),
1477 operation.clone(),
1478 TokenTimeConfig::default(),
1479 )
1480 .domain_restricted(domain.clone())
1481 .issue(&keypair);
1482
1483 assert!(token.is_ok(), "Failed to create domain-restricted token");
1484 let token = token.unwrap();
1485
1486 let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1488
1489 let authz = crate::verify::build_base_authorizer(
1491 subject.clone(),
1492 resource.clone(),
1493 operation.clone(),
1494 Some(domain.clone()),
1495 )
1496 .unwrap();
1497 assert!(
1498 authz.build(&biscuit).unwrap().authorize().is_ok(),
1499 "Token should verify with correct domain"
1500 );
1501
1502 let authz_no_domain = crate::verify::build_base_authorizer(
1504 subject.clone(),
1505 resource.clone(),
1506 operation.clone(),
1507 None,
1508 )
1509 .unwrap();
1510 assert!(
1511 authz_no_domain
1512 .build(&biscuit)
1513 .unwrap()
1514 .authorize()
1515 .is_err(),
1516 "Token should fail verification without domain fact"
1517 );
1518
1519 let authz_wrong_domain = crate::verify::build_base_authorizer(
1521 subject,
1522 resource,
1523 operation,
1524 Some("wrongdomain.com".to_string()),
1525 )
1526 .unwrap();
1527 assert!(
1528 authz_wrong_domain
1529 .build(&biscuit)
1530 .unwrap()
1531 .authorize()
1532 .is_err(),
1533 "Token should fail verification with wrong domain"
1534 );
1535 }
1536
1537 #[test]
1538 fn test_service_chain_with_domain_restriction() {
1539 let subject = "alice".to_string();
1540 let resource = "resource1".to_string();
1541 let operation = "read".to_string();
1542 let domain = "myapp.hessra.dev".to_string();
1543 let keypair = KeyPair::new();
1544 let public_key = keypair.public();
1545
1546 let chain_key = KeyPair::new();
1548 let chain_public_key = hex::encode(chain_key.public().to_bytes());
1549 let chain_public_key = format!("ed25519/{}", chain_public_key);
1550 let chain_node = ServiceNode {
1551 component: "edge_function".to_string(),
1552 public_key: chain_public_key.clone(),
1553 };
1554 let nodes = vec![chain_node];
1555
1556 let token = HessraAuthorization::new(
1558 subject.clone(),
1559 resource.clone(),
1560 operation.clone(),
1561 TokenTimeConfig::default(),
1562 )
1563 .service_chain(nodes.clone())
1564 .domain_restricted(domain.clone())
1565 .issue(&keypair);
1566
1567 assert!(
1568 token.is_ok(),
1569 "Failed to create domain-restricted service chain token"
1570 );
1571 let token = token.unwrap();
1572
1573 let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1575
1576 let authz = crate::verify::build_base_authorizer(
1578 subject.clone(),
1579 resource.clone(),
1580 operation.clone(),
1581 Some(domain),
1582 )
1583 .unwrap();
1584 assert!(
1585 authz.build(&biscuit).unwrap().authorize().is_ok(),
1586 "Service chain token should verify with correct domain"
1587 );
1588
1589 let authz_no_domain =
1591 crate::verify::build_base_authorizer(subject, resource, operation, None).unwrap();
1592 assert!(
1593 authz_no_domain
1594 .build(&biscuit)
1595 .unwrap()
1596 .authorize()
1597 .is_err(),
1598 "Service chain token should fail verification without domain fact"
1599 );
1600 }
1601
1602 #[test]
1603 fn test_multi_party_with_domain_restriction() {
1604 let subject = "alice".to_string();
1605 let resource = "resource1".to_string();
1606 let operation = "read".to_string();
1607 let domain = "myapp.hessra.dev".to_string();
1608 let keypair = KeyPair::new();
1609 let public_key = keypair.public();
1610
1611 let party_key = KeyPair::new();
1613 let party_public_key = hex::encode(party_key.public().to_bytes());
1614 let party_public_key = format!("ed25519/{}", party_public_key);
1615 let party_node = ServiceNode {
1616 component: "approval_service".to_string(),
1617 public_key: party_public_key.clone(),
1618 };
1619 let nodes = vec![party_node];
1620
1621 let token = HessraAuthorization::new(
1623 subject.clone(),
1624 resource.clone(),
1625 operation.clone(),
1626 TokenTimeConfig::default(),
1627 )
1628 .multi_party(nodes.clone())
1629 .domain_restricted(domain.clone())
1630 .issue(&keypair);
1631
1632 assert!(
1633 token.is_ok(),
1634 "Failed to create domain-restricted multi-party token"
1635 );
1636 let token = token.unwrap();
1637
1638 let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1640
1641 let _authz = crate::verify::build_base_authorizer(
1644 subject.clone(),
1645 resource.clone(),
1646 operation.clone(),
1647 Some(domain),
1648 )
1649 .unwrap();
1650 assert!(biscuit.to_base64().is_ok(), "Token should be valid biscuit");
1653
1654 let authz_no_domain =
1656 crate::verify::build_base_authorizer(subject, resource, operation, None).unwrap();
1657 assert!(
1658 authz_no_domain
1659 .build(&biscuit)
1660 .unwrap()
1661 .authorize()
1662 .is_err(),
1663 "Multi-party token should fail verification without domain fact or attestations"
1664 );
1665 }
1666
1667 #[test]
1668 fn test_authorization_verifier_with_domain() {
1669 use crate::verify::AuthorizationVerifier;
1670
1671 let subject = "alice".to_string();
1672 let resource = "resource1".to_string();
1673 let operation = "read".to_string();
1674 let domain = "myapp.hessra.dev".to_string();
1675 let keypair = KeyPair::new();
1676 let public_key = keypair.public();
1677
1678 let token = HessraAuthorization::new(
1680 subject.clone(),
1681 resource.clone(),
1682 operation.clone(),
1683 TokenTimeConfig::default(),
1684 )
1685 .domain_restricted(domain.clone())
1686 .issue(&keypair)
1687 .expect("Failed to create domain-restricted token");
1688
1689 assert!(
1691 AuthorizationVerifier::new(
1692 token.clone(),
1693 public_key,
1694 subject.clone(),
1695 resource.clone(),
1696 operation.clone(),
1697 )
1698 .with_domain(domain.clone())
1699 .verify()
1700 .is_ok(),
1701 "Token should verify with correct domain using builder"
1702 );
1703
1704 assert!(
1706 AuthorizationVerifier::new(
1707 token.clone(),
1708 public_key,
1709 subject.clone(),
1710 resource.clone(),
1711 operation.clone(),
1712 )
1713 .verify()
1714 .is_err(),
1715 "Token should fail verification without domain context"
1716 );
1717
1718 assert!(
1720 AuthorizationVerifier::new(
1721 token.clone(),
1722 public_key,
1723 subject.clone(),
1724 resource.clone(),
1725 operation.clone(),
1726 )
1727 .with_domain("wrongdomain.com".to_string())
1728 .verify()
1729 .is_err(),
1730 "Token should fail verification with wrong domain"
1731 );
1732
1733 let regular_token = HessraAuthorization::new(
1735 subject.clone(),
1736 resource.clone(),
1737 operation.clone(),
1738 TokenTimeConfig::default(),
1739 )
1740 .issue(&keypair)
1741 .expect("Failed to create regular token");
1742
1743 assert!(
1745 AuthorizationVerifier::new(
1746 regular_token.clone(),
1747 public_key,
1748 subject.clone(),
1749 resource.clone(),
1750 operation.clone(),
1751 )
1752 .verify()
1753 .is_ok(),
1754 "Regular token should verify without domain context"
1755 );
1756
1757 assert!(
1758 AuthorizationVerifier::new(regular_token, public_key, subject, resource, operation,)
1759 .with_domain(domain)
1760 .verify()
1761 .is_ok(),
1762 "Regular token should verify even with extra domain context"
1763 );
1764 }
1765
1766 #[test]
1767 fn test_service_chain_verifier_with_domain() {
1768 use crate::verify::{AuthorizationVerifier, ServiceNode};
1769
1770 let subject = "alice".to_string();
1771 let resource = "resource1".to_string();
1772 let operation = "read".to_string();
1773 let domain = "myapp.hessra.dev".to_string();
1774 let keypair = KeyPair::new();
1775 let public_key = keypair.public();
1776
1777 let node_keypair = KeyPair::new();
1779 let node_public_key = node_keypair.public();
1780 let node_key_string = format!("ed25519/{}", hex::encode(node_public_key.to_bytes()));
1781
1782 let service_nodes = vec![ServiceNode {
1783 component: "api-gateway".to_string(),
1784 public_key: node_key_string,
1785 }];
1786
1787 let token = HessraAuthorization::new(
1789 subject.clone(),
1790 resource.clone(),
1791 operation.clone(),
1792 TokenTimeConfig::default(),
1793 )
1794 .service_chain(service_nodes.clone())
1795 .domain_restricted(domain.clone())
1796 .issue(&keypair)
1797 .expect("Failed to create service chain token with domain");
1798
1799 let token_bytes = crate::decode_token(&token).expect("Failed to decode token");
1801
1802 let attested_token_bytes = crate::attest::add_service_node_attestation(
1804 token_bytes,
1805 public_key,
1806 &resource,
1807 &node_keypair,
1808 )
1809 .expect("Failed to add attestation");
1810
1811 assert!(
1813 AuthorizationVerifier::from_bytes(
1814 attested_token_bytes.clone(),
1815 public_key,
1816 subject.clone(),
1817 resource.clone(),
1818 operation.clone(),
1819 )
1820 .expect("Failed to create verifier")
1821 .with_service_chain(service_nodes.clone(), Some("api-gateway".to_string()))
1822 .with_domain(domain.clone())
1823 .verify()
1824 .is_ok(),
1825 "Service chain token should verify with domain"
1826 );
1827
1828 assert!(
1830 AuthorizationVerifier::from_bytes(
1831 attested_token_bytes.clone(),
1832 public_key,
1833 subject.clone(),
1834 resource.clone(),
1835 operation.clone(),
1836 )
1837 .expect("Failed to create verifier")
1838 .with_service_chain(service_nodes.clone(), Some("api-gateway".to_string()))
1839 .verify()
1840 .is_err(),
1841 "Service chain token should fail without domain"
1842 );
1843
1844 assert!(
1848 AuthorizationVerifier::from_bytes(
1849 attested_token_bytes,
1850 public_key,
1851 subject,
1852 resource,
1853 operation,
1854 )
1855 .expect("Failed to create verifier")
1856 .with_domain(domain)
1857 .verify()
1858 .is_ok(),
1859 "Service chain token with valid attestation should pass basic verification"
1860 );
1861 }
1862
1863 #[test]
1864 fn test_verify_capability_token_basic() {
1865 let subject = "alice".to_string();
1866 let resource = "resource1".to_string();
1867 let operation = "read".to_string();
1868 let keypair = KeyPair::new();
1869 let public_key = keypair.public();
1870
1871 let token = create_token(
1873 subject.clone(),
1874 resource.clone(),
1875 operation.clone(),
1876 keypair,
1877 )
1878 .unwrap();
1879
1880 let result =
1882 crate::verify::verify_capability_token_local(&token, public_key, &resource, &operation);
1883
1884 assert!(result.is_ok());
1885 }
1886
1887 #[test]
1888 fn test_verify_capability_token_wrong_resource() {
1889 let keypair = KeyPair::new();
1890 let public_key = keypair.public();
1891
1892 let token = create_token(
1893 "alice".to_string(),
1894 "resource1".to_string(),
1895 "read".to_string(),
1896 keypair,
1897 )
1898 .unwrap();
1899
1900 let result = crate::verify::verify_capability_token_local(
1902 &token,
1903 public_key,
1904 "resource2", "read",
1906 );
1907
1908 assert!(result.is_err());
1909 }
1910
1911 #[test]
1912 fn test_verify_capability_token_wrong_operation() {
1913 let keypair = KeyPair::new();
1914 let public_key = keypair.public();
1915
1916 let token = create_token(
1917 "alice".to_string(),
1918 "resource1".to_string(),
1919 "read".to_string(),
1920 keypair,
1921 )
1922 .unwrap();
1923
1924 let result = crate::verify::verify_capability_token_local(
1926 &token,
1927 public_key,
1928 "resource1",
1929 "write", );
1931
1932 assert!(result.is_err());
1933 }
1934
1935 #[test]
1936 fn test_verify_capability_with_domain() {
1937 let domain = "myapp.hessra.dev".to_string();
1938 let keypair = KeyPair::new();
1939 let public_key = keypair.public();
1940
1941 let token = HessraAuthorization::new(
1943 "alice".to_string(),
1944 "resource1".to_string(),
1945 "read".to_string(),
1946 TokenTimeConfig::default(),
1947 )
1948 .domain_restricted(domain.clone())
1949 .issue(&keypair)
1950 .unwrap();
1951
1952 let result = crate::verify::AuthorizationVerifier::new_capability(
1954 token.clone(),
1955 public_key,
1956 "resource1".to_string(),
1957 "read".to_string(),
1958 )
1959 .with_domain(domain.clone())
1960 .verify();
1961
1962 assert!(result.is_ok());
1963
1964 let result = crate::verify::AuthorizationVerifier::new_capability(
1966 token,
1967 public_key,
1968 "resource1".to_string(),
1969 "read".to_string(),
1970 )
1971 .verify();
1972
1973 assert!(result.is_err());
1974 }
1975
1976 #[test]
1977 fn test_verify_capability_with_service_chain() {
1978 let keypair = KeyPair::new();
1979 let public_key = keypair.public();
1980
1981 let chain_keypair = KeyPair::new();
1982 let chain_public_key =
1983 format!("ed25519/{}", hex::encode(chain_keypair.public().to_bytes()));
1984 let chain_node = ServiceNode {
1985 component: "edge_function".to_string(),
1986 public_key: chain_public_key,
1987 };
1988
1989 let token = HessraAuthorization::new(
1991 "alice".to_string(),
1992 "resource1".to_string(),
1993 "read".to_string(),
1994 TokenTimeConfig::default(),
1995 )
1996 .service_chain(vec![chain_node.clone()])
1997 .issue(&keypair)
1998 .unwrap();
1999
2000 let token_bytes = crate::decode_token(&token).unwrap();
2002 let attested = crate::attest::add_service_node_attestation(
2003 token_bytes,
2004 public_key,
2005 "resource1",
2006 &chain_keypair,
2007 )
2008 .unwrap();
2009 let attested_token = crate::encode_token(&attested);
2010
2011 let result = crate::verify::verify_service_chain_capability_token_local(
2013 &attested_token,
2014 public_key,
2015 "resource1",
2016 "read",
2017 vec![chain_node],
2018 None,
2019 );
2020
2021 assert!(result.is_ok());
2022 }
2023
2024 #[test]
2025 fn test_capability_verifier_builder() {
2026 let keypair = KeyPair::new();
2027 let public_key = keypair.public();
2028 let domain = "example.com".to_string();
2029
2030 let token = HessraAuthorization::new(
2031 "alice".to_string(),
2032 "resource1".to_string(),
2033 "read".to_string(),
2034 TokenTimeConfig::default(),
2035 )
2036 .domain_restricted(domain.clone())
2037 .issue(&keypair)
2038 .unwrap();
2039
2040 let result = crate::verify::AuthorizationVerifier::new_capability(
2042 token,
2043 public_key,
2044 "resource1".to_string(),
2045 "read".to_string(),
2046 )
2047 .with_domain(domain)
2048 .verify();
2049
2050 assert!(result.is_ok());
2051 }
2052
2053 #[test]
2054 fn test_capability_vs_identity_verification() {
2055 let keypair = KeyPair::new();
2056 let public_key = keypair.public();
2057
2058 let token = create_token(
2060 "alice".to_string(),
2061 "document_123".to_string(),
2062 "read".to_string(),
2063 keypair,
2064 )
2065 .unwrap();
2066
2067 let result = crate::verify::verify_token_local(
2069 &token,
2070 public_key,
2071 "alice", "document_123",
2073 "read",
2074 );
2075 assert!(result.is_ok());
2076
2077 let result = crate::verify::verify_token_local(
2079 &token,
2080 public_key,
2081 "bob", "document_123",
2083 "read",
2084 );
2085 assert!(result.is_err());
2086
2087 let result = crate::verify::verify_capability_token_local(
2089 &token,
2090 public_key,
2091 "document_123", "read",
2093 );
2094 assert!(result.is_ok());
2095 }
2096}