33// SPDX-License-Identifier: MIT
44
55use std:: {
6+ collections:: { HashMap , HashSet } ,
67 fmt,
78 path:: { Path , PathBuf } ,
89 sync:: { Arc , Mutex } ,
910} ;
1011
11- use glob:: Pattern ;
12+ pub use glob:: Pattern ;
1213use tauri_utils:: {
1314 config:: { Config , FsAllowlistScope } ,
1415 Env , PackageInfo ,
1516} ;
17+ use uuid:: Uuid ;
1618
1719use crate :: api:: path:: parse as parse_path;
1820
21+ /// Scope change event.
22+ #[ derive( Debug , Clone ) ]
23+ pub enum Event {
24+ /// A path has been allowed.
25+ PathAllowed ( PathBuf ) ,
26+ /// A path has been forbidden.
27+ PathForbidden ( PathBuf ) ,
28+ }
29+
30+ type EventListener = Box < dyn Fn ( & Event ) + Send > ;
31+
1932/// Scope for filesystem access.
2033#[ derive( Clone ) ]
2134pub struct Scope {
22- allow_patterns : Arc < Mutex < Vec < Pattern > > > ,
23- forbidden_patterns : Arc < Mutex < Vec < Pattern > > > ,
35+ alllowed_patterns : Arc < Mutex < HashSet < Pattern > > > ,
36+ forbidden_patterns : Arc < Mutex < HashSet < Pattern > > > ,
37+ event_listeners : Arc < Mutex < HashMap < Uuid , EventListener > > > ,
2438}
2539
2640impl fmt:: Debug for Scope {
2741 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
2842 f. debug_struct ( "Scope" )
2943 . field (
30- "allow_patterns " ,
44+ "alllowed_patterns " ,
3145 & self
32- . allow_patterns
46+ . alllowed_patterns
3347 . lock ( )
3448 . unwrap ( )
3549 . iter ( )
@@ -50,85 +64,123 @@ impl fmt::Debug for Scope {
5064 }
5165}
5266
53- fn push_pattern < P : AsRef < Path > > ( list : & mut Vec < Pattern > , pattern : P ) {
54- let pattern : PathBuf = pattern. as_ref ( ) . components ( ) . collect ( ) ;
55- list. push ( Pattern :: new ( & pattern . to_string_lossy ( ) ) . expect ( "invalid glob pattern" ) ) ;
67+ fn push_pattern < P : AsRef < Path > > ( list : & mut HashSet < Pattern > , pattern : P ) -> crate :: Result < ( ) > {
68+ let path : PathBuf = pattern. as_ref ( ) . components ( ) . collect ( ) ;
69+ list. insert ( Pattern :: new ( & path . to_string_lossy ( ) ) ? ) ;
5670 #[ cfg( windows) ]
5771 {
58- list
59- . push ( Pattern :: new ( & format ! ( "\\ \\ ?\\ {}" , pattern. display( ) ) ) . expect ( "invalid glob pattern" ) ) ;
72+ list. insert ( Pattern :: new ( & format ! ( "\\ \\ ?\\ {}" , path. display( ) ) ) ?) ;
6073 }
74+ Ok ( ( ) )
6175}
6276
6377impl Scope {
6478 /// Creates a new scope from a `FsAllowlistScope` configuration.
65- pub fn for_fs_api (
79+ pub ( crate ) fn for_fs_api (
6680 config : & Config ,
6781 package_info : & PackageInfo ,
6882 env : & Env ,
6983 scope : & FsAllowlistScope ,
70- ) -> Self {
71- let mut allow_patterns = Vec :: new ( ) ;
84+ ) -> crate :: Result < Self > {
85+ let mut alllowed_patterns = HashSet :: new ( ) ;
7286 for path in scope. allowed_paths ( ) {
7387 if let Ok ( path) = parse_path ( config, package_info, env, path) {
74- push_pattern ( & mut allow_patterns , path) ;
88+ push_pattern ( & mut alllowed_patterns , path) ? ;
7589 }
7690 }
7791
78- let mut forbidden_patterns = Vec :: new ( ) ;
92+ let mut forbidden_patterns = HashSet :: new ( ) ;
7993 if let Some ( forbidden_paths) = scope. forbidden_paths ( ) {
8094 for path in forbidden_paths {
8195 if let Ok ( path) = parse_path ( config, package_info, env, path) {
82- push_pattern ( & mut forbidden_patterns, path) ;
96+ push_pattern ( & mut forbidden_patterns, path) ? ;
8397 }
8498 }
8599 }
86100
87- Self {
88- allow_patterns : Arc :: new ( Mutex :: new ( allow_patterns ) ) ,
101+ Ok ( Self {
102+ alllowed_patterns : Arc :: new ( Mutex :: new ( alllowed_patterns ) ) ,
89103 forbidden_patterns : Arc :: new ( Mutex :: new ( forbidden_patterns) ) ,
104+ event_listeners : Default :: default ( ) ,
105+ } )
106+ }
107+
108+ /// The list of allowed patterns.
109+ pub fn allowed_patterns ( & self ) -> HashSet < Pattern > {
110+ self . alllowed_patterns . lock ( ) . unwrap ( ) . clone ( )
111+ }
112+
113+ /// The list of forbidden patterns.
114+ pub fn forbidden_patterns ( & self ) -> HashSet < Pattern > {
115+ self . forbidden_patterns . lock ( ) . unwrap ( ) . clone ( )
116+ }
117+
118+ /// Listen to an event on this scope.
119+ pub fn listen < F : Fn ( & Event ) + Send + ' static > ( & self , f : F ) -> Uuid {
120+ let id = Uuid :: new_v4 ( ) ;
121+ self . event_listeners . lock ( ) . unwrap ( ) . insert ( id, Box :: new ( f) ) ;
122+ id
123+ }
124+
125+ fn trigger ( & self , event : Event ) {
126+ for listener in self . event_listeners . lock ( ) . unwrap ( ) . values ( ) {
127+ listener ( & event) ;
90128 }
91129 }
92130
93131 /// Extend the allowed patterns with the given directory.
94132 ///
95133 /// After this function has been called, the frontend will be able to use the Tauri API to read
96134 /// the directory and all of its files and subdirectories.
97- pub fn allow_directory < P : AsRef < Path > > ( & self , path : P , recursive : bool ) {
135+ pub fn allow_directory < P : AsRef < Path > > ( & self , path : P , recursive : bool ) -> crate :: Result < ( ) > {
98136 let path = path. as_ref ( ) . to_path_buf ( ) ;
99- let mut list = self . allow_patterns . lock ( ) . unwrap ( ) ;
137+ {
138+ let mut list = self . alllowed_patterns . lock ( ) . unwrap ( ) ;
100139
101- // allow the directory to be read
102- push_pattern ( & mut list, & path) ;
103- // allow its files and subdirectories to be read
104- push_pattern ( & mut list, path. join ( if recursive { "**" } else { "*" } ) ) ;
140+ // allow the directory to be read
141+ push_pattern ( & mut list, & path) ?;
142+ // allow its files and subdirectories to be read
143+ push_pattern ( & mut list, path. join ( if recursive { "**" } else { "*" } ) ) ?;
144+ }
145+ self . trigger ( Event :: PathAllowed ( path) ) ;
146+ Ok ( ( ) )
105147 }
106148
107149 /// Extend the allowed patterns with the given file path.
108150 ///
109151 /// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
110- pub fn allow_file < P : AsRef < Path > > ( & self , path : P ) {
111- push_pattern ( & mut self . allow_patterns . lock ( ) . unwrap ( ) , path) ;
152+ pub fn allow_file < P : AsRef < Path > > ( & self , path : P ) -> crate :: Result < ( ) > {
153+ let path = path. as_ref ( ) ;
154+ push_pattern ( & mut self . alllowed_patterns . lock ( ) . unwrap ( ) , & path) ?;
155+ self . trigger ( Event :: PathAllowed ( path. to_path_buf ( ) ) ) ;
156+ Ok ( ( ) )
112157 }
113158
114159 /// Set the given directory path to be forbidden by this scope.
115160 ///
116161 /// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
117- pub fn forbid_directory < P : AsRef < Path > > ( & self , path : P , recursive : bool ) {
162+ pub fn forbid_directory < P : AsRef < Path > > ( & self , path : P , recursive : bool ) -> crate :: Result < ( ) > {
118163 let path = path. as_ref ( ) . to_path_buf ( ) ;
119- let mut list = self . forbidden_patterns . lock ( ) . unwrap ( ) ;
164+ {
165+ let mut list = self . forbidden_patterns . lock ( ) . unwrap ( ) ;
120166
121- // allow the directory to be read
122- push_pattern ( & mut list, & path) ;
123- // allow its files and subdirectories to be read
124- push_pattern ( & mut list, path. join ( if recursive { "**" } else { "*" } ) ) ;
167+ // allow the directory to be read
168+ push_pattern ( & mut list, & path) ?;
169+ // allow its files and subdirectories to be read
170+ push_pattern ( & mut list, path. join ( if recursive { "**" } else { "*" } ) ) ?;
171+ }
172+ self . trigger ( Event :: PathForbidden ( path) ) ;
173+ Ok ( ( ) )
125174 }
126175
127176 /// Set the given file path to be forbidden by this scope.
128177 ///
129178 /// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
130- pub fn forbid_file < P : AsRef < Path > > ( & self , path : P ) {
131- push_pattern ( & mut self . forbidden_patterns . lock ( ) . unwrap ( ) , path) ;
179+ pub fn forbid_file < P : AsRef < Path > > ( & self , path : P ) -> crate :: Result < ( ) > {
180+ let path = path. as_ref ( ) ;
181+ push_pattern ( & mut self . forbidden_patterns . lock ( ) . unwrap ( ) , & path) ?;
182+ self . trigger ( Event :: PathForbidden ( path. to_path_buf ( ) ) ) ;
183+ Ok ( ( ) )
132184 }
133185
134186 /// Determines if the given path is allowed on this scope.
@@ -154,7 +206,7 @@ impl Scope {
154206 false
155207 } else {
156208 let allowed = self
157- . allow_patterns
209+ . alllowed_patterns
158210 . lock ( )
159211 . unwrap ( )
160212 . iter ( )
0 commit comments