hessra_token_authz/
mint.rs

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
12/// Builder for creating Hessra authorization tokens with flexible configuration.
13///
14/// Authorization tokens can be configured with the following capabilities:
15/// - **Singleton Capability**: Simple authorization with subject, resource, and operation (default)
16/// - **Latent Capability**: Broader authorization with latent_rights that must be activated
17/// - **Service Chain**: Add service node attestations via `.service_chain(nodes)`
18/// - **Multi-Party**: Add multi-party attestation requirements via `.multi_party(nodes)`
19/// - **Domain Restriction**: Limit token to a specific domain via `.domain_restricted(domain)`
20///
21/// Service chain and multi-party capabilities can be combined in the same token
22/// (though this is not currently validated or actively used).
23///
24/// ## Token Types
25///
26/// **Singleton Capability Token** (default): Grants a specific right to a specific subject for
27/// a specific resource and operation. The token can be used immediately.
28///
29/// **Latent Capability Token**: Contains broad `latent_right(resource, operation)` permissions
30/// but cannot be used directly. Must be activated by the holder of the bound activator key using
31/// the `activate_latent_token` function. The same latent token can be activated multiple times
32/// with different subjects and (resource, operation) pairs from the latent_rights set.
33///
34/// # Example - Singleton Token
35/// ```rust
36/// use hessra_token_authz::HessraAuthorization;
37/// use hessra_token_core::{KeyPair, TokenTimeConfig};
38///
39/// let keypair = KeyPair::new();
40///
41/// // Singleton capability authorization with domain restriction
42/// let token = HessraAuthorization::new(
43///     "alice".to_string(),
44///     "resource1".to_string(),
45///     "read".to_string(),
46///     TokenTimeConfig::default()
47/// )
48/// .domain_restricted("myapp.hessra.dev".to_string())
49/// .issue(&keypair)
50/// .expect("Failed to create token");
51/// ```
52///
53/// # Example - Latent Token
54/// ```rust
55/// use hessra_token_authz::HessraAuthorization;
56/// use hessra_token_core::{KeyPair, TokenTimeConfig};
57///
58/// let keypair = KeyPair::new();
59/// let activator_keypair = KeyPair::new();
60/// let activator_public_key = format!("ed25519/{}", hex::encode(activator_keypair.public().to_bytes()));
61///
62/// // Latent capability authorization
63/// let latent_rights = vec![
64///     ("resource1".to_string(), "read".to_string()),
65///     ("resource2".to_string(), "write".to_string()),
66/// ];
67///
68/// let token = HessraAuthorization::new_latent(
69///     latent_rights,
70///     activator_public_key,
71///     TokenTimeConfig::default()
72/// )
73/// .issue(&keypair)
74/// .expect("Failed to create latent token");
75/// ```
76pub struct HessraAuthorization {
77    token_type: TokenType,
78    // Singleton capability fields
79    subject: Option<String>,
80    resource: Option<String>,
81    operation: Option<String>,
82    // Latent capability fields
83    latent_rights: Option<Vec<(String, String)>>,
84    activator_public_key: Option<String>,
85    // Common fields
86    time_config: TokenTimeConfig,
87    service_chain_nodes: Option<Vec<ServiceNode>>,
88    multi_party_nodes: Option<Vec<ServiceNode>>,
89    domain: Option<String>,
90}
91
92/// Token type for authorization tokens
93#[derive(Debug, Clone, PartialEq)]
94enum TokenType {
95    Singleton,
96    Latent,
97}
98
99impl HessraAuthorization {
100    /// Creates a new singleton capability authorization token builder.
101    ///
102    /// Singleton tokens grant a specific right to a specific subject for a specific resource
103    /// and operation. The token can be used immediately upon issuance.
104    ///
105    /// # Arguments
106    /// * `subject` - The subject (user) identifier
107    /// * `resource` - The resource identifier to grant access to
108    /// * `operation` - The operation to grant access to
109    /// * `time_config` - Time configuration for token validity
110    pub fn new(
111        subject: String,
112        resource: String,
113        operation: String,
114        time_config: TokenTimeConfig,
115    ) -> Self {
116        Self {
117            token_type: TokenType::Singleton,
118            subject: Some(subject),
119            resource: Some(resource),
120            operation: Some(operation),
121            latent_rights: None,
122            activator_public_key: None,
123            time_config,
124            service_chain_nodes: None,
125            multi_party_nodes: None,
126            domain: None,
127        }
128    }
129
130    /// Creates a new latent capability authorization token builder.
131    ///
132    /// Latent tokens contain broad `latent_right(resource, operation)` permissions but cannot
133    /// be used directly. They must be activated by the holder of the bound activator key using
134    /// the `activate_latent_token` function.
135    ///
136    /// The same latent token can be activated multiple times with different subjects and
137    /// (resource, operation) pairs from the latent_rights set.
138    ///
139    /// # Arguments
140    /// * `latent_rights` - Vector of (resource, operation) pairs that can be activated
141    /// * `activator_public_key` - Public key of the activator (format: "ed25519/hexstring")
142    /// * `time_config` - Time configuration for token validity
143    ///
144    /// # Example
145    /// ```rust
146    /// use hessra_token_authz::HessraAuthorization;
147    /// use hessra_token_core::{KeyPair, TokenTimeConfig};
148    ///
149    /// let keypair = KeyPair::new();
150    /// let activator_keypair = KeyPair::new();
151    /// let activator_public_key = format!("ed25519/{}", hex::encode(activator_keypair.public().to_bytes()));
152    ///
153    /// let latent_rights = vec![
154    ///     ("resource1".to_string(), "read".to_string()),
155    ///     ("resource1".to_string(), "write".to_string()),
156    ///     ("resource2".to_string(), "read".to_string()),
157    /// ];
158    ///
159    /// let token = HessraAuthorization::new_latent(
160    ///     latent_rights,
161    ///     activator_public_key,
162    ///     TokenTimeConfig::default()
163    /// )
164    /// .issue(&keypair)
165    /// .expect("Failed to create latent token");
166    /// ```
167    pub fn new_latent(
168        latent_rights: Vec<(String, String)>,
169        activator_public_key: String,
170        time_config: TokenTimeConfig,
171    ) -> Self {
172        Self {
173            token_type: TokenType::Latent,
174            subject: None,
175            resource: None,
176            operation: None,
177            latent_rights: Some(latent_rights),
178            activator_public_key: Some(activator_public_key),
179            time_config,
180            service_chain_nodes: None,
181            multi_party_nodes: None,
182            domain: None,
183        }
184    }
185
186    /// Adds service chain attestation to the authorization token.
187    ///
188    /// Service chain tokens include attestations for each service node in the chain.
189    /// The token grants access and includes trusting relationships for the specified nodes.
190    ///
191    /// Can be combined with `.multi_party()` to create a token with both capabilities
192    /// (though this is not currently validated or actively used).
193    ///
194    /// # Arguments
195    /// * `nodes` - Vector of service nodes that will attest to the token
196    pub fn service_chain(mut self, nodes: Vec<ServiceNode>) -> Self {
197        self.service_chain_nodes = Some(nodes);
198        self
199    }
200
201    /// Adds multi-party attestation requirement to the authorization token.
202    ///
203    /// Multi-party tokens require attestation from all specified parties before becoming valid.
204    /// The key difference from service chain is that the token is invalid until all parties
205    /// have provided their attestations.
206    ///
207    /// Can be combined with `.service_chain()` to create a token with both capabilities
208    /// (though this is not currently validated or actively used).
209    ///
210    /// # Arguments
211    /// * `nodes` - Vector of multi-party nodes that must attest to the token
212    pub fn multi_party(mut self, nodes: Vec<ServiceNode>) -> Self {
213        self.multi_party_nodes = Some(nodes);
214        self
215    }
216
217    /// Restricts the authorization to a specific domain.
218    ///
219    /// Adds a domain restriction check to the authority block:
220    /// - `check if domain({domain})`
221    ///
222    /// This ensures the token can only be used within the specified domain.
223    ///
224    /// # Arguments
225    /// * `domain` - The domain to restrict to (e.g., "myapp.hessra.dev")
226    pub fn domain_restricted(mut self, domain: String) -> Self {
227        self.domain = Some(domain);
228        self
229    }
230
231    /// Issues (builds and signs) the authorization token.
232    ///
233    /// This method builds and signs either a singleton or latent capability token based on
234    /// how the builder was constructed.
235    ///
236    /// # Arguments
237    /// * `keypair` - The keypair to sign the token with
238    ///
239    /// # Returns
240    /// Base64-encoded biscuit token
241    pub fn issue(self, keypair: &KeyPair) -> Result<String, Box<dyn Error>> {
242        let start_time = self
243            .time_config
244            .start_time
245            .unwrap_or_else(|| Utc::now().timestamp());
246        let expiration = start_time + self.time_config.duration;
247
248        let domain = self.domain;
249
250        // Build the appropriate biscuit based on token type
251        let mut biscuit_builder = match self.token_type {
252            TokenType::Singleton => {
253                // Extract fields for singleton token
254                let subject = self.subject.ok_or("Singleton token requires subject")?;
255                let resource = self.resource.ok_or("Singleton token requires resource")?;
256                let operation = self.operation.ok_or("Singleton token requires operation")?;
257
258                // Build singleton authority block
259                biscuit!(
260                    r#"
261                        right({subject}, {resource}, {operation});
262                        check if subject($sub), resource($res), operation($op), right($sub, $res, $op);
263                        check if time($time), $time < {expiration};
264                    "#
265                )
266            }
267            TokenType::Latent => {
268                // Extract fields for latent token
269                let latent_rights = self
270                    .latent_rights
271                    .ok_or("Latent token requires latent_rights")?;
272                let activator_public_key_str = self
273                    .activator_public_key
274                    .ok_or("Latent token requires activator_public_key")?;
275                let activator_public_key = biscuit_key_from_string(activator_public_key_str)?;
276
277                // Start with empty builder
278                let mut builder = BiscuitBuilder::new();
279
280                // Add latent_right facts for each (resource, operation) pair
281                for (resource, operation) in latent_rights.clone() {
282                    builder = builder.fact(biscuit::macros::fact!(
283                        r#"latent_right({resource}, {operation})"#
284                    ))?;
285                }
286
287                // Add the rule that derives right from delegate and latent_right
288                builder = builder.rule(rule!(
289                    r#"
290                        right($subject, $resource, $operation) <-
291                            delegate($subject),
292                            right($resource, $operation),
293                            latent_right($resource, $operation)
294                            trusting {activator_public_key};
295                    "#
296                ))?;
297
298                // Add check that the derived right is valid (trusting activator)
299                builder = builder.check(check!(
300                    r#"
301                        check if subject($sub), resource($res), operation($op),
302                                  right($sub, $res, $op)
303                                  trusting {activator_public_key};
304                    "#
305                ))?;
306
307                // Add check that resource and operation are in latent_rights
308                builder = builder.check(check!(
309                    r#"
310                        check if resource($res), operation($op), latent_right($res, $op);
311                    "#
312                ))?;
313
314                // Add time check
315                builder = builder.check(check!(
316                    r#"
317                        check if time($time), $time < {expiration};
318                    "#
319                ))?;
320
321                builder
322            }
323        };
324
325        // Add domain restriction if specified (works for both token types)
326        if let Some(domain) = domain {
327            biscuit_builder = biscuit_builder.check(check!(
328                r#"
329                    check if domain({domain});
330                "#
331            ))?;
332        }
333
334        // Add service chain rules if specified (works for both token types)
335        if let Some(nodes) = self.service_chain_nodes {
336            for node in nodes {
337                let component = node.component;
338                let public_key = biscuit_key_from_string(node.public_key)?;
339                biscuit_builder = biscuit_builder.rule(rule!(
340                    r#"
341                        node($s, {component}) <- service($s) trusting {public_key};
342                    "#
343                ))?;
344            }
345        }
346
347        // Add multi-party checks if specified (works for both token types)
348        if let Some(nodes) = self.multi_party_nodes {
349            for node in nodes {
350                let component = node.component;
351                let public_key = biscuit_key_from_string(node.public_key)?;
352                biscuit_builder = biscuit_builder.check(check!(
353                    r#"
354                        check if namespace({component}) trusting {public_key};
355                    "#
356                ))?;
357            }
358        }
359
360        // Build and sign the biscuit
361        let biscuit = biscuit_builder.build(keypair)?;
362        info!("biscuit (authority): {}", biscuit);
363        let token = biscuit.to_base64()?;
364        Ok(token)
365    }
366}
367
368/// Creates a base biscuit builder with default time configuration.
369///
370/// This is a private helper function that creates a biscuit builder with the default
371/// time configuration (5 minutes duration from current time).
372///
373/// # Arguments
374///
375/// * `subject` - The subject (user) identifier
376/// * `resource` - The resource identifier
377/// * `operation` - The operation to be granted
378///
379/// # Returns
380///
381/// * `Ok(BiscuitBuilder)` - The configured biscuit builder if successful
382/// * `Err(Box<dyn Error>)` - If builder creation fails
383fn _create_base_biscuit_builder(
384    subject: String,
385    resource: String,
386    operation: String,
387) -> Result<BiscuitBuilder, Box<dyn Error>> {
388    create_base_biscuit_builder_with_time(subject, resource, operation, TokenTimeConfig::default())
389}
390
391/// Creates a base biscuit builder with custom time configuration.
392///
393/// This is a private helper function that creates a biscuit builder with custom
394/// time settings for token validity period.
395///
396/// # Arguments
397///
398/// * `subject` - The subject (user) identifier
399/// * `resource` - The resource identifier
400/// * `operation` - The operation to be granted
401/// * `time_config` - Time configuration for token validity
402///
403/// # Returns
404///
405/// * `Ok(BiscuitBuilder)` - The configured biscuit builder if successful
406/// * `Err(Box<dyn Error>)` - If builder creation fails
407fn create_base_biscuit_builder_with_time(
408    subject: String,
409    resource: String,
410    operation: String,
411    time_config: TokenTimeConfig,
412) -> Result<BiscuitBuilder, Box<dyn Error>> {
413    let start_time = time_config
414        .start_time
415        .unwrap_or_else(|| Utc::now().timestamp());
416    let expiration = start_time + time_config.duration;
417
418    let biscuit_builder = biscuit!(
419        r#"
420            right({subject}, {resource}, {operation});
421            check if subject($sub), resource($res), operation($op), right($sub, $res, $op);
422            check if time($time), $time < {expiration};
423        "#
424    );
425
426    Ok(biscuit_builder)
427}
428
429/// Creates a biscuit (not serialized, not base64 encoded) with custom time
430/// configuration.
431///
432/// This function creates a raw Biscuit object that can be further processed
433/// or converted to different formats. It grants the specified operation on
434/// the resource for the given subject.
435///
436/// # Arguments
437///
438/// * `subject` - The subject (user) identifier
439/// * `resource` - The resource identifier to grant access to
440/// * `operation` - The operation to grant access to
441/// * `key` - The key pair used to sign the token
442/// * `time_config` - Time configuration for token validity
443///
444/// # Returns
445///
446/// * `Ok(Biscuit)` - The raw biscuit if successful
447/// * `Err(Box<dyn Error>)` - If token creation fails
448pub fn create_raw_biscuit(
449    subject: String,
450    resource: String,
451    operation: String,
452    key: KeyPair,
453    time_config: TokenTimeConfig,
454) -> Result<Biscuit, Box<dyn Error>> {
455    let biscuit = create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?
456        .build(&key)?;
457
458    info!("biscuit (authority): {}", biscuit);
459
460    Ok(biscuit)
461}
462
463/// Creates a new biscuit token with the specified subject and resource.
464///
465/// This function creates a token that grants read and write access to the specified resource
466/// for the given subject. The token will be valid for 5 minutes by default.
467///
468/// # Arguments
469///
470/// * `subject` - The subject (user) identifier
471/// * `resource` - The resource identifier to grant access to
472/// * `operation` - The operation to grant access to
473/// * `key` - The key pair used to sign the token
474/// * `time_config` - Optional time configuration for token validity
475///
476/// # Returns
477///
478/// * `Ok(Vec<u8>)` - The binary token data if successful
479/// * `Err(Box<dyn Error>)` - If token creation fails
480pub fn create_biscuit(
481    subject: String,
482    resource: String,
483    operation: String,
484    key: KeyPair,
485    time_config: TokenTimeConfig,
486) -> Result<Vec<u8>, Box<dyn Error>> {
487    let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
488    let token = biscuit.to_vec()?;
489    Ok(token)
490}
491
492/// Creates a base64-encoded biscuit token with custom time configuration.
493///
494/// This is a private helper function that creates a biscuit token and returns
495/// it as a base64-encoded string for easy transmission and storage.
496///
497/// # Arguments
498///
499/// * `subject` - The subject (user) identifier
500/// * `resource` - The resource identifier to grant access to
501/// * `operation` - The operation to grant access to
502/// * `key` - The key pair used to sign the token
503/// * `time_config` - Time configuration for token validity
504///
505/// # Returns
506///
507/// * `Ok(String)` - The base64-encoded token if successful
508/// * `Err(Box<dyn Error>)` - If token creation fails
509fn create_base64_biscuit(
510    subject: String,
511    resource: String,
512    operation: String,
513    key: KeyPair,
514    time_config: TokenTimeConfig,
515) -> Result<String, Box<dyn Error>> {
516    let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
517    let token = biscuit.to_base64()?;
518    Ok(token)
519}
520
521/// Creates a biscuit token with default time configuration.
522///
523/// This function creates a base64-encoded token string that grants the specified
524/// operation on the resource for the given subject. The token will be valid for
525/// 5 minutes by default.
526///
527/// # Arguments
528///
529/// * `subject` - The subject (user) identifier
530/// * `resource` - The resource identifier to grant access to
531/// * `operation` - The operation to grant access to
532/// * `key` - The key pair used to sign the token
533///
534/// # Returns
535///
536/// * `Ok(String)` - The base64-encoded token if successful
537/// * `Err(Box<dyn Error>)` - If token creation fails
538pub fn create_token(
539    subject: String,
540    resource: String,
541    operation: String,
542    key: KeyPair,
543) -> Result<String, Box<dyn Error>> {
544    create_base64_biscuit(
545        subject,
546        resource,
547        operation,
548        key,
549        TokenTimeConfig::default(),
550    )
551}
552
553/// Creates a biscuit token with custom time configuration.
554///
555/// This function creates a base64-encoded token string that grants the specified
556/// operation on the resource for the given subject with custom time settings.
557///
558/// # Arguments
559///
560/// * `subject` - The subject (user) identifier
561/// * `resource` - The resource identifier to grant access to
562/// * `operation` - The operation to grant access to
563/// * `key` - The key pair used to sign the token
564/// * `time_config` - Time configuration for token validity
565///
566/// # Returns
567///
568/// * `Ok(String)` - The base64-encoded token if successful
569/// * `Err(Box<dyn Error>)` - If token creation fails
570pub fn create_token_with_time(
571    subject: String,
572    resource: String,
573    operation: String,
574    key: KeyPair,
575    time_config: TokenTimeConfig,
576) -> Result<String, Box<dyn Error>> {
577    create_base64_biscuit(subject, resource, operation, key, time_config)
578}
579
580/// Creates a new biscuit token with service chain attestations.
581/// Creates a new biscuit token with service chain attestations.
582///
583/// This function creates a token that grants access to the specified resource for the given subject,
584/// and includes attestations for each service node in the chain. The token will be valid for 5 minutes by default.
585///
586/// # Arguments
587///
588/// * `subject` - The subject (user) identifier
589/// * `resource` - The resource identifier to grant access to
590/// * `operation` - The operation to grant access to
591/// * `key` - The key pair used to sign the token
592/// * `nodes` - Vector of service nodes that will attest to the token
593///
594/// # Returns
595///
596/// * `Ok(Vec<u8>)` - The binary token data if successful
597/// * `Err(Box<dyn Error>)` - If token creation fails
598pub fn create_service_chain_biscuit(
599    subject: String,
600    resource: String,
601    operation: String,
602    key: KeyPair,
603    nodes: &Vec<ServiceNode>,
604    time_config: TokenTimeConfig,
605) -> Result<Vec<u8>, Box<dyn Error>> {
606    let biscuit =
607        create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
608    let token = biscuit.to_vec()?;
609    Ok(token)
610}
611
612/// Creates a base64-encoded service chain biscuit token.
613///
614/// This is a private helper function that creates a service chain biscuit token
615/// and returns it as a base64-encoded string for easy transmission and storage.
616///
617/// # Arguments
618///
619/// * `subject` - The subject (user) identifier
620/// * `resource` - The resource identifier to grant access to
621/// * `operation` - The operation to grant access to
622/// * `key` - The key pair used to sign the token
623/// * `nodes` - Vector of service nodes that will attest to the token
624/// * `time_config` - Time configuration for token validity
625///
626/// # Returns
627///
628/// * `Ok(String)` - The base64-encoded token if successful
629/// * `Err(Box<dyn Error>)` - If token creation fails
630fn create_base64_service_chain_biscuit(
631    subject: String,
632    resource: String,
633    operation: String,
634    key: KeyPair,
635    nodes: &Vec<ServiceNode>,
636    time_config: TokenTimeConfig,
637) -> Result<String, Box<dyn Error>> {
638    let biscuit =
639        create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
640    let token = biscuit.to_base64()?;
641    Ok(token)
642}
643
644/// Creates a raw service chain biscuit token.
645///
646/// This function creates a raw Biscuit object with service chain attestations
647/// that can be further processed or converted to different formats. It delegates
648/// to the time-aware version with the provided configuration.
649///
650/// # Arguments
651///
652/// * `subject` - The subject (user) identifier
653/// * `resource` - The resource identifier to grant access to
654/// * `operation` - The operation to grant access to
655/// * `key` - The key pair used to sign the token
656/// * `nodes` - Vector of service nodes that will attest to the token
657/// * `time_config` - Time configuration for token validity
658///
659/// # Returns
660///
661/// * `Ok(Biscuit)` - The raw biscuit if successful
662/// * `Err(Box<dyn Error>)` - If token creation fails
663pub fn create_raw_service_chain_biscuit(
664    subject: String,
665    resource: String,
666    operation: String,
667    key: KeyPair,
668    nodes: &Vec<ServiceNode>,
669    time_config: TokenTimeConfig,
670) -> Result<Biscuit, Box<dyn Error>> {
671    create_service_chain_biscuit_with_time(subject, resource, operation, key, nodes, time_config)
672}
673
674/// Creates a service chain biscuit token with default time configuration.
675///
676/// This function creates a base64-encoded service chain token string that grants
677/// the specified operation on the resource for the given subject. The token will
678/// be valid for 5 minutes by default and includes attestations for each service
679/// node in the chain.
680///
681/// # Arguments
682///
683/// * `subject` - The subject (user) identifier
684/// * `resource` - The resource identifier to grant access to
685/// * `operation` - The operation to grant access to
686/// * `key` - The key pair used to sign the token
687/// * `nodes` - Vector of service nodes that will attest to the token
688///
689/// # Returns
690///
691/// * `Ok(String)` - The base64-encoded token if successful
692/// * `Err(Box<dyn Error>)` - If token creation fails
693pub fn create_service_chain_token(
694    subject: String,
695    resource: String,
696    operation: String,
697    key: KeyPair,
698    nodes: &Vec<ServiceNode>,
699) -> Result<String, Box<dyn Error>> {
700    create_base64_service_chain_biscuit(
701        subject,
702        resource,
703        operation,
704        key,
705        nodes,
706        TokenTimeConfig::default(),
707    )
708}
709
710/// Creates a new biscuit token with service chain attestations and custom time settings.
711///
712/// This function creates a token that grants access to the specified resource for the given subject,
713/// includes attestations for each service node in the chain, and allows custom time configuration.
714///
715/// # Arguments
716///
717/// * `subject` - The subject (user) identifier
718/// * `resource` - The resource identifier to grant access to
719/// * `operation` - The operation to grant access to
720/// * `key` - The key pair used to sign the token
721/// * `nodes` - Vector of service nodes that will attest to the token
722/// * `time_config` - Time configuration for token validity
723///
724/// # Returns
725///
726/// * `Ok(Vec<u8>)` - The binary token data if successful
727/// * `Err(Box<dyn Error>)` - If token creation fails
728pub fn create_service_chain_biscuit_with_time(
729    subject: String,
730    resource: String,
731    operation: String,
732    key: KeyPair,
733    nodes: &Vec<ServiceNode>,
734    time_config: TokenTimeConfig,
735) -> Result<Biscuit, Box<dyn Error>> {
736    let service = resource.clone();
737    let mut biscuit_builder =
738        create_base_biscuit_builder_with_time(subject, service, operation, time_config)?;
739
740    // Add each node in the service chain to the biscuit builder
741    for node in nodes {
742        let component = node.component.clone();
743        let public_key = biscuit_key_from_string(node.public_key.clone())?;
744        biscuit_builder = biscuit_builder.rule(rule!(
745            r#"
746                node($s, {component}) <- service($s) trusting {public_key};
747            "#
748        ))?;
749    }
750
751    let biscuit = biscuit_builder.build(&key)?;
752
753    info!("biscuit (authority): {}", biscuit);
754
755    Ok(biscuit)
756}
757
758/// Creates a service chain biscuit token with custom time configuration.
759///
760/// This function creates a base64-encoded service chain token string that grants
761/// the specified operation on the resource for the given subject with custom time
762/// settings. The token includes attestations for each service node in the chain.
763///
764/// # Arguments
765///
766/// * `subject` - The subject (user) identifier
767/// * `resource` - The resource identifier to grant access to
768/// * `operation` - The operation to grant access to
769/// * `key` - The key pair used to sign the token
770/// * `nodes` - Vector of service nodes that will attest to the token
771/// * `time_config` - Time configuration for token validity
772///
773/// # Returns
774///
775/// * `Ok(String)` - The base64-encoded token if successful
776/// * `Err(Box<dyn Error>)` - If token creation fails
777pub fn create_service_chain_token_with_time(
778    subject: String,
779    resource: String,
780    operation: String,
781    key: KeyPair,
782    nodes: &Vec<ServiceNode>,
783    time_config: TokenTimeConfig,
784) -> Result<String, Box<dyn Error>> {
785    let biscuit = create_service_chain_biscuit_with_time(
786        subject,
787        resource,
788        operation,
789        key,
790        nodes,
791        time_config,
792    )?;
793    let token = biscuit.to_base64()?;
794    Ok(token)
795}
796
797/// Creates a new biscuit token with multi-party attestations.
798///
799/// This function creates a token that grants access to the specified resource for the given subject,
800/// includes attestations for each multi-party node in the chain, and allows custom time configuration.
801///
802/// The key difference between a multi-party biscuit and a service chain biscuit is that a multi-party
803/// biscuit is not valid until it has been attested by all the parties.
804///
805/// # Arguments
806///
807/// * `subject` - The subject (user) identifier
808/// * `resource` - The resource identifier to grant access to
809/// * `operation` - The operation to grant access to
810/// * `key` - The key pair used to sign the token
811/// * `multi_party_nodes` - Vector of multi-party nodes that will attest to the token
812pub fn create_raw_multi_party_biscuit(
813    subject: String,
814    resource: String,
815    operation: String,
816    key: KeyPair,
817    multi_party_nodes: &Vec<ServiceNode>,
818) -> Result<Biscuit, Box<dyn Error>> {
819    create_multi_party_biscuit_with_time(
820        subject,
821        resource,
822        operation,
823        key,
824        multi_party_nodes,
825        TokenTimeConfig::default(),
826    )
827}
828
829/// Creates a new biscuit token with multi-party attestations.
830///
831/// This function creates a token that grants access to the specified resource for the given subject,
832/// includes attestations for each multi-party node in the chain. The token will be valid for 5 minutes by default.
833///
834/// The key difference between a multi-party biscuit and a service chain biscuit is that a multi-party
835/// biscuit is not valid until it has been attested by all the parties.
836///
837/// # Arguments
838///
839/// * `subject` - The subject (user) identifier
840/// * `resource` - The resource identifier to grant access to
841/// * `operation` - The operation to grant access to
842/// * `key` - The key pair used to sign the token
843/// * `multi_party_nodes` - Vector of multi-party nodes that will attest to the token
844///
845/// # Returns
846///
847/// * `Ok(Vec<u8>)` - The binary token data if successful
848/// * `Err(Box<dyn Error>)` - If token creation fails
849pub fn create_multi_party_biscuit(
850    subject: String,
851    resource: String,
852    operation: String,
853    key: KeyPair,
854    multi_party_nodes: &Vec<ServiceNode>,
855) -> Result<Vec<u8>, Box<dyn Error>> {
856    let biscuit =
857        create_raw_multi_party_biscuit(subject, resource, operation, key, multi_party_nodes)?;
858    let token = biscuit.to_vec()?;
859    Ok(token)
860}
861
862/// Creates a base64-encoded multi-party biscuit token.
863///
864/// This is a private helper function that creates a multi-party biscuit token
865/// and returns it as a base64-encoded string for easy transmission and storage.
866/// Multi-party tokens require attestation from all specified nodes before they
867/// become valid.
868///
869/// # Arguments
870///
871/// * `subject` - The subject (user) identifier
872/// * `resource` - The resource identifier to grant access to
873/// * `operation` - The operation to grant access to
874/// * `key` - The key pair used to sign the token
875/// * `multi_party_nodes` - Vector of multi-party nodes that must attest to the token
876/// * `time_config` - Time configuration for token validity
877///
878/// # Returns
879///
880/// * `Ok(String)` - The base64-encoded token if successful
881/// * `Err(Box<dyn Error>)` - If token creation fails
882fn create_base64_multi_party_biscuit(
883    subject: String,
884    resource: String,
885    operation: String,
886    key: KeyPair,
887    multi_party_nodes: &Vec<ServiceNode>,
888    time_config: TokenTimeConfig,
889) -> Result<String, Box<dyn Error>> {
890    let biscuit = create_multi_party_biscuit_with_time(
891        subject,
892        resource,
893        operation,
894        key,
895        multi_party_nodes,
896        time_config,
897    )?;
898    let token = biscuit.to_base64()?;
899    Ok(token)
900}
901
902/// Creates a new multi-party biscuit token with default time configuration.
903///
904/// This function creates a token that grants access to the specified resource for the given subject,
905/// includes attestations for each multi-party node, and uses the default time configuration (5 minutes).
906///
907/// The key difference between a multi-party biscuit and a service chain biscuit is that a multi-party
908/// biscuit is not valid until it has been attested by all the parties.
909///
910/// # Arguments
911///
912/// * `subject` - The subject (user) identifier
913/// * `resource` - The resource identifier to grant access to
914/// * `operation` - The operation to grant access to
915/// * `key` - The key pair used to sign the token
916/// * `multi_party_nodes` - Vector of multi-party nodes that will attest to the token
917///
918/// # Returns
919///
920/// * `Ok(String)` - The base64-encoded token if successful
921/// * `Err(Box<dyn Error>)` - If token creation fails
922pub fn create_multi_party_token(
923    subject: String,
924    resource: String,
925    operation: String,
926    key: KeyPair,
927    multi_party_nodes: &Vec<ServiceNode>,
928) -> Result<String, Box<dyn Error>> {
929    create_base64_multi_party_biscuit(
930        subject,
931        resource,
932        operation,
933        key,
934        multi_party_nodes,
935        TokenTimeConfig::default(),
936    )
937}
938
939/// Creates a new biscuit token with multi-party attestations and custom time settings.
940///
941/// This function creates a token that grants access to the specified resource for the given subject,
942/// includes attestations for each multi-party node in the chain, and allows custom time configuration.
943///
944/// The key difference between a multi-party biscuit and a service chain biscuit is that a multi-party
945/// biscuit is not valid until it has been attested by all the parties.
946///
947/// # Arguments
948///
949/// * `subject` - The subject (user) identifier
950/// * `resource` - The resource identifier to grant access to
951/// * `operation` - The operation to grant access to
952/// * `key` - The key pair used to sign the token
953/// * `multi_party_nodes` - Vector of multi-party nodes that will attest to the token
954/// * `time_config` - Time configuration for token validity
955///
956/// # Returns
957///
958/// * `Ok(Biscuit)` - The raw biscuit if successful
959/// * `Err(Box<dyn Error>)` - If token creation fails
960pub fn create_multi_party_biscuit_with_time(
961    subject: String,
962    resource: String,
963    operation: String,
964    key: KeyPair,
965    multi_party_nodes: &Vec<ServiceNode>,
966    time_config: TokenTimeConfig,
967) -> Result<Biscuit, Box<dyn Error>> {
968    let mut biscuit_builder =
969        create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?;
970
971    for node in multi_party_nodes {
972        let component = node.component.clone();
973        let public_key = biscuit_key_from_string(node.public_key.clone())?;
974        biscuit_builder = biscuit_builder.check(check!(
975            r#"
976                check if namespace({component}) trusting {public_key};
977            "#
978        ))?;
979    }
980
981    let biscuit = biscuit_builder.build(&key)?;
982
983    info!("biscuit (authority): {}", biscuit);
984
985    Ok(biscuit)
986}
987
988pub fn create_multi_party_token_with_time(
989    subject: String,
990    resource: String,
991    operation: String,
992    key: KeyPair,
993    multi_party_nodes: &Vec<ServiceNode>,
994    time_config: TokenTimeConfig,
995) -> Result<String, Box<dyn Error>> {
996    let biscuit = create_multi_party_biscuit_with_time(
997        subject,
998        resource,
999        operation,
1000        key,
1001        multi_party_nodes,
1002        time_config,
1003    )?;
1004    let token = biscuit.to_base64()?;
1005    Ok(token)
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010    use super::*;
1011    use crate::attenuate::{activate_latent_token, activate_latent_token_from_string};
1012    use crate::verify::{verify_biscuit_local, verify_service_chain_biscuit_local};
1013    use biscuit::macros::block;
1014    use biscuit::Biscuit;
1015    #[test]
1016    fn test_create_biscuit() {
1017        let subject = "test@test.com".to_owned();
1018        let resource: String = "res1".to_string();
1019        let operation = "read".to_string();
1020        let root = KeyPair::new();
1021        let public_key = root.public();
1022        let token = create_biscuit(
1023            subject.clone(),
1024            resource.clone(),
1025            operation.clone(),
1026            root,
1027            TokenTimeConfig::default(),
1028        )
1029        .unwrap();
1030
1031        let res = verify_biscuit_local(token, public_key, subject, resource, operation);
1032        assert!(res.is_ok());
1033    }
1034
1035    #[test]
1036    fn test_biscuit_operations() {
1037        let subject = "test@test.com".to_owned();
1038        let resource = "res1".to_string();
1039        let operation = "read".to_string();
1040        let root = KeyPair::new();
1041        let public_key = root.public();
1042
1043        // Test read operation
1044        let read_token = create_biscuit(
1045            subject.clone(),
1046            resource.clone(),
1047            operation.clone(),
1048            root,
1049            TokenTimeConfig::default(),
1050        )
1051        .unwrap();
1052
1053        let res = verify_biscuit_local(
1054            read_token.clone(),
1055            public_key,
1056            subject.clone(),
1057            resource.clone(),
1058            operation.clone(),
1059        );
1060        assert!(res.is_ok());
1061
1062        let root = KeyPair::new();
1063        let public_key = root.public();
1064
1065        // Test write operation
1066        let write_token = create_biscuit(
1067            subject.clone(),
1068            resource.clone(),
1069            "write".to_string(),
1070            root,
1071            TokenTimeConfig::default(),
1072        )
1073        .unwrap();
1074
1075        let res = verify_biscuit_local(
1076            write_token.clone(),
1077            public_key,
1078            subject.clone(),
1079            resource.clone(),
1080            "write".to_string(),
1081        );
1082        assert!(res.is_ok());
1083
1084        // Test that read token cannot be used for write
1085        let res = verify_biscuit_local(
1086            read_token,
1087            public_key,
1088            subject.clone(),
1089            resource.clone(),
1090            "write".to_string(),
1091        );
1092        assert!(res.is_err());
1093
1094        // Test that write token cannot be used for read
1095        let res = verify_biscuit_local(
1096            write_token,
1097            public_key,
1098            subject.clone(),
1099            resource.clone(),
1100            "read".to_string(),
1101        );
1102        assert!(res.is_err());
1103    }
1104
1105    #[test]
1106    fn test_biscuit_expiration() {
1107        let subject = "test@test.com".to_owned();
1108        let resource = "res1".to_string();
1109        let operation = "read".to_string();
1110        let root = KeyPair::new();
1111        let public_key = root.public();
1112        // Create a biscuit with a 5 minute expiration from now
1113        let token = create_biscuit(
1114            subject.clone(),
1115            resource.clone(),
1116            operation.clone(),
1117            root,
1118            TokenTimeConfig::default(),
1119        )
1120        .unwrap();
1121
1122        let res = verify_biscuit_local(
1123            token.clone(),
1124            public_key,
1125            subject.clone(),
1126            resource.clone(),
1127            operation.clone(),
1128        );
1129        assert!(res.is_ok());
1130
1131        // Create a biscuit with a start time over 5 minutes ago
1132        let root = KeyPair::new();
1133        let token = create_biscuit(
1134            subject.clone(),
1135            resource.clone(),
1136            operation.clone(),
1137            root,
1138            TokenTimeConfig {
1139                start_time: Some(Utc::now().timestamp() - 301),
1140                duration: 300,
1141            },
1142        )
1143        .unwrap();
1144        let res = verify_biscuit_local(token, public_key, subject, resource, operation);
1145        assert!(res.is_err());
1146    }
1147
1148    #[test]
1149    fn test_custom_token_time_config() {
1150        let subject = "test@test.com".to_owned();
1151        let resource = "res1".to_string();
1152        let operation = "read".to_string();
1153        let root = KeyPair::new();
1154        let public_key = root.public();
1155
1156        // Create token with custom start time (1 hour in the past) and longer duration (1 hour)
1157        let past_time = Utc::now().timestamp() - 3600;
1158        let time_config = TokenTimeConfig {
1159            start_time: Some(past_time),
1160            duration: 7200, // 2 hours
1161        };
1162
1163        let token = create_biscuit(
1164            subject.clone(),
1165            resource.clone(),
1166            operation.clone(),
1167            root,
1168            time_config,
1169        )
1170        .unwrap();
1171
1172        // Token should be valid at a time between start and expiration
1173        let res = verify_biscuit_local(
1174            token.clone(),
1175            public_key,
1176            subject.clone(),
1177            resource.clone(),
1178            operation.clone(),
1179        );
1180        assert!(res.is_ok());
1181    }
1182
1183    #[test]
1184    fn test_service_chain_biscuit() {
1185        let subject = "test@test.com".to_owned();
1186        let resource = "res1".to_string();
1187        let operation = "read".to_string();
1188        let root = KeyPair::new();
1189        let public_key = root.public();
1190        let chain_key = KeyPair::new();
1191        let chain_public_key = hex::encode(chain_key.public().to_bytes());
1192        let chain_public_key = format!("ed25519/{}", chain_public_key);
1193        let chain_node = ServiceNode {
1194            component: "edge_function".to_string(),
1195            public_key: chain_public_key.clone(),
1196        };
1197        let nodes = vec![chain_node];
1198        let token = create_service_chain_biscuit(
1199            subject.clone(),
1200            resource.clone(),
1201            operation.clone(),
1202            root,
1203            &nodes,
1204            TokenTimeConfig::default(),
1205        );
1206        if let Err(e) = &token {
1207            println!("Error: {}", e);
1208        }
1209        assert!(token.is_ok());
1210        let token = token.unwrap();
1211        let res = verify_biscuit_local(
1212            token.clone(),
1213            public_key,
1214            subject.clone(),
1215            resource.clone(),
1216            operation.clone(),
1217        );
1218        assert!(res.is_ok());
1219        let biscuit = Biscuit::from(&token, public_key).unwrap();
1220        let third_party_request = biscuit.third_party_request().unwrap();
1221        let third_party_block = block!(
1222            r#"
1223            service("res1");
1224            "#
1225        );
1226        let third_party_block = third_party_request
1227            .create_block(&chain_key.private(), third_party_block)
1228            .unwrap();
1229        let attested_biscuit = biscuit
1230            .append_third_party(chain_key.public(), third_party_block)
1231            .unwrap();
1232        let attested_token = attested_biscuit.to_vec().unwrap();
1233        let res = verify_service_chain_biscuit_local(
1234            attested_token,
1235            public_key,
1236            subject.clone(),
1237            resource.clone(),
1238            operation.clone(),
1239            nodes,
1240            None,
1241        );
1242        assert!(res.is_ok());
1243    }
1244
1245    #[test]
1246    fn test_service_chain_biscuit_with_component_name() {
1247        let subject = "test@test.com".to_owned();
1248        let resource = "res1".to_string();
1249        let root = KeyPair::new();
1250        let public_key = root.public();
1251
1252        // Create two chain nodes
1253        let chain_key1 = KeyPair::new();
1254        let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
1255        let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
1256        let chain_node1 = ServiceNode {
1257            component: "edge_function".to_string(),
1258            public_key: chain_public_key1.clone(),
1259        };
1260
1261        let chain_key2 = KeyPair::new();
1262        let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
1263        let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
1264        let chain_node2 = ServiceNode {
1265            component: "middleware".to_string(),
1266            public_key: chain_public_key2.clone(),
1267        };
1268
1269        let nodes = vec![chain_node1.clone(), chain_node2.clone()];
1270
1271        // Create the initial token using the first node
1272        let token = create_service_chain_biscuit(
1273            subject.clone(),
1274            resource.clone(),
1275            "read".to_string(),
1276            root,
1277            &nodes,
1278            TokenTimeConfig::default(),
1279        );
1280        assert!(token.is_ok());
1281        let token = token.unwrap();
1282
1283        // Create the biscuit and add third-party blocks
1284        let biscuit = Biscuit::from(&token, public_key).unwrap();
1285        let third_party_request = biscuit.third_party_request().unwrap();
1286        let third_party_block = block!(
1287            r#"
1288                service("res1");
1289            "#
1290        );
1291        let third_party_block = third_party_request
1292            .create_block(&chain_key1.private(), third_party_block)
1293            .unwrap();
1294        let attested_biscuit = biscuit
1295            .append_third_party(chain_key1.public(), third_party_block)
1296            .unwrap();
1297        let attested_token = attested_biscuit.to_vec().unwrap();
1298
1299        // Test with the "edge_function" component name - should pass
1300        // the first node in the service chain checking itself is valid
1301        // since it is checking the base biscuit
1302        let res = verify_service_chain_biscuit_local(
1303            attested_token.clone(),
1304            public_key,
1305            subject.clone(),
1306            resource.clone(),
1307            "read".to_string(),
1308            nodes.clone(),
1309            Some("edge_function".to_string()),
1310        );
1311        // This should fail - since we're not verifying any nodes when checking up to but not including "edge_function"
1312        assert!(res.is_ok());
1313
1314        // Create a chain with two nodes
1315        let nodes = vec![chain_node1.clone(), chain_node2.clone()];
1316
1317        // Test with "middleware" component - should succeed verifying node1 only
1318        let res = verify_service_chain_biscuit_local(
1319            attested_token.clone(),
1320            public_key,
1321            subject.clone(),
1322            resource.clone(),
1323            "read".to_string(),
1324            nodes.clone(),
1325            Some("middleware".to_string()),
1326        );
1327        assert!(res.is_ok());
1328    }
1329
1330    #[test]
1331    fn test_service_chain_biscuit_with_nonexistent_component() {
1332        let subject = "test@test.com".to_owned();
1333        let resource = "res1".to_string();
1334        let root = KeyPair::new();
1335        let public_key = root.public();
1336        let chain_key = KeyPair::new();
1337        let chain_public_key = hex::encode(chain_key.public().to_bytes());
1338        let chain_public_key = format!("ed25519/{}", chain_public_key);
1339        let chain_node = ServiceNode {
1340            component: "edge_function".to_string(),
1341            public_key: chain_public_key.clone(),
1342        };
1343        let nodes = vec![chain_node];
1344        let token = create_service_chain_biscuit(
1345            subject.clone(),
1346            resource.clone(),
1347            "read".to_string(),
1348            root,
1349            &nodes,
1350            TokenTimeConfig::default(),
1351        );
1352        assert!(token.is_ok());
1353        let token = token.unwrap();
1354
1355        let biscuit = Biscuit::from(&token, public_key).unwrap();
1356        let third_party_request = biscuit.third_party_request().unwrap();
1357        let third_party_block = block!(
1358            r#"
1359            service("res1");
1360            "#
1361        );
1362        let third_party_block = third_party_request
1363            .create_block(&chain_key.private(), third_party_block)
1364            .unwrap();
1365        let attested_biscuit = biscuit
1366            .append_third_party(chain_key.public(), third_party_block)
1367            .unwrap();
1368        let attested_token = attested_biscuit.to_vec().unwrap();
1369
1370        // Test with a component name that doesn't exist in the chain
1371        let res = verify_service_chain_biscuit_local(
1372            attested_token,
1373            public_key,
1374            subject.clone(),
1375            resource.clone(),
1376            "read".to_string(),
1377            nodes.clone(),
1378            Some("nonexistent_component".to_string()),
1379        );
1380        assert!(res.is_err());
1381
1382        // Verify the error message contains the component name
1383        let err = res.unwrap_err().to_string();
1384        assert!(err.contains("nonexistent_component"));
1385    }
1386
1387    #[test]
1388    fn test_service_chain_biscuit_with_multiple_nodes() {
1389        let subject = "test@test.com".to_owned();
1390        let resource = "res1".to_string();
1391        let root = KeyPair::new();
1392        let public_key = root.public();
1393
1394        // Create three chain nodes
1395        let chain_key1 = KeyPair::new();
1396        let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
1397        let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
1398        let chain_node1 = ServiceNode {
1399            component: "edge_function".to_string(),
1400            public_key: chain_public_key1.clone(),
1401        };
1402
1403        let chain_key2 = KeyPair::new();
1404        let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
1405        let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
1406        let chain_node2 = ServiceNode {
1407            component: "middleware".to_string(),
1408            public_key: chain_public_key2.clone(),
1409        };
1410
1411        let chain_key3 = KeyPair::new();
1412        let chain_public_key3 = hex::encode(chain_key3.public().to_bytes());
1413        let chain_public_key3 = format!("ed25519/{}", chain_public_key3);
1414        let chain_node3 = ServiceNode {
1415            component: "backend".to_string(),
1416            public_key: chain_public_key3.clone(),
1417        };
1418
1419        // Create the initial token with the first node
1420        let nodes = vec![
1421            chain_node1.clone(),
1422            chain_node2.clone(),
1423            chain_node3.clone(),
1424        ];
1425        let token = create_service_chain_biscuit(
1426            subject.clone(),
1427            resource.clone(),
1428            "read".to_string(),
1429            root,
1430            &nodes,
1431            TokenTimeConfig::default(),
1432        );
1433        assert!(token.is_ok());
1434        let token = token.unwrap();
1435
1436        println!("Created initial token");
1437
1438        // Create the biscuit and add node1's block
1439        let biscuit = Biscuit::from(&token, public_key).unwrap();
1440        let third_party_request1 = biscuit.third_party_request().unwrap();
1441        let third_party_block1 = block!(
1442            r#"
1443                service("res1");
1444            "#
1445        );
1446        let third_party_block1 = third_party_request1
1447            .create_block(&chain_key1.private(), third_party_block1)
1448            .unwrap();
1449        let attested_biscuit1 = biscuit
1450            .append_third_party(chain_key1.public(), third_party_block1)
1451            .unwrap();
1452
1453        // Chain with all three nodes
1454        let all_nodes = vec![
1455            chain_node1.clone(),
1456            chain_node2.clone(),
1457            chain_node3.clone(),
1458        ];
1459        let attested_token1 = attested_biscuit1.to_vec().unwrap();
1460
1461        // Test 1: Verify up to but not including middleware
1462        // This should verify edge_function only
1463        let res = verify_service_chain_biscuit_local(
1464            attested_token1.clone(),
1465            public_key,
1466            subject.clone(),
1467            resource.clone(),
1468            "read".to_string(),
1469            all_nodes.clone(),
1470            Some("middleware".to_string()),
1471        );
1472        assert!(res.is_ok());
1473
1474        // Test 3: Verify up to but not including backend
1475        // This should try to verify both edge_function and middleware
1476        // but since the middleware attestation wasn't added, it will fail
1477        let res = verify_service_chain_biscuit_local(
1478            attested_token1.clone(),
1479            public_key,
1480            subject.clone(),
1481            resource.clone(),
1482            "read".to_string(),
1483            all_nodes.clone(),
1484            Some("backend".to_string()),
1485        );
1486        assert!(res.is_err());
1487    }
1488
1489    #[test]
1490    fn test_service_chain_biscuit_with_custom_time() {
1491        let subject = "test@test.com".to_owned();
1492        let resource = "res1".to_string();
1493        let root = KeyPair::new();
1494        let public_key = root.public();
1495        let chain_key = KeyPair::new();
1496        let chain_public_key = hex::encode(chain_key.public().to_bytes());
1497        let chain_public_key = format!("ed25519/{}", chain_public_key);
1498        let chain_node = ServiceNode {
1499            component: "edge_function".to_string(),
1500            public_key: chain_public_key.clone(),
1501        };
1502        let nodes = vec![chain_node];
1503
1504        // Create a valid token with default time configuration (5 minutes)
1505        let valid_token = create_service_chain_biscuit_with_time(
1506            subject.clone(),
1507            resource.clone(),
1508            "read".to_string(),
1509            root,
1510            &nodes,
1511            TokenTimeConfig::default(),
1512        );
1513        assert!(valid_token.is_ok());
1514        let valid_token = valid_token.unwrap().to_vec().unwrap();
1515
1516        // Verify the valid token works
1517        let res = verify_biscuit_local(
1518            valid_token.clone(),
1519            public_key,
1520            subject.clone(),
1521            resource.clone(),
1522            "read".to_string(),
1523        );
1524        assert!(res.is_ok());
1525
1526        // Create an expired token (start time 6 minutes ago with 5 minute duration)
1527        let expired_time_config = TokenTimeConfig {
1528            start_time: Some(Utc::now().timestamp() - 360), // 6 minutes ago
1529            duration: 300,                                  // 5 minutes
1530        };
1531
1532        // Create a new key pair for the expired token
1533        let root2 = KeyPair::new();
1534        let public_key2 = root2.public();
1535
1536        let expired_token = create_service_chain_biscuit_with_time(
1537            subject.clone(),
1538            resource.clone(),
1539            "read".to_string(),
1540            root2,
1541            &nodes,
1542            expired_time_config,
1543        );
1544        assert!(expired_token.is_ok());
1545        let expired_token = expired_token.unwrap().to_vec().unwrap();
1546
1547        // Verify expired token fails
1548        let res = verify_biscuit_local(
1549            expired_token,
1550            public_key2,
1551            subject,
1552            resource,
1553            "read".to_string(),
1554        );
1555        assert!(res.is_err());
1556    }
1557
1558    #[test]
1559    fn test_multi_party_biscuit_helper_functions() {
1560        let subject = "test@test.com".to_owned();
1561        let resource = "res1".to_string();
1562        let operation = "read".to_string();
1563        let root = KeyPair::new();
1564
1565        // Create a multi-party node
1566        let multi_party_key = KeyPair::new();
1567        let multi_party_public_key = hex::encode(multi_party_key.public().to_bytes());
1568        let multi_party_public_key = format!("ed25519/{}", multi_party_public_key);
1569        let multi_party_node = ServiceNode {
1570            component: "approval_service".to_string(),
1571            public_key: multi_party_public_key.clone(),
1572        };
1573        let nodes = vec![multi_party_node];
1574
1575        // Test create_multi_party_token (default time config)
1576        let token_string = create_multi_party_token(
1577            subject.clone(),
1578            resource.clone(),
1579            operation.clone(),
1580            root,
1581            &nodes,
1582        );
1583        assert!(token_string.is_ok());
1584
1585        // Test create_multi_party_biscuit (binary token with default time config)
1586        let root2 = KeyPair::new();
1587        let binary_token = create_multi_party_biscuit(
1588            subject.clone(),
1589            resource.clone(),
1590            operation.clone(),
1591            root2,
1592            &nodes,
1593        );
1594        assert!(binary_token.is_ok());
1595
1596        // Test create_raw_multi_party_biscuit (raw biscuit with default time config)
1597        let root3 = KeyPair::new();
1598        let raw_biscuit = create_raw_multi_party_biscuit(
1599            subject.clone(),
1600            resource.clone(),
1601            operation.clone(),
1602            root3,
1603            &nodes,
1604        );
1605        assert!(raw_biscuit.is_ok());
1606
1607        // Test create_multi_party_token_with_time (custom time config)
1608        let custom_time_config = TokenTimeConfig {
1609            start_time: Some(Utc::now().timestamp()),
1610            duration: 600, // 10 minutes
1611        };
1612        let root4 = KeyPair::new();
1613        let custom_time_token = create_multi_party_token_with_time(
1614            subject.clone(),
1615            resource.clone(),
1616            operation.clone(),
1617            root4,
1618            &nodes,
1619            custom_time_config,
1620        );
1621        assert!(custom_time_token.is_ok());
1622
1623        // Test create_multi_party_biscuit_with_time (raw biscuit with custom time config)
1624        let root5 = KeyPair::new();
1625        let custom_time_biscuit = create_multi_party_biscuit_with_time(
1626            subject.clone(),
1627            resource.clone(),
1628            operation.clone(),
1629            root5,
1630            &nodes,
1631            custom_time_config,
1632        );
1633        assert!(custom_time_biscuit.is_ok());
1634    }
1635
1636    #[test]
1637    fn test_basic_authorization_with_domain_restriction() {
1638        let subject = "alice".to_string();
1639        let resource = "resource1".to_string();
1640        let operation = "read".to_string();
1641        let domain = "myapp.hessra.dev".to_string();
1642        let keypair = KeyPair::new();
1643        let public_key = keypair.public();
1644
1645        // Create basic authorization token with domain restriction
1646        let token = HessraAuthorization::new(
1647            subject.clone(),
1648            resource.clone(),
1649            operation.clone(),
1650            TokenTimeConfig::default(),
1651        )
1652        .domain_restricted(domain.clone())
1653        .issue(&keypair);
1654
1655        assert!(token.is_ok(), "Failed to create domain-restricted token");
1656        let token = token.unwrap();
1657
1658        // Parse and verify the token
1659        let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1660
1661        // Build authorizer with domain fact - should succeed
1662        let authz = crate::verify::build_base_authorizer(
1663            subject.clone(),
1664            resource.clone(),
1665            operation.clone(),
1666            Some(domain.clone()),
1667        )
1668        .unwrap();
1669        assert!(
1670            authz.build(&biscuit).unwrap().authorize().is_ok(),
1671            "Token should verify with correct domain"
1672        );
1673
1674        // Build authorizer without domain fact - should fail
1675        let authz_no_domain = crate::verify::build_base_authorizer(
1676            subject.clone(),
1677            resource.clone(),
1678            operation.clone(),
1679            None,
1680        )
1681        .unwrap();
1682        assert!(
1683            authz_no_domain
1684                .build(&biscuit)
1685                .unwrap()
1686                .authorize()
1687                .is_err(),
1688            "Token should fail verification without domain fact"
1689        );
1690
1691        // Build authorizer with wrong domain - should fail
1692        let authz_wrong_domain = crate::verify::build_base_authorizer(
1693            subject,
1694            resource,
1695            operation,
1696            Some("wrongdomain.com".to_string()),
1697        )
1698        .unwrap();
1699        assert!(
1700            authz_wrong_domain
1701                .build(&biscuit)
1702                .unwrap()
1703                .authorize()
1704                .is_err(),
1705            "Token should fail verification with wrong domain"
1706        );
1707    }
1708
1709    #[test]
1710    fn test_service_chain_with_domain_restriction() {
1711        let subject = "alice".to_string();
1712        let resource = "resource1".to_string();
1713        let operation = "read".to_string();
1714        let domain = "myapp.hessra.dev".to_string();
1715        let keypair = KeyPair::new();
1716        let public_key = keypair.public();
1717
1718        // Create service node
1719        let chain_key = KeyPair::new();
1720        let chain_public_key = hex::encode(chain_key.public().to_bytes());
1721        let chain_public_key = format!("ed25519/{}", chain_public_key);
1722        let chain_node = ServiceNode {
1723            component: "edge_function".to_string(),
1724            public_key: chain_public_key.clone(),
1725        };
1726        let nodes = vec![chain_node];
1727
1728        // Create service chain token with domain restriction
1729        let token = HessraAuthorization::new(
1730            subject.clone(),
1731            resource.clone(),
1732            operation.clone(),
1733            TokenTimeConfig::default(),
1734        )
1735        .service_chain(nodes.clone())
1736        .domain_restricted(domain.clone())
1737        .issue(&keypair);
1738
1739        assert!(
1740            token.is_ok(),
1741            "Failed to create domain-restricted service chain token"
1742        );
1743        let token = token.unwrap();
1744
1745        // Parse and verify the token
1746        let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1747
1748        // Build authorizer with domain fact - should succeed
1749        let authz = crate::verify::build_base_authorizer(
1750            subject.clone(),
1751            resource.clone(),
1752            operation.clone(),
1753            Some(domain),
1754        )
1755        .unwrap();
1756        assert!(
1757            authz.build(&biscuit).unwrap().authorize().is_ok(),
1758            "Service chain token should verify with correct domain"
1759        );
1760
1761        // Build authorizer without domain fact - should fail
1762        let authz_no_domain =
1763            crate::verify::build_base_authorizer(subject, resource, operation, None).unwrap();
1764        assert!(
1765            authz_no_domain
1766                .build(&biscuit)
1767                .unwrap()
1768                .authorize()
1769                .is_err(),
1770            "Service chain token should fail verification without domain fact"
1771        );
1772    }
1773
1774    #[test]
1775    fn test_multi_party_with_domain_restriction() {
1776        let subject = "alice".to_string();
1777        let resource = "resource1".to_string();
1778        let operation = "read".to_string();
1779        let domain = "myapp.hessra.dev".to_string();
1780        let keypair = KeyPair::new();
1781        let public_key = keypair.public();
1782
1783        // Create multi-party node
1784        let party_key = KeyPair::new();
1785        let party_public_key = hex::encode(party_key.public().to_bytes());
1786        let party_public_key = format!("ed25519/{}", party_public_key);
1787        let party_node = ServiceNode {
1788            component: "approval_service".to_string(),
1789            public_key: party_public_key.clone(),
1790        };
1791        let nodes = vec![party_node];
1792
1793        // Create multi-party token with domain restriction
1794        let token = HessraAuthorization::new(
1795            subject.clone(),
1796            resource.clone(),
1797            operation.clone(),
1798            TokenTimeConfig::default(),
1799        )
1800        .multi_party(nodes.clone())
1801        .domain_restricted(domain.clone())
1802        .issue(&keypair);
1803
1804        assert!(
1805            token.is_ok(),
1806            "Failed to create domain-restricted multi-party token"
1807        );
1808        let token = token.unwrap();
1809
1810        // Parse and verify the token
1811        let biscuit = Biscuit::from_base64(&token, public_key).unwrap();
1812
1813        // Build authorizer with domain fact - token will still fail because multi-party needs attestations
1814        // But we're testing that domain check is present
1815        let _authz = crate::verify::build_base_authorizer(
1816            subject.clone(),
1817            resource.clone(),
1818            operation.clone(),
1819            Some(domain),
1820        )
1821        .unwrap();
1822        // Multi-party token needs attestation, so this will fail for that reason, not domain
1823        // We're just checking the token was created successfully with domain check
1824        assert!(biscuit.to_base64().is_ok(), "Token should be valid biscuit");
1825
1826        // Build authorizer without domain fact - should also fail
1827        let authz_no_domain =
1828            crate::verify::build_base_authorizer(subject, resource, operation, None).unwrap();
1829        assert!(
1830            authz_no_domain
1831                .build(&biscuit)
1832                .unwrap()
1833                .authorize()
1834                .is_err(),
1835            "Multi-party token should fail verification without domain fact or attestations"
1836        );
1837    }
1838
1839    #[test]
1840    fn test_authorization_verifier_with_domain() {
1841        use crate::verify::AuthorizationVerifier;
1842
1843        let subject = "alice".to_string();
1844        let resource = "resource1".to_string();
1845        let operation = "read".to_string();
1846        let domain = "myapp.hessra.dev".to_string();
1847        let keypair = KeyPair::new();
1848        let public_key = keypair.public();
1849
1850        // Create basic authorization token with domain restriction
1851        let token = HessraAuthorization::new(
1852            subject.clone(),
1853            resource.clone(),
1854            operation.clone(),
1855            TokenTimeConfig::default(),
1856        )
1857        .domain_restricted(domain.clone())
1858        .issue(&keypair)
1859        .expect("Failed to create domain-restricted token");
1860
1861        // Verify with matching domain using builder - should succeed
1862        assert!(
1863            AuthorizationVerifier::new(
1864                token.clone(),
1865                public_key,
1866                subject.clone(),
1867                resource.clone(),
1868                operation.clone(),
1869            )
1870            .with_domain(domain.clone())
1871            .verify()
1872            .is_ok(),
1873            "Token should verify with correct domain using builder"
1874        );
1875
1876        // Verify without domain context - should fail
1877        assert!(
1878            AuthorizationVerifier::new(
1879                token.clone(),
1880                public_key,
1881                subject.clone(),
1882                resource.clone(),
1883                operation.clone(),
1884            )
1885            .verify()
1886            .is_err(),
1887            "Token should fail verification without domain context"
1888        );
1889
1890        // Verify with wrong domain - should fail
1891        assert!(
1892            AuthorizationVerifier::new(
1893                token.clone(),
1894                public_key,
1895                subject.clone(),
1896                resource.clone(),
1897                operation.clone(),
1898            )
1899            .with_domain("wrongdomain.com".to_string())
1900            .verify()
1901            .is_err(),
1902            "Token should fail verification with wrong domain"
1903        );
1904
1905        // Test with non-domain-restricted token - extra domain context shouldn't break it
1906        let regular_token = HessraAuthorization::new(
1907            subject.clone(),
1908            resource.clone(),
1909            operation.clone(),
1910            TokenTimeConfig::default(),
1911        )
1912        .issue(&keypair)
1913        .expect("Failed to create regular token");
1914
1915        // Regular token should pass with or without domain context
1916        assert!(
1917            AuthorizationVerifier::new(
1918                regular_token.clone(),
1919                public_key,
1920                subject.clone(),
1921                resource.clone(),
1922                operation.clone(),
1923            )
1924            .verify()
1925            .is_ok(),
1926            "Regular token should verify without domain context"
1927        );
1928
1929        assert!(
1930            AuthorizationVerifier::new(regular_token, public_key, subject, resource, operation,)
1931                .with_domain(domain)
1932                .verify()
1933                .is_ok(),
1934            "Regular token should verify even with extra domain context"
1935        );
1936    }
1937
1938    #[test]
1939    fn test_service_chain_verifier_with_domain() {
1940        use crate::verify::{AuthorizationVerifier, ServiceNode};
1941
1942        let subject = "alice".to_string();
1943        let resource = "resource1".to_string();
1944        let operation = "read".to_string();
1945        let domain = "myapp.hessra.dev".to_string();
1946        let keypair = KeyPair::new();
1947        let public_key = keypair.public();
1948
1949        // Create service node
1950        let node_keypair = KeyPair::new();
1951        let node_public_key = node_keypair.public();
1952        let node_key_string = format!("ed25519/{}", hex::encode(node_public_key.to_bytes()));
1953
1954        let service_nodes = vec![ServiceNode {
1955            component: "api-gateway".to_string(),
1956            public_key: node_key_string,
1957        }];
1958
1959        // Create service chain token with domain restriction
1960        let token = HessraAuthorization::new(
1961            subject.clone(),
1962            resource.clone(),
1963            operation.clone(),
1964            TokenTimeConfig::default(),
1965        )
1966        .service_chain(service_nodes.clone())
1967        .domain_restricted(domain.clone())
1968        .issue(&keypair)
1969        .expect("Failed to create service chain token with domain");
1970
1971        // Convert token to bytes for attestation
1972        let token_bytes = crate::decode_token(&token).expect("Failed to decode token");
1973
1974        // Add service node attestation
1975        let attested_token_bytes = crate::attest::add_service_node_attestation(
1976            token_bytes,
1977            public_key,
1978            &resource,
1979            &node_keypair,
1980        )
1981        .expect("Failed to add attestation");
1982
1983        // Verify with service chain and domain - should succeed
1984        assert!(
1985            AuthorizationVerifier::from_bytes(
1986                attested_token_bytes.clone(),
1987                public_key,
1988                subject.clone(),
1989                resource.clone(),
1990                operation.clone(),
1991            )
1992            .expect("Failed to create verifier")
1993            .with_service_chain(service_nodes.clone(), Some("api-gateway".to_string()))
1994            .with_domain(domain.clone())
1995            .verify()
1996            .is_ok(),
1997            "Service chain token should verify with domain"
1998        );
1999
2000        // Verify with service chain but no domain - should fail
2001        assert!(
2002            AuthorizationVerifier::from_bytes(
2003                attested_token_bytes.clone(),
2004                public_key,
2005                subject.clone(),
2006                resource.clone(),
2007                operation.clone(),
2008            )
2009            .expect("Failed to create verifier")
2010            .with_service_chain(service_nodes.clone(), Some("api-gateway".to_string()))
2011            .verify()
2012            .is_err(),
2013            "Service chain token should fail without domain"
2014        );
2015
2016        // Verify with domain but no service chain checks
2017        // This should actually PASS because the attestation is valid and embedded in the token
2018        // The service chain checks are additional validation for specific component requirements
2019        assert!(
2020            AuthorizationVerifier::from_bytes(
2021                attested_token_bytes,
2022                public_key,
2023                subject,
2024                resource,
2025                operation,
2026            )
2027            .expect("Failed to create verifier")
2028            .with_domain(domain)
2029            .verify()
2030            .is_ok(),
2031            "Service chain token with valid attestation should pass basic verification"
2032        );
2033    }
2034
2035    #[test]
2036    fn test_latent_capability_basic() {
2037        let root_keypair = KeyPair::new();
2038        let root_public_key = root_keypair.public();
2039
2040        let activator_keypair = KeyPair::new();
2041        let activator_public_key = format!(
2042            "ed25519/{}",
2043            hex::encode(activator_keypair.public().to_bytes())
2044        );
2045
2046        // Create latent token with multiple rights
2047        let latent_rights = vec![
2048            ("resource1".to_string(), "read".to_string()),
2049            ("resource1".to_string(), "write".to_string()),
2050            ("resource2".to_string(), "read".to_string()),
2051        ];
2052
2053        let latent_token = HessraAuthorization::new_latent(
2054            latent_rights,
2055            activator_public_key,
2056            TokenTimeConfig::default(),
2057        )
2058        .issue(&root_keypair)
2059        .expect("Failed to create latent token");
2060
2061        // Activate the latent token for a specific subject and right
2062        let subject = "alice".to_string();
2063        let resource = "resource1".to_string();
2064        let operation = "read".to_string();
2065
2066        let activated_token = activate_latent_token_from_string(
2067            latent_token.clone(),
2068            root_public_key,
2069            subject.clone(),
2070            resource.clone(),
2071            operation.clone(),
2072            &activator_keypair,
2073            TokenTimeConfig::default(),
2074        )
2075        .expect("Failed to activate latent token");
2076
2077        // Verify the activated token
2078        let result = crate::verify::verify_token_local(
2079            &activated_token,
2080            root_public_key,
2081            &subject,
2082            &resource,
2083            &operation,
2084        );
2085
2086        assert!(result.is_ok(), "Activated token should verify successfully");
2087    }
2088
2089    #[test]
2090    fn test_latent_capability_multiple_activations() {
2091        let root_keypair = KeyPair::new();
2092        let root_public_key = root_keypair.public();
2093
2094        let activator_keypair = KeyPair::new();
2095        let activator_public_key = format!(
2096            "ed25519/{}",
2097            hex::encode(activator_keypair.public().to_bytes())
2098        );
2099
2100        // Create latent token with multiple rights
2101        let latent_rights = vec![
2102            ("resource1".to_string(), "read".to_string()),
2103            ("resource1".to_string(), "write".to_string()),
2104            ("resource2".to_string(), "read".to_string()),
2105        ];
2106
2107        let latent_token = HessraAuthorization::new_latent(
2108            latent_rights,
2109            activator_public_key,
2110            TokenTimeConfig::default(),
2111        )
2112        .issue(&root_keypair)
2113        .expect("Failed to create latent token");
2114
2115        // Activate the same latent token multiple times with different combinations
2116        let activations = vec![
2117            (
2118                "alice".to_string(),
2119                "resource1".to_string(),
2120                "read".to_string(),
2121            ),
2122            (
2123                "bob".to_string(),
2124                "resource1".to_string(),
2125                "write".to_string(),
2126            ),
2127            (
2128                "charlie".to_string(),
2129                "resource2".to_string(),
2130                "read".to_string(),
2131            ),
2132        ];
2133
2134        for (subject, resource, operation) in activations {
2135            let activated_token = activate_latent_token_from_string(
2136                latent_token.clone(),
2137                root_public_key,
2138                subject.clone(),
2139                resource.clone(),
2140                operation.clone(),
2141                &activator_keypair,
2142                TokenTimeConfig::default(),
2143            )
2144            .expect("Failed to activate latent token");
2145
2146            // Verify each activated token
2147            let result = crate::verify::verify_token_local(
2148                &activated_token,
2149                root_public_key,
2150                &subject,
2151                &resource,
2152                &operation,
2153            );
2154
2155            assert!(
2156                result.is_ok(),
2157                "Activated token for {} should verify successfully",
2158                subject
2159            );
2160        }
2161    }
2162
2163    #[test]
2164    fn test_latent_capability_invalid_right() {
2165        let root_keypair = KeyPair::new();
2166        let root_public_key = root_keypair.public();
2167
2168        let activator_keypair = KeyPair::new();
2169        let activator_public_key = format!(
2170            "ed25519/{}",
2171            hex::encode(activator_keypair.public().to_bytes())
2172        );
2173
2174        // Create latent token with limited rights
2175        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2176
2177        let latent_token = HessraAuthorization::new_latent(
2178            latent_rights,
2179            activator_public_key,
2180            TokenTimeConfig::default(),
2181        )
2182        .issue(&root_keypair)
2183        .expect("Failed to create latent token");
2184
2185        // Try to activate with a right that's not in latent_rights
2186        let activated_token = activate_latent_token_from_string(
2187            latent_token,
2188            root_public_key,
2189            "alice".to_string(),
2190            "resource1".to_string(),
2191            "write".to_string(), // Not in latent_rights!
2192            &activator_keypair,
2193            TokenTimeConfig::default(),
2194        )
2195        .expect("Activation should succeed (validation happens at verification)");
2196
2197        // Verification should fail because the right is not in latent_rights
2198        let result = crate::verify::verify_token_local(
2199            &activated_token,
2200            root_public_key,
2201            "alice",
2202            "resource1",
2203            "write",
2204        );
2205
2206        assert!(
2207            result.is_err(),
2208            "Should fail to verify token with right not in latent_rights"
2209        );
2210    }
2211
2212    #[test]
2213    fn test_latent_capability_with_domain_restriction() {
2214        let root_keypair = KeyPair::new();
2215        let root_public_key = root_keypair.public();
2216
2217        let activator_keypair = KeyPair::new();
2218        let activator_public_key = format!(
2219            "ed25519/{}",
2220            hex::encode(activator_keypair.public().to_bytes())
2221        );
2222
2223        let domain = "myapp.hessra.dev".to_string();
2224
2225        // Create latent token with domain restriction
2226        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2227
2228        let latent_token = HessraAuthorization::new_latent(
2229            latent_rights,
2230            activator_public_key,
2231            TokenTimeConfig::default(),
2232        )
2233        .domain_restricted(domain.clone())
2234        .issue(&root_keypair)
2235        .expect("Failed to create latent token with domain restriction");
2236
2237        // Activate the token
2238        let activated_token = activate_latent_token_from_string(
2239            latent_token,
2240            root_public_key,
2241            "alice".to_string(),
2242            "resource1".to_string(),
2243            "read".to_string(),
2244            &activator_keypair,
2245            TokenTimeConfig::default(),
2246        )
2247        .expect("Failed to activate latent token");
2248
2249        // Verify with correct domain
2250        let result = crate::verify::AuthorizationVerifier::new(
2251            activated_token.clone(),
2252            root_public_key,
2253            "alice".to_string(),
2254            "resource1".to_string(),
2255            "read".to_string(),
2256        )
2257        .with_domain(domain.clone())
2258        .verify();
2259
2260        assert!(result.is_ok(), "Should verify with correct domain");
2261
2262        // Verify without domain should fail
2263        let result = crate::verify::AuthorizationVerifier::new(
2264            activated_token,
2265            root_public_key,
2266            "alice".to_string(),
2267            "resource1".to_string(),
2268            "read".to_string(),
2269        )
2270        .verify();
2271
2272        assert!(result.is_err(), "Should fail to verify without domain");
2273    }
2274
2275    #[test]
2276    fn test_latent_capability_with_multi_party() {
2277        let root_keypair = KeyPair::new();
2278        let root_public_key = root_keypair.public();
2279
2280        let activator_keypair = KeyPair::new();
2281        let activator_public_key = format!(
2282            "ed25519/{}",
2283            hex::encode(activator_keypair.public().to_bytes())
2284        );
2285
2286        // Create multi-party node
2287        let party_keypair = KeyPair::new();
2288        let party_public_key =
2289            format!("ed25519/{}", hex::encode(party_keypair.public().to_bytes()));
2290        let party_node = ServiceNode {
2291            component: "approval_service".to_string(),
2292            public_key: party_public_key,
2293        };
2294
2295        // Create latent token with multi-party requirement
2296        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2297
2298        let latent_token = HessraAuthorization::new_latent(
2299            latent_rights,
2300            activator_public_key,
2301            TokenTimeConfig::default(),
2302        )
2303        .multi_party(vec![party_node])
2304        .issue(&root_keypair)
2305        .expect("Failed to create latent token with multi-party");
2306
2307        // Add multi-party attestation to the latent token (before activation)
2308        let latent_token_bytes =
2309            crate::decode_token(&latent_token).expect("Failed to decode token");
2310        let attested_latent_bytes = crate::attest::add_multi_party_attestation(
2311            latent_token_bytes,
2312            root_public_key,
2313            "approval_service".to_string(),
2314            party_keypair,
2315        )
2316        .expect("Failed to add multi-party attestation");
2317
2318        // Now activate the attested latent token
2319        let activated_token_bytes = activate_latent_token(
2320            attested_latent_bytes,
2321            root_public_key,
2322            "alice".to_string(),
2323            "resource1".to_string(),
2324            "read".to_string(),
2325            &activator_keypair,
2326            TokenTimeConfig::default(),
2327        )
2328        .expect("Failed to activate attested latent token");
2329
2330        let activated_token = crate::encode_token(&activated_token_bytes);
2331
2332        // Verify the activated token
2333        let result = crate::verify::verify_token_local(
2334            &activated_token,
2335            root_public_key,
2336            "alice",
2337            "resource1",
2338            "read",
2339        );
2340
2341        assert!(
2342            result.is_ok(),
2343            "Activated token with multi-party attestation should verify"
2344        );
2345    }
2346
2347    #[test]
2348    fn test_activated_latent_with_service_chain() {
2349        let root_keypair = KeyPair::new();
2350        let root_public_key = root_keypair.public();
2351
2352        let activator_keypair = KeyPair::new();
2353        let activator_public_key = format!(
2354            "ed25519/{}",
2355            hex::encode(activator_keypair.public().to_bytes())
2356        );
2357
2358        // Create service chain node
2359        let chain_keypair = KeyPair::new();
2360        let chain_public_key =
2361            format!("ed25519/{}", hex::encode(chain_keypair.public().to_bytes()));
2362        let chain_node = ServiceNode {
2363            component: "edge_function".to_string(),
2364            public_key: chain_public_key,
2365        };
2366
2367        // Create latent token with service chain
2368        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2369
2370        let latent_token = HessraAuthorization::new_latent(
2371            latent_rights,
2372            activator_public_key,
2373            TokenTimeConfig::default(),
2374        )
2375        .service_chain(vec![chain_node.clone()])
2376        .issue(&root_keypair)
2377        .expect("Failed to create latent token with service chain");
2378
2379        // Activate the latent token first
2380        let activated_token_bytes = activate_latent_token(
2381            crate::decode_token(&latent_token).expect("Failed to decode"),
2382            root_public_key,
2383            "alice".to_string(),
2384            "resource1".to_string(),
2385            "read".to_string(),
2386            &activator_keypair,
2387            TokenTimeConfig::default(),
2388        )
2389        .expect("Failed to activate latent token");
2390
2391        // Add service chain attestation to the activated token
2392        let attested_token_bytes = crate::attest::add_service_node_attestation(
2393            activated_token_bytes,
2394            root_public_key,
2395            "resource1",
2396            &chain_keypair,
2397        )
2398        .expect("Failed to add service chain attestation");
2399
2400        // Verify with service chain
2401        let result = verify_service_chain_biscuit_local(
2402            attested_token_bytes,
2403            root_public_key,
2404            "alice".to_string(),
2405            "resource1".to_string(),
2406            "read".to_string(),
2407            vec![chain_node],
2408            None,
2409        );
2410
2411        assert!(
2412            result.is_ok(),
2413            "Activated latent token with service chain should verify"
2414        );
2415    }
2416
2417    #[test]
2418    fn test_latent_capability_wrong_activator_key() {
2419        let root_keypair = KeyPair::new();
2420        let root_public_key = root_keypair.public();
2421
2422        let activator_keypair = KeyPair::new();
2423        let activator_public_key = format!(
2424            "ed25519/{}",
2425            hex::encode(activator_keypair.public().to_bytes())
2426        );
2427
2428        // Create latent token bound to specific activator
2429        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2430
2431        let latent_token = HessraAuthorization::new_latent(
2432            latent_rights,
2433            activator_public_key,
2434            TokenTimeConfig::default(),
2435        )
2436        .issue(&root_keypair)
2437        .expect("Failed to create latent token");
2438
2439        // Try to activate with a different keypair
2440        let wrong_activator_keypair = KeyPair::new();
2441
2442        let activated_token = activate_latent_token_from_string(
2443            latent_token,
2444            root_public_key,
2445            "alice".to_string(),
2446            "resource1".to_string(),
2447            "read".to_string(),
2448            &wrong_activator_keypair,
2449            TokenTimeConfig::default(),
2450        )
2451        .expect("Activation succeeds (wrong key detected at verification)");
2452
2453        // Verification should fail because the activator key doesn't match
2454        let result = crate::verify::verify_token_local(
2455            &activated_token,
2456            root_public_key,
2457            "alice",
2458            "resource1",
2459            "read",
2460        );
2461
2462        assert!(
2463            result.is_err(),
2464            "Should fail to verify token activated with wrong key"
2465        );
2466    }
2467
2468    #[test]
2469    fn test_latent_capability_time_attenuation() {
2470        let root_keypair = KeyPair::new();
2471        let root_public_key = root_keypair.public();
2472
2473        let activator_keypair = KeyPair::new();
2474        let activator_public_key = format!(
2475            "ed25519/{}",
2476            hex::encode(activator_keypair.public().to_bytes())
2477        );
2478
2479        // Create latent token with 30 minute expiration
2480        let latent_rights = vec![("resource1".to_string(), "read".to_string())];
2481
2482        let latent_token = HessraAuthorization::new_latent(
2483            latent_rights,
2484            activator_public_key,
2485            TokenTimeConfig {
2486                start_time: Some(Utc::now().timestamp()),
2487                duration: 1800, // 30 minutes
2488            },
2489        )
2490        .issue(&root_keypair)
2491        .expect("Failed to create latent token");
2492
2493        // Activate with 5 minute expiration (shorter than latent token)
2494        let activated_token_short = activate_latent_token_from_string(
2495            latent_token.clone(),
2496            root_public_key,
2497            "alice".to_string(),
2498            "resource1".to_string(),
2499            "read".to_string(),
2500            &activator_keypair,
2501            TokenTimeConfig {
2502                start_time: Some(Utc::now().timestamp()),
2503                duration: 300, // 5 minutes
2504            },
2505        )
2506        .expect("Failed to activate latent token");
2507
2508        // Should verify successfully now
2509        let result = crate::verify::verify_token_local(
2510            &activated_token_short,
2511            root_public_key,
2512            "alice",
2513            "resource1",
2514            "read",
2515        );
2516        assert!(
2517            result.is_ok(),
2518            "Activated token with 5min expiration should verify"
2519        );
2520
2521        // Create an expired activation (1 second ago, already expired)
2522        let activated_token_expired = activate_latent_token_from_string(
2523            latent_token.clone(),
2524            root_public_key,
2525            "bob".to_string(),
2526            "resource1".to_string(),
2527            "read".to_string(),
2528            &activator_keypair,
2529            TokenTimeConfig {
2530                start_time: Some(Utc::now().timestamp() - 2), // Started 2 seconds ago
2531                duration: 1,                                  // 1 second duration (already expired)
2532            },
2533        )
2534        .expect("Failed to activate latent token");
2535
2536        // Should fail verification because activation expired
2537        let result = crate::verify::verify_token_local(
2538            &activated_token_expired,
2539            root_public_key,
2540            "bob",
2541            "resource1",
2542            "read",
2543        );
2544        assert!(
2545            result.is_err(),
2546            "Activated token with expired time should fail verification"
2547        );
2548
2549        // Test that even though latent token is still valid (30min),
2550        // the shorter activation time (5min) is respected
2551        // We create a latent token that expires in the future but activate it
2552        // with an already-expired time config
2553        let latent_token_long = HessraAuthorization::new_latent(
2554            vec![("resource2".to_string(), "write".to_string())],
2555            format!(
2556                "ed25519/{}",
2557                hex::encode(activator_keypair.public().to_bytes())
2558            ),
2559            TokenTimeConfig {
2560                start_time: Some(Utc::now().timestamp()),
2561                duration: 3600, // 1 hour - very long
2562            },
2563        )
2564        .issue(&root_keypair)
2565        .expect("Failed to create long-lived latent token");
2566
2567        let activated_expired_despite_latent = activate_latent_token_from_string(
2568            latent_token_long,
2569            root_public_key,
2570            "charlie".to_string(),
2571            "resource2".to_string(),
2572            "write".to_string(),
2573            &activator_keypair,
2574            TokenTimeConfig {
2575                start_time: Some(Utc::now().timestamp() - 10), // Started 10 seconds ago
2576                duration: 1,                                   // 1 second duration (expired)
2577            },
2578        )
2579        .expect("Failed to activate long-lived latent token");
2580
2581        // Should fail even though latent token is still valid
2582        let result = crate::verify::verify_token_local(
2583            &activated_expired_despite_latent,
2584            root_public_key,
2585            "charlie",
2586            "resource2",
2587            "write",
2588        );
2589        assert!(
2590            result.is_err(),
2591            "Activation expiration should take precedence over latent expiration"
2592        );
2593    }
2594
2595    #[test]
2596    fn test_verify_capability_token_basic() {
2597        let subject = "alice".to_string();
2598        let resource = "resource1".to_string();
2599        let operation = "read".to_string();
2600        let keypair = KeyPair::new();
2601        let public_key = keypair.public();
2602
2603        // Create token with subject
2604        let token = create_token(
2605            subject.clone(),
2606            resource.clone(),
2607            operation.clone(),
2608            keypair,
2609        )
2610        .unwrap();
2611
2612        // Verify without providing subject - should succeed
2613        let result =
2614            crate::verify::verify_capability_token_local(&token, public_key, &resource, &operation);
2615
2616        assert!(result.is_ok());
2617    }
2618
2619    #[test]
2620    fn test_verify_capability_token_wrong_resource() {
2621        let keypair = KeyPair::new();
2622        let public_key = keypair.public();
2623
2624        let token = create_token(
2625            "alice".to_string(),
2626            "resource1".to_string(),
2627            "read".to_string(),
2628            keypair,
2629        )
2630        .unwrap();
2631
2632        // Try to verify for wrong resource
2633        let result = crate::verify::verify_capability_token_local(
2634            &token,
2635            public_key,
2636            "resource2", // Wrong resource
2637            "read",
2638        );
2639
2640        assert!(result.is_err());
2641    }
2642
2643    #[test]
2644    fn test_verify_capability_token_wrong_operation() {
2645        let keypair = KeyPair::new();
2646        let public_key = keypair.public();
2647
2648        let token = create_token(
2649            "alice".to_string(),
2650            "resource1".to_string(),
2651            "read".to_string(),
2652            keypair,
2653        )
2654        .unwrap();
2655
2656        // Try to verify for wrong operation
2657        let result = crate::verify::verify_capability_token_local(
2658            &token,
2659            public_key,
2660            "resource1",
2661            "write", // Wrong operation
2662        );
2663
2664        assert!(result.is_err());
2665    }
2666
2667    #[test]
2668    fn test_verify_capability_with_domain() {
2669        let domain = "myapp.hessra.dev".to_string();
2670        let keypair = KeyPair::new();
2671        let public_key = keypair.public();
2672
2673        // Create token with domain restriction
2674        let token = HessraAuthorization::new(
2675            "alice".to_string(),
2676            "resource1".to_string(),
2677            "read".to_string(),
2678            TokenTimeConfig::default(),
2679        )
2680        .domain_restricted(domain.clone())
2681        .issue(&keypair)
2682        .unwrap();
2683
2684        // Verify capability with matching domain
2685        let result = crate::verify::AuthorizationVerifier::new_capability(
2686            token.clone(),
2687            public_key,
2688            "resource1".to_string(),
2689            "read".to_string(),
2690        )
2691        .with_domain(domain.clone())
2692        .verify();
2693
2694        assert!(result.is_ok());
2695
2696        // Verify capability without domain - should fail
2697        let result = crate::verify::AuthorizationVerifier::new_capability(
2698            token,
2699            public_key,
2700            "resource1".to_string(),
2701            "read".to_string(),
2702        )
2703        .verify();
2704
2705        assert!(result.is_err());
2706    }
2707
2708    #[test]
2709    fn test_verify_capability_with_service_chain() {
2710        let keypair = KeyPair::new();
2711        let public_key = keypair.public();
2712
2713        let chain_keypair = KeyPair::new();
2714        let chain_public_key =
2715            format!("ed25519/{}", hex::encode(chain_keypair.public().to_bytes()));
2716        let chain_node = ServiceNode {
2717            component: "edge_function".to_string(),
2718            public_key: chain_public_key,
2719        };
2720
2721        // Create service chain token
2722        let token = HessraAuthorization::new(
2723            "alice".to_string(),
2724            "resource1".to_string(),
2725            "read".to_string(),
2726            TokenTimeConfig::default(),
2727        )
2728        .service_chain(vec![chain_node.clone()])
2729        .issue(&keypair)
2730        .unwrap();
2731
2732        // Add attestation
2733        let token_bytes = crate::decode_token(&token).unwrap();
2734        let attested = crate::attest::add_service_node_attestation(
2735            token_bytes,
2736            public_key,
2737            "resource1",
2738            &chain_keypair,
2739        )
2740        .unwrap();
2741        let attested_token = crate::encode_token(&attested);
2742
2743        // Verify capability with service chain
2744        let result = crate::verify::verify_service_chain_capability_token_local(
2745            &attested_token,
2746            public_key,
2747            "resource1",
2748            "read",
2749            vec![chain_node],
2750            None,
2751        );
2752
2753        assert!(result.is_ok());
2754    }
2755
2756    #[test]
2757    fn test_capability_verifier_builder() {
2758        let keypair = KeyPair::new();
2759        let public_key = keypair.public();
2760        let domain = "example.com".to_string();
2761
2762        let token = HessraAuthorization::new(
2763            "alice".to_string(),
2764            "resource1".to_string(),
2765            "read".to_string(),
2766            TokenTimeConfig::default(),
2767        )
2768        .domain_restricted(domain.clone())
2769        .issue(&keypair)
2770        .unwrap();
2771
2772        // Test builder pattern
2773        let result = crate::verify::AuthorizationVerifier::new_capability(
2774            token,
2775            public_key,
2776            "resource1".to_string(),
2777            "read".to_string(),
2778        )
2779        .with_domain(domain)
2780        .verify();
2781
2782        assert!(result.is_ok());
2783    }
2784
2785    #[test]
2786    fn test_capability_vs_identity_verification() {
2787        let keypair = KeyPair::new();
2788        let public_key = keypair.public();
2789
2790        // Create token for alice
2791        let token = create_token(
2792            "alice".to_string(),
2793            "document_123".to_string(),
2794            "read".to_string(),
2795            keypair,
2796        )
2797        .unwrap();
2798
2799        // Identity-based: Must specify correct subject
2800        let result = crate::verify::verify_token_local(
2801            &token,
2802            public_key,
2803            "alice", // Must match
2804            "document_123",
2805            "read",
2806        );
2807        assert!(result.is_ok());
2808
2809        // Identity-based: Wrong subject fails
2810        let result = crate::verify::verify_token_local(
2811            &token,
2812            public_key,
2813            "bob", // Wrong subject
2814            "document_123",
2815            "read",
2816        );
2817        assert!(result.is_err());
2818
2819        // Capability-based: Don't care about subject
2820        let result = crate::verify::verify_capability_token_local(
2821            &token,
2822            public_key,
2823            "document_123", // No subject needed
2824            "read",
2825        );
2826        assert!(result.is_ok());
2827    }
2828}