1pub mod brc114;
9pub mod cache;
11pub mod callbacks;
13pub mod config;
15pub mod ensure;
17pub mod metadata;
19pub mod originator;
21pub mod p_module;
23pub mod token_crud;
25pub mod types;
27
28use std::collections::HashMap;
29use std::sync::Arc;
30
31use async_trait::async_trait;
32use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
33use base64::Engine as _;
34
35use bsv::wallet::interfaces::{
36 AbortActionArgs, AbortActionResult, AcquireCertificateArgs, AuthenticatedResult, Certificate,
37 CreateActionArgs, CreateActionResult, CreateHmacArgs, CreateHmacResult, CreateSignatureArgs,
38 CreateSignatureResult, DecryptArgs, DecryptResult, DiscoverByAttributesArgs,
39 DiscoverByIdentityKeyArgs, DiscoverCertificatesResult, EncryptArgs, EncryptResult,
40 GetHeaderArgs, GetHeaderResult, GetHeightResult, GetNetworkResult, GetPublicKeyArgs,
41 GetPublicKeyResult, GetVersionResult, InternalizeActionArgs, InternalizeActionResult,
42 ListActionsArgs, ListActionsResult, ListCertificatesArgs, ListCertificatesResult,
43 ListOutputsArgs, ListOutputsResult, ProveCertificateArgs, ProveCertificateResult,
44 RelinquishCertificateArgs, RelinquishCertificateResult, RelinquishOutputArgs,
45 RelinquishOutputResult, RevealCounterpartyKeyLinkageArgs, RevealCounterpartyKeyLinkageResult,
46 RevealSpecificKeyLinkageArgs, RevealSpecificKeyLinkageResult, SignActionArgs, SignActionResult,
47 VerifyHmacArgs, VerifyHmacResult, VerifySignatureArgs, VerifySignatureResult, WalletInterface,
48};
49
50use config::PermissionsManagerConfig;
51use p_module::PermissionsModule;
52use types::{
53 GroupedPermissions, PermissionRequest, PermissionResponse, PermissionToken, PermissionType,
54};
55
56pub struct WalletPermissionsManager {
66 inner: Arc<dyn WalletInterface + Send + Sync>,
68 admin_originator: String,
70 config: PermissionsManagerConfig,
72 permission_modules: HashMap<String, Arc<dyn PermissionsModule>>,
74 callbacks: Option<Arc<dyn callbacks::PermissionCallbacks>>,
76 cache: cache::PermissionCache,
78}
79
80impl WalletPermissionsManager {
81 pub fn new(
86 inner: Arc<dyn WalletInterface + Send + Sync>,
87 admin_originator: String,
88 config: PermissionsManagerConfig,
89 ) -> Self {
90 let normalized_admin = originator::normalize_originator(&admin_originator);
91 Self {
92 inner,
93 admin_originator: if normalized_admin.is_empty() {
94 admin_originator
95 } else {
96 normalized_admin
97 },
98 config,
99 permission_modules: HashMap::new(),
100 callbacks: None,
101 cache: cache::PermissionCache::new(),
102 }
103 }
104
105 pub(crate) fn is_admin(&self, originator: Option<&str>) -> bool {
107 match originator {
108 Some(o) => originator::is_admin_originator(o, &self.admin_originator),
109 None => false,
110 }
111 }
112
113 pub fn register_module(&mut self, scheme_id: String, module: Arc<dyn PermissionsModule>) {
118 self.permission_modules.insert(scheme_id, module);
119 }
120
121 pub(crate) fn has_permission_module(&self, scheme_id: &str) -> bool {
123 self.permission_modules.contains_key(scheme_id)
124 }
125
126 pub fn config(&self) -> &PermissionsManagerConfig {
128 &self.config
129 }
130
131 pub fn inner(&self) -> &Arc<dyn WalletInterface + Send + Sync> {
133 &self.inner
134 }
135
136 pub(crate) fn admin_originator(&self) -> String {
138 self.admin_originator.clone()
139 }
140
141 pub fn bind_callback(&mut self, cbs: Arc<dyn callbacks::PermissionCallbacks>) {
147 self.callbacks = Some(cbs);
148 }
149
150 pub fn unbind_callback(&mut self) {
152 self.callbacks = None;
153 }
154
155 pub async fn clear_caches(&self) {
157 self.cache.clear().await;
158 }
159
160 pub(crate) fn cache(&self) -> &cache::PermissionCache {
162 &self.cache
163 }
164
165 pub(crate) fn callbacks(&self) -> &Option<Arc<dyn callbacks::PermissionCallbacks>> {
167 &self.callbacks
168 }
169
170 pub async fn grant_permission(
176 &self,
177 request: &PermissionRequest,
178 expiry: Option<u64>,
179 ) -> Result<(), bsv::wallet::error::WalletError> {
180 token_crud::grant_permission(self.inner.as_ref(), request, expiry, &self.admin_originator)
181 .await
182 }
183
184 pub fn deny_permission(&self, request: &PermissionRequest) -> PermissionResponse {
186 token_crud::deny_permission(request)
187 }
188
189 pub async fn grant_grouped_permission(
191 &self,
192 grouped: &GroupedPermissions,
193 expiry: Option<u64>,
194 ) -> Result<(), bsv::wallet::error::WalletError> {
195 token_crud::grant_grouped_permission(
196 self.inner.as_ref(),
197 grouped,
198 expiry,
199 &self.admin_originator,
200 )
201 .await
202 }
203
204 pub fn deny_grouped_permission(&self, grouped: &GroupedPermissions) -> Vec<PermissionResponse> {
206 token_crud::deny_grouped_permission(grouped)
207 }
208
209 pub async fn revoke_permission(
211 &self,
212 txid: &str,
213 output_index: u32,
214 permission_type: PermissionType,
215 ) -> Result<(), bsv::wallet::error::WalletError> {
216 let basket = match permission_type {
217 PermissionType::ProtocolPermission => types::BASKET_DPACP,
218 PermissionType::BasketAccess => types::BASKET_DBAP,
219 PermissionType::CertificateAccess => types::BASKET_DCAP,
220 PermissionType::SpendingAuthorization => types::BASKET_DSAP,
221 };
222 token_crud::revoke_permission_token(
223 self.inner.as_ref(),
224 txid,
225 output_index,
226 basket,
227 &self.admin_originator,
228 )
229 .await
230 }
231
232 pub async fn revoke_all_for_originator(
234 &self,
235 originator_domain: &str,
236 ) -> Result<u32, bsv::wallet::error::WalletError> {
237 token_crud::revoke_all_for_originator(
238 self.inner.as_ref(),
239 originator_domain,
240 &self.admin_originator,
241 )
242 .await
243 }
244
245 pub async fn list_protocol_permissions(
247 &self,
248 originator_domain: &str,
249 ) -> Result<Vec<PermissionToken>, bsv::wallet::error::WalletError> {
250 token_crud::list_protocol_permissions(
251 self.inner.as_ref(),
252 originator_domain,
253 &self.admin_originator,
254 )
255 .await
256 }
257
258 pub async fn has_protocol_permission(
260 &self,
261 originator_domain: &str,
262 protocol: &str,
263 security_level: u8,
264 counterparty: &str,
265 ) -> Result<bool, bsv::wallet::error::WalletError> {
266 token_crud::has_protocol_permission(
267 self.inner.as_ref(),
268 originator_domain,
269 protocol,
270 security_level,
271 counterparty,
272 &self.admin_originator,
273 )
274 .await
275 }
276
277 pub async fn list_basket_access(
279 &self,
280 originator_domain: &str,
281 ) -> Result<Vec<PermissionToken>, bsv::wallet::error::WalletError> {
282 token_crud::list_basket_access(
283 self.inner.as_ref(),
284 originator_domain,
285 &self.admin_originator,
286 )
287 .await
288 }
289
290 pub async fn has_basket_access(
292 &self,
293 originator_domain: &str,
294 basket_name: &str,
295 ) -> Result<bool, bsv::wallet::error::WalletError> {
296 token_crud::has_basket_access(
297 self.inner.as_ref(),
298 originator_domain,
299 basket_name,
300 &self.admin_originator,
301 )
302 .await
303 }
304
305 pub async fn list_certificate_access(
307 &self,
308 originator_domain: &str,
309 ) -> Result<Vec<PermissionToken>, bsv::wallet::error::WalletError> {
310 token_crud::list_certificate_access(
311 self.inner.as_ref(),
312 originator_domain,
313 &self.admin_originator,
314 )
315 .await
316 }
317
318 pub async fn list_spending_authorizations(
320 &self,
321 originator_domain: &str,
322 ) -> Result<Vec<PermissionToken>, bsv::wallet::error::WalletError> {
323 token_crud::list_spending_authorizations(
324 self.inner.as_ref(),
325 originator_domain,
326 &self.admin_originator,
327 )
328 .await
329 }
330}
331
332fn protocol_info(proto: &bsv::wallet::types::Protocol) -> (String, u8) {
338 (proto.protocol.clone(), proto.security_level)
339}
340
341fn counterparty_str(cpty: &bsv::wallet::types::Counterparty) -> String {
343 match cpty.counterparty_type {
344 bsv::wallet::types::CounterpartyType::Self_ => "self".to_string(),
345 bsv::wallet::types::CounterpartyType::Anyone => "anyone".to_string(),
346 bsv::wallet::types::CounterpartyType::Other => cpty
347 .public_key
348 .as_ref()
349 .map(|pk| pk.to_der_hex())
350 .unwrap_or_default(),
351 bsv::wallet::types::CounterpartyType::Uninitialized => String::new(),
352 }
353}
354
355fn orig(originator: Option<&str>) -> &str {
357 originator.unwrap_or("")
358}
359
360#[async_trait]
361impl WalletInterface for WalletPermissionsManager {
362 async fn is_authenticated(
367 &self,
368 originator: Option<&str>,
369 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
370 self.inner.is_authenticated(originator).await
371 }
372
373 async fn wait_for_authentication(
374 &self,
375 originator: Option<&str>,
376 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
377 self.inner.wait_for_authentication(originator).await
378 }
379
380 async fn get_height(
381 &self,
382 originator: Option<&str>,
383 ) -> Result<GetHeightResult, bsv::wallet::error::WalletError> {
384 self.inner.get_height(originator).await
385 }
386
387 async fn get_header_for_height(
388 &self,
389 args: GetHeaderArgs,
390 originator: Option<&str>,
391 ) -> Result<GetHeaderResult, bsv::wallet::error::WalletError> {
392 self.inner.get_header_for_height(args, originator).await
393 }
394
395 async fn get_network(
396 &self,
397 originator: Option<&str>,
398 ) -> Result<GetNetworkResult, bsv::wallet::error::WalletError> {
399 self.inner.get_network(originator).await
400 }
401
402 async fn get_version(
403 &self,
404 originator: Option<&str>,
405 ) -> Result<GetVersionResult, bsv::wallet::error::WalletError> {
406 self.inner.get_version(originator).await
407 }
408
409 async fn create_action(
416 &self,
417 mut args: CreateActionArgs,
418 originator: Option<&str>,
419 ) -> Result<CreateActionResult, bsv::wallet::error::WalletError> {
420 let o = orig(originator);
421
422 for output in &args.outputs {
424 if let Some(ref basket) = output.basket {
425 ensure::ensure_basket_access(self, o, basket, "insertion").await?;
426 }
427 }
428
429 let total_output_sats: u64 = args.outputs.iter().map(|o| o.satoshis).sum();
431 if total_output_sats > 0 {
432 ensure::ensure_spending_authorization(self, o, total_output_sats).await?;
433 }
434
435 if self.config.encrypt_wallet_metadata {
437 args.description = metadata::encrypt_action_metadata(
438 self.inner.as_ref(),
439 &args.description,
440 &self.admin_originator,
441 )
442 .await?;
443 }
444
445 self.inner.create_action(args, originator).await
446 }
447
448 async fn sign_action(
450 &self,
451 args: SignActionArgs,
452 originator: Option<&str>,
453 ) -> Result<SignActionResult, bsv::wallet::error::WalletError> {
454 self.inner.sign_action(args, originator).await
455 }
456
457 async fn abort_action(
459 &self,
460 args: AbortActionArgs,
461 originator: Option<&str>,
462 ) -> Result<AbortActionResult, bsv::wallet::error::WalletError> {
463 self.inner.abort_action(args, originator).await
464 }
465
466 async fn list_actions(
468 &self,
469 args: ListActionsArgs,
470 originator: Option<&str>,
471 ) -> Result<ListActionsResult, bsv::wallet::error::WalletError> {
472 let o = orig(originator);
473
474 if !args.labels.is_empty() {
476 ensure::ensure_label_access(self, o, &args.labels, "list").await?;
477 }
478
479 let mut result = self.inner.list_actions(args, originator).await?;
480
481 if self.config.encrypt_wallet_metadata {
483 for action in &mut result.actions {
484 action.description = metadata::decrypt_action_metadata(
485 self.inner.as_ref(),
486 &action.description,
487 &self.admin_originator,
488 )
489 .await?;
490 }
491 }
492
493 Ok(result)
494 }
495
496 async fn internalize_action(
498 &self,
499 args: InternalizeActionArgs,
500 originator: Option<&str>,
501 ) -> Result<InternalizeActionResult, bsv::wallet::error::WalletError> {
502 self.inner.internalize_action(args, originator).await
507 }
508
509 async fn list_outputs(
515 &self,
516 args: ListOutputsArgs,
517 originator: Option<&str>,
518 ) -> Result<ListOutputsResult, bsv::wallet::error::WalletError> {
519 let o = orig(originator);
520 ensure::ensure_basket_access(self, o, &args.basket, "listing").await?;
521
522 let mut result = self.inner.list_outputs(args, originator).await?;
523
524 if self.config.encrypt_wallet_metadata {
526 for output in &mut result.outputs {
527 if let Some(ref instructions) = output.custom_instructions {
528 output.custom_instructions = Some(
529 metadata::decrypt_action_metadata(
530 self.inner.as_ref(),
531 instructions,
532 &self.admin_originator,
533 )
534 .await?,
535 );
536 }
537 }
538 }
539
540 Ok(result)
541 }
542
543 async fn relinquish_output(
545 &self,
546 args: RelinquishOutputArgs,
547 originator: Option<&str>,
548 ) -> Result<RelinquishOutputResult, bsv::wallet::error::WalletError> {
549 let o = orig(originator);
550 ensure::ensure_basket_access(self, o, &args.basket, "relinquishing").await?;
551 self.inner.relinquish_output(args, originator).await
552 }
553
554 async fn get_public_key(
560 &self,
561 args: GetPublicKeyArgs,
562 originator: Option<&str>,
563 ) -> Result<GetPublicKeyResult, bsv::wallet::error::WalletError> {
564 let o = orig(originator);
565 if let Some(ref proto) = args.protocol_id {
566 let (name, sec) = protocol_info(proto);
567 let cpty = args
568 .counterparty
569 .as_ref()
570 .map(|c| counterparty_str(c))
571 .unwrap_or_default();
572 ensure::ensure_protocol_permission(
573 self,
574 o,
575 &name,
576 sec,
577 &cpty,
578 args.privileged,
579 "publicKey",
580 )
581 .await?;
582 }
583 self.inner.get_public_key(args, originator).await
584 }
585
586 async fn reveal_counterparty_key_linkage(
590 &self,
591 args: RevealCounterpartyKeyLinkageArgs,
592 originator: Option<&str>,
593 ) -> Result<RevealCounterpartyKeyLinkageResult, bsv::wallet::error::WalletError> {
594 let o = orig(originator);
595 let cpty = args.counterparty.to_der_hex();
596 let privileged = args.privileged.unwrap_or(false);
597 ensure::ensure_protocol_permission(
598 self,
599 o,
600 "key linkage revelation",
601 2,
602 &cpty,
603 privileged,
604 "keyLinkage",
605 )
606 .await?;
607 self.inner
608 .reveal_counterparty_key_linkage(args, originator)
609 .await
610 }
611
612 async fn reveal_specific_key_linkage(
614 &self,
615 args: RevealSpecificKeyLinkageArgs,
616 originator: Option<&str>,
617 ) -> Result<RevealSpecificKeyLinkageResult, bsv::wallet::error::WalletError> {
618 let o = orig(originator);
619 let (name, sec) = protocol_info(&args.protocol_id);
620 let cpty = counterparty_str(&args.counterparty);
621 let privileged = args.privileged.unwrap_or(false);
622 ensure::ensure_protocol_permission(self, o, &name, sec, &cpty, privileged, "keyLinkage")
623 .await?;
624 self.inner
625 .reveal_specific_key_linkage(args, originator)
626 .await
627 }
628
629 async fn encrypt(
631 &self,
632 args: EncryptArgs,
633 originator: Option<&str>,
634 ) -> Result<EncryptResult, bsv::wallet::error::WalletError> {
635 let o = orig(originator);
636 let (name, sec) = protocol_info(&args.protocol_id);
637 let cpty = counterparty_str(&args.counterparty);
638 ensure::ensure_protocol_permission(
639 self,
640 o,
641 &name,
642 sec,
643 &cpty,
644 args.privileged,
645 "encrypting",
646 )
647 .await?;
648 self.inner.encrypt(args, originator).await
649 }
650
651 async fn decrypt(
653 &self,
654 args: DecryptArgs,
655 originator: Option<&str>,
656 ) -> Result<DecryptResult, bsv::wallet::error::WalletError> {
657 let o = orig(originator);
658 let (name, sec) = protocol_info(&args.protocol_id);
659 let cpty = counterparty_str(&args.counterparty);
660 ensure::ensure_protocol_permission(
661 self,
662 o,
663 &name,
664 sec,
665 &cpty,
666 args.privileged,
667 "decryption",
668 )
669 .await?;
670 self.inner.decrypt(args, originator).await
671 }
672
673 async fn create_hmac(
675 &self,
676 args: CreateHmacArgs,
677 originator: Option<&str>,
678 ) -> Result<CreateHmacResult, bsv::wallet::error::WalletError> {
679 let o = orig(originator);
680 let (name, sec) = protocol_info(&args.protocol_id);
681 let cpty = counterparty_str(&args.counterparty);
682 ensure::ensure_protocol_permission(self, o, &name, sec, &cpty, args.privileged, "hmac")
683 .await?;
684 self.inner.create_hmac(args, originator).await
685 }
686
687 async fn verify_hmac(
689 &self,
690 args: VerifyHmacArgs,
691 originator: Option<&str>,
692 ) -> Result<VerifyHmacResult, bsv::wallet::error::WalletError> {
693 let o = orig(originator);
694 let (name, sec) = protocol_info(&args.protocol_id);
695 let cpty = counterparty_str(&args.counterparty);
696 ensure::ensure_protocol_permission(
697 self,
698 o,
699 &name,
700 sec,
701 &cpty,
702 args.privileged,
703 "hmacVerification",
704 )
705 .await?;
706 self.inner.verify_hmac(args, originator).await
707 }
708
709 async fn create_signature(
711 &self,
712 args: CreateSignatureArgs,
713 originator: Option<&str>,
714 ) -> Result<CreateSignatureResult, bsv::wallet::error::WalletError> {
715 let o = orig(originator);
716 let (name, sec) = protocol_info(&args.protocol_id);
717 let cpty = counterparty_str(&args.counterparty);
718 ensure::ensure_protocol_permission(self, o, &name, sec, &cpty, args.privileged, "signing")
719 .await?;
720 self.inner.create_signature(args, originator).await
721 }
722
723 async fn verify_signature(
725 &self,
726 args: VerifySignatureArgs,
727 originator: Option<&str>,
728 ) -> Result<VerifySignatureResult, bsv::wallet::error::WalletError> {
729 let o = orig(originator);
730 let (name, sec) = protocol_info(&args.protocol_id);
731 let cpty = counterparty_str(&args.counterparty);
732 ensure::ensure_protocol_permission(
733 self,
734 o,
735 &name,
736 sec,
737 &cpty,
738 args.privileged,
739 "signatureVerification",
740 )
741 .await?;
742 self.inner.verify_signature(args, originator).await
743 }
744
745 async fn acquire_certificate(
755 &self,
756 args: AcquireCertificateArgs,
757 originator: Option<&str>,
758 ) -> Result<Certificate, bsv::wallet::error::WalletError> {
759 let o = orig(originator);
760 if self.config.require_certificate_access_for_acquisition {
761 let cert_type_str = BASE64_STANDARD.encode(args.cert_type.0);
762 ensure::ensure_protocol_permission(
763 self,
764 o,
765 &format!("certificate acquisition {}", cert_type_str),
766 1,
767 "self",
768 args.privileged,
769 "generic",
770 )
771 .await?;
772 }
773 self.inner.acquire_certificate(args, originator).await
774 }
775
776 async fn list_certificates(
781 &self,
782 args: ListCertificatesArgs,
783 originator: Option<&str>,
784 ) -> Result<ListCertificatesResult, bsv::wallet::error::WalletError> {
785 let o = orig(originator);
786 if self.config.require_certificate_access_for_listing {
787 ensure::ensure_protocol_permission(
788 self,
789 o,
790 "certificate list",
791 1,
792 "self",
793 *args.privileged,
794 "generic",
795 )
796 .await?;
797 }
798 self.inner.list_certificates(args, originator).await
799 }
800
801 async fn prove_certificate(
806 &self,
807 args: ProveCertificateArgs,
808 originator: Option<&str>,
809 ) -> Result<ProveCertificateResult, bsv::wallet::error::WalletError> {
810 let o = orig(originator);
811 let cert_type_str = args
812 .certificate
813 .cert_type
814 .as_ref()
815 .map(|ct| BASE64_STANDARD.encode(ct.0))
816 .unwrap_or_default();
817 let verifier_hex = args.verifier.to_der_hex();
818 ensure::ensure_certificate_access(
819 self,
820 o,
821 &cert_type_str,
822 Some(&args.fields_to_reveal),
823 Some(&verifier_hex),
824 *args.privileged,
825 "disclosure",
826 )
827 .await?;
828 self.inner.prove_certificate(args, originator).await
829 }
830
831 async fn relinquish_certificate(
837 &self,
838 args: RelinquishCertificateArgs,
839 originator: Option<&str>,
840 ) -> Result<RelinquishCertificateResult, bsv::wallet::error::WalletError> {
841 let o = orig(originator);
842 if self.config.require_certificate_access_for_relinquishing {
843 let cert_type_str = BASE64_STANDARD.encode(args.cert_type.0);
844 ensure::ensure_protocol_permission(
845 self,
846 o,
847 &format!("certificate relinquishment {}", cert_type_str),
848 1,
849 "self",
850 false,
851 "generic",
852 )
853 .await?;
854 }
855 self.inner.relinquish_certificate(args, originator).await
856 }
857
858 async fn discover_by_identity_key(
865 &self,
866 args: DiscoverByIdentityKeyArgs,
867 originator: Option<&str>,
868 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
869 if self.config.require_certificate_access_for_discovery {
870 let o = orig(originator);
871 ensure::ensure_certificate_access(self, o, "*", None, None, false, "discovery").await?;
872 }
873 self.inner.discover_by_identity_key(args, originator).await
874 }
875
876 async fn discover_by_attributes(
878 &self,
879 args: DiscoverByAttributesArgs,
880 originator: Option<&str>,
881 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
882 if self.config.require_certificate_access_for_discovery {
883 let o = orig(originator);
884 ensure::ensure_certificate_access(self, o, "*", None, None, false, "discovery").await?;
885 }
886 self.inner.discover_by_attributes(args, originator).await
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use super::*;
893 use crate::WalletError;
894
895 struct MockWalletInterface;
902
903 #[async_trait]
904 impl WalletInterface for MockWalletInterface {
905 async fn is_authenticated(
906 &self,
907 _originator: Option<&str>,
908 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
909 Ok(AuthenticatedResult {
910 authenticated: true,
911 })
912 }
913
914 async fn wait_for_authentication(
915 &self,
916 _originator: Option<&str>,
917 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
918 Err(bsv::wallet::error::WalletError::NotImplemented(
919 "wait_for_authentication".into(),
920 ))
921 }
922
923 async fn get_height(
924 &self,
925 _originator: Option<&str>,
926 ) -> Result<GetHeightResult, bsv::wallet::error::WalletError> {
927 Err(bsv::wallet::error::WalletError::NotImplemented(
928 "get_height".into(),
929 ))
930 }
931
932 async fn get_header_for_height(
933 &self,
934 _args: GetHeaderArgs,
935 _originator: Option<&str>,
936 ) -> Result<GetHeaderResult, bsv::wallet::error::WalletError> {
937 Err(bsv::wallet::error::WalletError::NotImplemented(
938 "get_header_for_height".into(),
939 ))
940 }
941
942 async fn get_network(
943 &self,
944 _originator: Option<&str>,
945 ) -> Result<GetNetworkResult, bsv::wallet::error::WalletError> {
946 Err(bsv::wallet::error::WalletError::NotImplemented(
947 "get_network".into(),
948 ))
949 }
950
951 async fn get_version(
952 &self,
953 _originator: Option<&str>,
954 ) -> Result<GetVersionResult, bsv::wallet::error::WalletError> {
955 Err(bsv::wallet::error::WalletError::NotImplemented(
956 "get_version".into(),
957 ))
958 }
959
960 async fn create_action(
961 &self,
962 _args: CreateActionArgs,
963 _originator: Option<&str>,
964 ) -> Result<CreateActionResult, bsv::wallet::error::WalletError> {
965 Err(bsv::wallet::error::WalletError::NotImplemented(
966 "create_action".into(),
967 ))
968 }
969
970 async fn sign_action(
971 &self,
972 _args: SignActionArgs,
973 _originator: Option<&str>,
974 ) -> Result<SignActionResult, bsv::wallet::error::WalletError> {
975 Err(bsv::wallet::error::WalletError::NotImplemented(
976 "sign_action".into(),
977 ))
978 }
979
980 async fn abort_action(
981 &self,
982 _args: AbortActionArgs,
983 _originator: Option<&str>,
984 ) -> Result<AbortActionResult, bsv::wallet::error::WalletError> {
985 Err(bsv::wallet::error::WalletError::NotImplemented(
986 "abort_action".into(),
987 ))
988 }
989
990 async fn list_actions(
991 &self,
992 _args: ListActionsArgs,
993 _originator: Option<&str>,
994 ) -> Result<ListActionsResult, bsv::wallet::error::WalletError> {
995 Err(bsv::wallet::error::WalletError::NotImplemented(
996 "list_actions".into(),
997 ))
998 }
999
1000 async fn internalize_action(
1001 &self,
1002 _args: InternalizeActionArgs,
1003 _originator: Option<&str>,
1004 ) -> Result<InternalizeActionResult, bsv::wallet::error::WalletError> {
1005 Err(bsv::wallet::error::WalletError::NotImplemented(
1006 "internalize_action".into(),
1007 ))
1008 }
1009
1010 async fn list_outputs(
1011 &self,
1012 _args: ListOutputsArgs,
1013 _originator: Option<&str>,
1014 ) -> Result<ListOutputsResult, bsv::wallet::error::WalletError> {
1015 Err(bsv::wallet::error::WalletError::NotImplemented(
1016 "list_outputs".into(),
1017 ))
1018 }
1019
1020 async fn relinquish_output(
1021 &self,
1022 _args: RelinquishOutputArgs,
1023 _originator: Option<&str>,
1024 ) -> Result<RelinquishOutputResult, bsv::wallet::error::WalletError> {
1025 Err(bsv::wallet::error::WalletError::NotImplemented(
1026 "relinquish_output".into(),
1027 ))
1028 }
1029
1030 async fn get_public_key(
1031 &self,
1032 _args: GetPublicKeyArgs,
1033 _originator: Option<&str>,
1034 ) -> Result<GetPublicKeyResult, bsv::wallet::error::WalletError> {
1035 Err(bsv::wallet::error::WalletError::NotImplemented(
1036 "get_public_key".into(),
1037 ))
1038 }
1039
1040 async fn reveal_counterparty_key_linkage(
1041 &self,
1042 _args: RevealCounterpartyKeyLinkageArgs,
1043 _originator: Option<&str>,
1044 ) -> Result<RevealCounterpartyKeyLinkageResult, bsv::wallet::error::WalletError> {
1045 Err(bsv::wallet::error::WalletError::NotImplemented(
1046 "reveal_counterparty_key_linkage".into(),
1047 ))
1048 }
1049
1050 async fn reveal_specific_key_linkage(
1051 &self,
1052 _args: RevealSpecificKeyLinkageArgs,
1053 _originator: Option<&str>,
1054 ) -> Result<RevealSpecificKeyLinkageResult, bsv::wallet::error::WalletError> {
1055 Err(bsv::wallet::error::WalletError::NotImplemented(
1056 "reveal_specific_key_linkage".into(),
1057 ))
1058 }
1059
1060 async fn encrypt(
1061 &self,
1062 _args: EncryptArgs,
1063 _originator: Option<&str>,
1064 ) -> Result<EncryptResult, bsv::wallet::error::WalletError> {
1065 Err(bsv::wallet::error::WalletError::NotImplemented(
1066 "encrypt".into(),
1067 ))
1068 }
1069
1070 async fn decrypt(
1071 &self,
1072 _args: DecryptArgs,
1073 _originator: Option<&str>,
1074 ) -> Result<DecryptResult, bsv::wallet::error::WalletError> {
1075 Err(bsv::wallet::error::WalletError::NotImplemented(
1076 "decrypt".into(),
1077 ))
1078 }
1079
1080 async fn create_hmac(
1081 &self,
1082 _args: CreateHmacArgs,
1083 _originator: Option<&str>,
1084 ) -> Result<CreateHmacResult, bsv::wallet::error::WalletError> {
1085 Err(bsv::wallet::error::WalletError::NotImplemented(
1086 "create_hmac".into(),
1087 ))
1088 }
1089
1090 async fn verify_hmac(
1091 &self,
1092 _args: VerifyHmacArgs,
1093 _originator: Option<&str>,
1094 ) -> Result<VerifyHmacResult, bsv::wallet::error::WalletError> {
1095 Err(bsv::wallet::error::WalletError::NotImplemented(
1096 "verify_hmac".into(),
1097 ))
1098 }
1099
1100 async fn create_signature(
1101 &self,
1102 _args: CreateSignatureArgs,
1103 _originator: Option<&str>,
1104 ) -> Result<CreateSignatureResult, bsv::wallet::error::WalletError> {
1105 Err(bsv::wallet::error::WalletError::NotImplemented(
1106 "create_signature".into(),
1107 ))
1108 }
1109
1110 async fn verify_signature(
1111 &self,
1112 _args: VerifySignatureArgs,
1113 _originator: Option<&str>,
1114 ) -> Result<VerifySignatureResult, bsv::wallet::error::WalletError> {
1115 Err(bsv::wallet::error::WalletError::NotImplemented(
1116 "verify_signature".into(),
1117 ))
1118 }
1119
1120 async fn acquire_certificate(
1121 &self,
1122 _args: AcquireCertificateArgs,
1123 _originator: Option<&str>,
1124 ) -> Result<Certificate, bsv::wallet::error::WalletError> {
1125 Err(bsv::wallet::error::WalletError::NotImplemented(
1126 "acquire_certificate".into(),
1127 ))
1128 }
1129
1130 async fn list_certificates(
1131 &self,
1132 _args: ListCertificatesArgs,
1133 _originator: Option<&str>,
1134 ) -> Result<ListCertificatesResult, bsv::wallet::error::WalletError> {
1135 Err(bsv::wallet::error::WalletError::NotImplemented(
1136 "list_certificates".into(),
1137 ))
1138 }
1139
1140 async fn prove_certificate(
1141 &self,
1142 _args: ProveCertificateArgs,
1143 _originator: Option<&str>,
1144 ) -> Result<ProveCertificateResult, bsv::wallet::error::WalletError> {
1145 Err(bsv::wallet::error::WalletError::NotImplemented(
1146 "prove_certificate".into(),
1147 ))
1148 }
1149
1150 async fn relinquish_certificate(
1151 &self,
1152 _args: RelinquishCertificateArgs,
1153 _originator: Option<&str>,
1154 ) -> Result<RelinquishCertificateResult, bsv::wallet::error::WalletError> {
1155 Err(bsv::wallet::error::WalletError::NotImplemented(
1156 "relinquish_certificate".into(),
1157 ))
1158 }
1159
1160 async fn discover_by_identity_key(
1161 &self,
1162 _args: DiscoverByIdentityKeyArgs,
1163 _originator: Option<&str>,
1164 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
1165 Err(bsv::wallet::error::WalletError::NotImplemented(
1166 "discover_by_identity_key".into(),
1167 ))
1168 }
1169
1170 async fn discover_by_attributes(
1171 &self,
1172 _args: DiscoverByAttributesArgs,
1173 _originator: Option<&str>,
1174 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
1175 Err(bsv::wallet::error::WalletError::NotImplemented(
1176 "discover_by_attributes".into(),
1177 ))
1178 }
1179 }
1180
1181 fn make_manager(admin: &str) -> WalletPermissionsManager {
1182 WalletPermissionsManager::new(
1183 Arc::new(MockWalletInterface),
1184 admin.to_string(),
1185 PermissionsManagerConfig::default(),
1186 )
1187 }
1188
1189 #[tokio::test]
1190 async fn test_admin_bypasses_all() {
1191 let mgr = make_manager("admin.example.com");
1192 let result = mgr.is_authenticated(Some("admin.example.com")).await;
1194 assert!(result.is_ok());
1195 let auth = result.unwrap();
1196 assert!(auth.authenticated);
1197 }
1198
1199 #[tokio::test]
1200 async fn test_non_admin_delegates() {
1201 let mgr = make_manager("admin.example.com");
1202 let result = mgr.is_authenticated(Some("other.example.com")).await;
1204 assert!(result.is_ok());
1205 let auth = result.unwrap();
1206 assert!(auth.authenticated);
1207 }
1208
1209 #[tokio::test]
1210 async fn test_p_module_registration() {
1211 let mut mgr = make_manager("admin.example.com");
1212 assert!(mgr.permission_modules.is_empty());
1213
1214 struct TestModule;
1216
1217 #[async_trait]
1218 impl PermissionsModule for TestModule {
1219 async fn on_request(
1220 &self,
1221 _request: &types::PermissionRequest,
1222 _originator: &str,
1223 ) -> Result<types::PermissionResponse, WalletError> {
1224 Ok(types::PermissionResponse::EphemeralGrant)
1225 }
1226
1227 async fn on_response(
1228 &self,
1229 _request: &types::PermissionRequest,
1230 result: serde_json::Value,
1231 _originator: &str,
1232 ) -> Result<serde_json::Value, WalletError> {
1233 Ok(result)
1234 }
1235 }
1236
1237 mgr.register_module("test-scheme".to_string(), Arc::new(TestModule));
1238 assert_eq!(mgr.permission_modules.len(), 1);
1239 assert!(mgr.permission_modules.contains_key("test-scheme"));
1240 assert!(mgr.has_permission_module("test-scheme"));
1241 assert!(!mgr.has_permission_module("other-scheme"));
1242 }
1243
1244 #[tokio::test]
1245 async fn test_admin_normalization() {
1246 let mgr = make_manager("https://admin.example.com:443");
1248 assert!(mgr.is_admin(Some("admin.example.com")));
1249 assert!(mgr.is_admin(Some("ADMIN.EXAMPLE.COM")));
1250 assert!(!mgr.is_admin(Some("other.com")));
1251 assert!(!mgr.is_admin(None));
1252 }
1253
1254 #[tokio::test]
1255 async fn test_admin_originator_accessor() {
1256 let mgr = make_manager("admin.example.com");
1257 assert_eq!(mgr.admin_originator(), "admin.example.com");
1258 }
1259
1260 #[tokio::test]
1261 async fn test_deny_permission_via_manager() {
1262 let mgr = make_manager("admin.example.com");
1263 let req = types::PermissionRequest {
1264 permission_type: types::PermissionType::ProtocolPermission,
1265 originator: "example.com".to_string(),
1266 privileged: None,
1267 protocol: Some("test".to_string()),
1268 security_level: None,
1269 counterparty: None,
1270 basket_name: None,
1271 cert_type: None,
1272 cert_fields: None,
1273 verifier: None,
1274 amount: None,
1275 description: None,
1276 labels: None,
1277 is_new_user: false,
1278 };
1279 let resp = mgr.deny_permission(&req);
1280 match resp {
1281 types::PermissionResponse::Deny { reason } => {
1282 assert!(reason.contains("Protocol permission denied"));
1283 }
1284 _ => panic!("Expected Deny response"),
1285 }
1286 }
1287}