@@ -7,15 +7,18 @@ use std::fmt::{Debug, Display};
77use std:: sync:: Arc ;
88
99use serde:: de:: DeserializeOwned ;
10+ use serde:: Serialize ;
1011use state:: TypeMap ;
1112
12- use tauri_utils:: acl:: capability:: CapabilityFile ;
13- use tauri_utils:: acl:: manifest:: Manifest ;
13+ use tauri_utils:: acl:: {
14+ capability:: { Capability , CapabilityFile , PermissionEntry } ,
15+ manifest:: Manifest ,
16+ Value , APP_ACL_KEY ,
17+ } ;
1418use tauri_utils:: acl:: {
1519 resolved:: { CommandKey , Resolved , ResolvedCommand , ResolvedScope , ScopeKey } ,
16- ExecutionContext ,
20+ ExecutionContext , Scopes ,
1721} ;
18- use tauri_utils:: acl:: { Value , APP_ACL_KEY } ;
1922
2023use crate :: { ipc:: InvokeError , sealed:: ManagerBase , Runtime } ;
2124use crate :: { AppHandle , Manager } ;
@@ -62,6 +65,140 @@ impl Origin {
6265 }
6366}
6467
68+ /// A capability that can be added at runtime.
69+ pub trait RuntimeCapability {
70+ /// Creates the capability file.
71+ fn build ( self ) -> CapabilityFile ;
72+ }
73+
74+ impl < T : AsRef < str > > RuntimeCapability for T {
75+ fn build ( self ) -> CapabilityFile {
76+ self . as_ref ( ) . parse ( ) . expect ( "invalid capability" )
77+ }
78+ }
79+
80+ /// A builder for a [`Capability`].
81+ pub struct CapabilityBuilder ( Capability ) ;
82+
83+ impl CapabilityBuilder {
84+ /// Creates a new capability builder with a unique identifier.
85+ pub fn new ( identifier : impl Into < String > ) -> Self {
86+ Self ( Capability {
87+ identifier : identifier. into ( ) ,
88+ description : "" . into ( ) ,
89+ remote : None ,
90+ local : true ,
91+ windows : Vec :: new ( ) ,
92+ webviews : Vec :: new ( ) ,
93+ permissions : Vec :: new ( ) ,
94+ platforms : Vec :: new ( ) ,
95+ } )
96+ }
97+
98+ /// Allows this capability to be used by a remote URL.
99+ pub fn remote ( mut self , url : String ) -> Self {
100+ self
101+ . 0
102+ . remote
103+ . get_or_insert_with ( Default :: default)
104+ . urls
105+ . push ( url) ;
106+ self
107+ }
108+
109+ /// Whether this capability is applied on local app URLs or not. Defaults to `true`.
110+ pub fn local ( mut self , local : bool ) -> Self {
111+ self . 0 . local = local;
112+ self
113+ }
114+
115+ /// Link this capability to the given window label.
116+ pub fn window ( mut self , window : impl Into < String > ) -> Self {
117+ self . 0 . windows . push ( window. into ( ) ) ;
118+ self
119+ }
120+
121+ /// Link this capability to the a list of window labels.
122+ pub fn windows ( mut self , windows : impl IntoIterator < Item = impl Into < String > > ) -> Self {
123+ self . 0 . windows . extend ( windows. into_iter ( ) . map ( |w| w. into ( ) ) ) ;
124+ self
125+ }
126+
127+ /// Link this capability to the given webview label.
128+ pub fn webview ( mut self , webview : impl Into < String > ) -> Self {
129+ self . 0 . webviews . push ( webview. into ( ) ) ;
130+ self
131+ }
132+
133+ /// Link this capability to the a list of window labels.
134+ pub fn webviews ( mut self , webviews : impl IntoIterator < Item = impl Into < String > > ) -> Self {
135+ self
136+ . 0
137+ . webviews
138+ . extend ( webviews. into_iter ( ) . map ( |w| w. into ( ) ) ) ;
139+ self
140+ }
141+
142+ /// Add a new permission to this capability.
143+ pub fn permission ( mut self , permission : impl Into < String > ) -> Self {
144+ let permission = permission. into ( ) ;
145+ self . 0 . permissions . push ( PermissionEntry :: PermissionRef (
146+ permission
147+ . clone ( )
148+ . try_into ( )
149+ . unwrap_or_else ( |_| panic ! ( "invalid permission identifier '{permission}'" ) ) ,
150+ ) ) ;
151+ self
152+ }
153+
154+ /// Add a new scoped permission to this capability.
155+ pub fn permission_scoped < T : Serialize > (
156+ mut self ,
157+ permission : impl Into < String > ,
158+ allowed : Vec < T > ,
159+ denied : Vec < T > ,
160+ ) -> Self {
161+ let permission = permission. into ( ) ;
162+ let identifier = permission
163+ . clone ( )
164+ . try_into ( )
165+ . unwrap_or_else ( |_| panic ! ( "invalid permission identifier '{permission}'" ) ) ;
166+
167+ let allowed_scope = allowed
168+ . into_iter ( )
169+ . map ( |a| {
170+ serde_json:: to_value ( a)
171+ . expect ( "failed to serialize scope" )
172+ . into ( )
173+ } )
174+ . collect ( ) ;
175+ let denied_scope = denied
176+ . into_iter ( )
177+ . map ( |a| {
178+ serde_json:: to_value ( a)
179+ . expect ( "failed to serialize scope" )
180+ . into ( )
181+ } )
182+ . collect ( ) ;
183+ let scope = Scopes {
184+ allow : Some ( allowed_scope) ,
185+ deny : Some ( denied_scope) ,
186+ } ;
187+
188+ self
189+ . 0
190+ . permissions
191+ . push ( PermissionEntry :: ExtendedPermission { identifier, scope } ) ;
192+ self
193+ }
194+ }
195+
196+ impl RuntimeCapability for CapabilityBuilder {
197+ fn build ( self ) -> CapabilityFile {
198+ CapabilityFile :: Capability ( self . 0 )
199+ }
200+ }
201+
65202impl RuntimeAuthority {
66203 #[ doc( hidden) ]
67204 pub fn new ( acl : BTreeMap < String , Manifest > , resolved_acl : Resolved ) -> Self {
@@ -102,9 +239,9 @@ impl RuntimeAuthority {
102239 }
103240
104241 /// Adds the given capability to the runtime authority.
105- pub fn add_capability ( & mut self , capability : CapabilityFile ) -> crate :: Result < ( ) > {
242+ pub fn add_capability ( & mut self , capability : impl RuntimeCapability ) -> crate :: Result < ( ) > {
106243 let mut capabilities = BTreeMap :: new ( ) ;
107- match capability {
244+ match capability. build ( ) {
108245 CapabilityFile :: Capability ( c) => {
109246 capabilities. insert ( c. identifier . clone ( ) , c) ;
110247 }
0 commit comments