1use biscuit_auth as biscuit;
2use wasm_bindgen::prelude::*;
3
4#[global_allocator]
5static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
6
7#[wasm_bindgen]
12pub struct Biscuit(biscuit::Biscuit);
13
14#[wasm_bindgen]
15impl Biscuit {
16 pub fn builder() -> BiscuitBuilder {
20 BiscuitBuilder::new()
21 }
22
23 pub fn create_block(&self) -> BlockBuilder {
27 BlockBuilder(self.0.create_block())
28 }
29
30 pub fn append(&self, block: BlockBuilder) -> Result<Biscuit, JsValue> {
32 Ok(Biscuit(
33 self.0
34 .append(block.0)
35 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
36 ))
37 }
38
39 pub fn authorizer(&self) -> Result<Authorizer, JsValue> {
41 Ok(Authorizer {
42 token: Some(self.0.clone()),
43 ..Authorizer::default()
44 })
45 }
46
47 pub fn seal(&self) -> Result<Biscuit, JsValue> {
51 Ok(Biscuit(
52 self.0
53 .seal()
54 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
55 ))
56 }
57
58 pub fn from_bytes(data: &[u8], root: &PublicKey) -> Result<Biscuit, JsValue> {
62 Ok(Biscuit(
63 biscuit::Biscuit::from(data, |_| root.0)
64 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
65 ))
66 }
67
68 pub fn from_base64(data: &str, root: &PublicKey) -> Result<Biscuit, JsValue> {
72 Ok(Biscuit(
73 biscuit::Biscuit::from_base64(data, |_| root.0)
74 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
75 ))
76 }
77
78 pub fn to_bytes(&self) -> Result<Box<[u8]>, JsValue> {
80 Ok(self
81 .0
82 .to_vec()
83 .map_err(|e| JsValue::from_serde(&e).unwrap())?
84 .into_boxed_slice())
85 }
86
87 pub fn to_base64(&self) -> Result<String, JsValue> {
89 Ok(self
90 .0
91 .to_base64()
92 .map_err(|e| JsValue::from_serde(&e).unwrap())?)
93 }
94
95 pub fn revocation_identifiers(&self) -> Box<[JsValue]> {
97 let ids: Vec<_> = self
98 .0
99 .revocation_identifiers()
100 .into_iter()
101 .map(|id| base64::encode_config(id, base64::URL_SAFE).into())
102 .collect();
103 ids.into_boxed_slice()
104 }
105
106 pub fn block_count(&self) -> usize {
108 self.0.block_count()
109 }
110
111 pub fn block_source(&self, index: usize) -> Option<String> {
113 self.0.print_block_source(index)
114 }
115}
116
117#[wasm_bindgen]
119#[derive(Default)]
120pub struct Authorizer {
121 token: Option<biscuit::Biscuit>,
122 facts: Vec<biscuit::builder::Fact>,
123 rules: Vec<biscuit::builder::Rule>,
124 checks: Vec<biscuit::builder::Check>,
125 policies: Vec<biscuit::builder::Policy>,
126}
127
128#[wasm_bindgen]
129impl Authorizer {
130 #[wasm_bindgen(constructor)]
131 pub fn new() -> Authorizer {
132 Authorizer::default()
133 }
134
135 pub fn add_token(&mut self, token: Biscuit) {
136 self.token = Some(token.0);
137 }
138
139 pub fn add_fact(&mut self, fact: Fact) -> Result<(), JsValue> {
141 self.facts.push(fact.0);
142 Ok(())
143 }
144
145 pub fn add_rule(&mut self, rule: Rule) -> Result<(), JsValue> {
147 self.rules.push(rule.0);
148 Ok(())
149 }
150
151 pub fn add_check(&mut self, check: Check) -> Result<(), JsValue> {
155 self.checks.push(check.0);
156 Ok(())
157 }
158
159 pub fn add_policy(&mut self, policy: Policy) -> Result<(), JsValue> {
165 self.policies.push(policy.0);
166 Ok(())
167 }
168
169 pub fn add_code(&mut self, source: &str) -> Result<(), JsValue> {
171 let source_result = biscuit::parser::parse_source(source).map_err(|e| {
172 let e: biscuit::error::Token = e.into();
173 JsValue::from_serde(&e).unwrap()
174 })?;
175
176 for (_, fact) in source_result.facts.into_iter() {
177 self.facts.push(fact);
178 }
179
180 for (_, rule) in source_result.rules.into_iter() {
181 self.rules.push(rule);
182 }
183
184 for (_, check) in source_result.checks.into_iter() {
185 self.checks.push(check);
186 }
187
188 for (_, policy) in source_result.policies.into_iter() {
189 self.policies.push(policy);
190 }
191
192 Ok(())
193 }
194
195 pub fn authorize(&self) -> Result<usize, JsValue> {
200 let mut authorizer = match &self.token {
201 Some(token) => token
202 .authorizer()
203 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
204 None => biscuit::Authorizer::new().map_err(|e| JsValue::from_serde(&e).unwrap())?,
205 };
206
207 for fact in self.facts.iter() {
208 authorizer
209 .add_fact(fact.clone())
210 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
211 }
212 for rule in self.rules.iter() {
213 authorizer
214 .add_rule(rule.clone())
215 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
216 }
217 for check in self.checks.iter() {
218 authorizer
219 .add_check(check.clone())
220 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
221 }
222 for policy in self.policies.iter() {
223 authorizer
224 .add_policy(policy.clone())
225 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
226 }
227
228 Ok(authorizer
229 .authorize()
230 .map_err(|e| JsValue::from_serde(&e).unwrap())?)
231 }
232}
233
234#[wasm_bindgen]
236pub struct BiscuitBuilder {
237 facts: Vec<biscuit::builder::Fact>,
238 rules: Vec<biscuit::builder::Rule>,
239 checks: Vec<biscuit::builder::Check>,
240}
241
242#[wasm_bindgen]
243impl BiscuitBuilder {
244 fn new() -> BiscuitBuilder {
245 BiscuitBuilder {
246 facts: Vec::new(),
247 rules: Vec::new(),
248 checks: Vec::new(),
249 }
250 }
251
252 pub fn build(self, root: &PrivateKey) -> Result<Biscuit, JsValue> {
253 let keypair = biscuit_auth::KeyPair::from(root.0.clone());
254 let mut builder = biscuit_auth::Biscuit::builder(&keypair);
255 for fact in self.facts.into_iter() {
256 builder
257 .add_authority_fact(fact)
258 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
259 }
260 for rule in self.rules.into_iter() {
261 builder
262 .add_authority_rule(rule)
263 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
264 }
265 for check in self.checks.into_iter() {
266 builder
267 .add_authority_check(check)
268 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
269 }
270
271 Ok(Biscuit(
272 builder
273 .build()
274 .map_err(|e| JsValue::from_serde(&e).unwrap())?,
275 ))
276 }
277
278 pub fn add_authority_fact(&mut self, fact: Fact) -> Result<(), JsValue> {
280 self.facts.push(fact.0);
281 Ok(())
282 }
283
284 pub fn add_authority_rule(&mut self, rule: Rule) -> Result<(), JsValue> {
286 self.rules.push(rule.0);
287 Ok(())
288 }
289
290 pub fn add_authority_check(&mut self, check: Check) -> Result<(), JsValue> {
294 self.checks.push(check.0);
295 Ok(())
296 }
297}
298
299#[wasm_bindgen]
301pub struct BlockBuilder(biscuit::builder::BlockBuilder);
302
303#[wasm_bindgen]
304impl BlockBuilder {
305 pub fn add_fact(&mut self, fact: Fact) -> Result<(), JsValue> {
307 Ok(self
308 .0
309 .add_fact(fact.0)
310 .map_err(|e| JsValue::from_serde(&e).unwrap())?)
311 }
312
313 pub fn add_rule(&mut self, rule: Rule) -> Result<(), JsValue> {
315 Ok(self
316 .0
317 .add_rule(rule.0)
318 .map_err(|e| JsValue::from_serde(&e).unwrap())?)
319 }
320
321 pub fn add_check(&mut self, check: Check) -> Result<(), JsValue> {
325 Ok(self
326 .0
327 .add_check(check.0)
328 .map_err(|e| JsValue::from_serde(&e).unwrap())?)
329 }
330
331 pub fn add_code(&mut self, source: &str) -> Result<(), JsValue> {
333 self.0
334 .add_code(source)
335 .map_err(|e| JsValue::from_serde(&e).unwrap())
336 }
337}
338
339#[wasm_bindgen]
340pub struct Fact(biscuit::builder::Fact);
341
342#[wasm_bindgen]
343impl Fact {
344 pub fn from_str(source: &str) -> Result<Fact, JsValue> {
345 source
346 .try_into()
347 .map(Fact)
348 .map_err(|e| JsValue::from_serde(&e).unwrap())
349 }
350
351 pub fn set(&mut self, name: &str, value: JsValue) -> Result<(), JsValue> {
352 let value = js_to_term(value)?;
353
354 self.0
355 .set(name, value)
356 .map_err(|e| JsValue::from_serde(&e).unwrap())
357 }
358}
359
360#[wasm_bindgen]
361pub struct Rule(biscuit::builder::Rule);
362
363#[wasm_bindgen]
364impl Rule {
365 pub fn from_str(source: &str) -> Result<Rule, JsValue> {
366 source
367 .try_into()
368 .map(Rule)
369 .map_err(|e| JsValue::from_serde(&e).unwrap())
370 }
371
372 pub fn set(&mut self, name: &str, value: JsValue) -> Result<(), JsValue> {
373 let value = js_to_term(value)?;
374
375 self.0
376 .set(name, value)
377 .map_err(|e| JsValue::from_serde(&e).unwrap())
378 }
379}
380
381#[wasm_bindgen]
382pub struct Check(biscuit::builder::Check);
383
384#[wasm_bindgen]
385impl Check {
386 pub fn from_str(source: &str) -> Result<Check, JsValue> {
387 source
388 .try_into()
389 .map(Check)
390 .map_err(|e| JsValue::from_serde(&e).unwrap())
391 }
392
393 pub fn set(&mut self, name: &str, value: JsValue) -> Result<(), JsValue> {
394 let value = js_to_term(value)?;
395
396 self.0
397 .set(name, value)
398 .map_err(|e| JsValue::from_serde(&e).unwrap())
399 }
400}
401
402#[wasm_bindgen]
403pub struct Policy(biscuit::builder::Policy);
404
405#[wasm_bindgen]
406impl Policy {
407 pub fn from_str(source: &str) -> Result<Policy, JsValue> {
408 source
409 .try_into()
410 .map(Policy)
411 .map_err(|e| JsValue::from_serde(&e).unwrap())
412 }
413
414 pub fn set(&mut self, name: &str, value: JsValue) -> Result<(), JsValue> {
415 let value = js_to_term(value)?;
416
417 self.0
418 .set(name, value)
419 .map_err(|e| JsValue::from_serde(&e).unwrap())
420 }
421}
422
423fn js_to_term(value: JsValue) -> Result<biscuit::builder::Term, JsValue> {
424 if let Some(b) = value.as_bool() {
425 Ok(biscuit::builder::Term::Bool(b))
426 } else if let Some(f) = value.as_f64() {
427 Ok(biscuit::builder::Term::Integer(f as i64))
428 } else if let Some(s) = value.as_string() {
429 Ok(biscuit::builder::Term::Str(s))
430 } else {
431 Err(JsValue::from_serde("unexpected value").unwrap())
432 }
433}
434
435#[wasm_bindgen]
437pub struct KeyPair(biscuit::KeyPair);
438
439#[wasm_bindgen]
440impl KeyPair {
441 #[wasm_bindgen(constructor)]
442 pub fn new() -> KeyPair {
443 KeyPair(biscuit::KeyPair::new())
444 }
445
446 pub fn from(key: PrivateKey) -> Self {
447 KeyPair(biscuit::KeyPair::from(key.0))
448 }
449
450 pub fn public(&self) -> PublicKey {
451 PublicKey(self.0.public())
452 }
453
454 pub fn private(&self) -> PrivateKey {
455 PrivateKey(self.0.private())
456 }
457}
458
459#[wasm_bindgen]
461pub struct PublicKey(biscuit::PublicKey);
462
463#[wasm_bindgen]
464impl PublicKey {
465 pub fn to_bytes(&self, out: &mut [u8]) -> Result<(), JsValue> {
467 if out.len() != 32 {
468 return Err(JsValue::from_serde(&biscuit::error::Token::Format(
469 biscuit::error::Format::InvalidKeySize(out.len()),
470 ))
471 .unwrap());
472 }
473
474 out.copy_from_slice(&self.0.to_bytes());
475 Ok(())
476 }
477
478 pub fn to_hex(&self) -> String {
480 hex::encode(&self.0.to_bytes())
481 }
482
483 pub fn from_bytes(data: &[u8]) -> Result<PublicKey, JsValue> {
485 let key = biscuit_auth::PublicKey::from_bytes(data)
486 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
487 Ok(PublicKey(key))
488 }
489
490 pub fn from_hex(data: &str) -> Result<PublicKey, JsValue> {
492 let data = hex::decode(data).map_err(|e| {
493 JsValue::from_serde(&biscuit::error::Token::Format(
494 biscuit::error::Format::InvalidKey(format!(
495 "could not deserialize hex encoded key: {}",
496 e
497 )),
498 ))
499 .unwrap()
500 })?;
501 let key = biscuit_auth::PublicKey::from_bytes(&data)
502 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
503 Ok(PublicKey(key))
504 }
505}
506#[wasm_bindgen]
507pub struct PrivateKey(biscuit::PrivateKey);
508
509#[wasm_bindgen]
510impl PrivateKey {
511 pub fn to_bytes(&self, out: &mut [u8]) -> Result<(), JsValue> {
513 if out.len() != 32 {
514 return Err(JsValue::from_serde(&biscuit::error::Token::Format(
515 biscuit::error::Format::InvalidKeySize(out.len()),
516 ))
517 .unwrap());
518 }
519
520 out.copy_from_slice(&self.0.to_bytes());
521 Ok(())
522 }
523
524 pub fn to_hex(&self) -> String {
526 hex::encode(&self.0.to_bytes())
527 }
528
529 pub fn from_bytes(data: &[u8]) -> Result<PrivateKey, JsValue> {
531 let key = biscuit_auth::PrivateKey::from_bytes(data)
532 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
533 Ok(PrivateKey(key))
534 }
535
536 pub fn from_hex(data: &str) -> Result<PrivateKey, JsValue> {
538 let data = hex::decode(data).map_err(|e| {
539 JsValue::from_serde(&biscuit::error::Token::Format(
540 biscuit::error::Format::InvalidKey(format!(
541 "could not deserialize hex encoded key: {}",
542 e
543 )),
544 ))
545 .unwrap()
546 })?;
547 let key = biscuit_auth::PrivateKey::from_bytes(&data)
548 .map_err(|e| JsValue::from_serde(&e).unwrap())?;
549 Ok(PrivateKey(key))
550 }
551}
552
553#[wasm_bindgen]
554extern "C" {
555 #[wasm_bindgen(js_namespace = console)]
558 fn log(s: &str);
559}
560
561#[wasm_bindgen(start)]
562pub fn init() {
563 wasm_logger::init(wasm_logger::Config::default());
564 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
565
566 log("biscuit-wasm loading")
567}