@@ -28,19 +28,26 @@ This provides better compatibility with 3rd party clients that may already
2828have created items that match the entry, and reduces the chance
2929of ambiguity in later searches.
3030
31- ## Async runtime required
32-
33- While this crate uses the secret-service via its blocking API,
34- the secret-service crate is built on zbus which always talks to the dbus via async calls.
35- Thus, using the secret-service implies using an async runtime under the covers.
36- If you are already using an async runtime,
37- you can use keyring features to make sure that secret-service
38- uses a compatible runtime. But be careful: if you make keyring calls
39- on the main thread in this situation, you will likely crash because
40- you will block the main thread (see\
31+ ## keyring v1 incompatibility
32+
33+ In order to fix
34+ [this bug](https://github.com/hwchen/keyring-rs/issues/204)
35+ efficiently, this implementation can no longer access
36+ credentials that have no `target` attribute. Since keyring v1
37+ didn't set this attribute, any old credentials left from v1
38+ will have to be upgraded to a v3-compatible format
39+ using platform-specific code. You can use the new secret-service-specific
40+ entry creation call [new_with_no_target] to
41+ create an [Entry] that will retrieve a v1 password and/or delete it.
42+
43+ ## Tokio runtime caution
44+
45+ If you are using the `async-secret-service` with this crate,
46+ and specifying `tokio` as your runtime, be careful:
47+ if you make keyring calls on the main thread, you will likely deadlock (see\
4148[this issue on GitHub](https://github.com/hwchen/keyring-rs/issues/132)
42- for details). You will need to spawn a separate thread on which
43- you make your keyring calls so the main thread doesn't block .
49+ for details). You need to spawn a separate thread on which
50+ you make your keyring calls to avoid this .
4451
4552## Headless usage
4653
@@ -233,6 +240,26 @@ impl SsCredential {
233240 } )
234241 }
235242
243+ /// Create a credential that has *no* target and the given service and user.
244+ ///
245+ /// This emulates what keyring v1 did, and can be very handy when you need to
246+ /// access an old v1 credential that's in your secret service default collection.
247+ pub fn new_with_no_target ( service : & str , user : & str ) -> Result < Self > {
248+ let attributes = HashMap :: from ( [
249+ ( "service" . to_string ( ) , service. to_string ( ) ) ,
250+ ( "username" . to_string ( ) , user. to_string ( ) ) ,
251+ ( "application" . to_string ( ) , "rust-keyring" . to_string ( ) ) ,
252+ ] ) ;
253+ Ok ( Self {
254+ attributes,
255+ label : format ! (
256+ "keyring-rs v{} for no target, service '{service}', user '{user}'" ,
257+ env!( "CARGO_PKG_VERSION" ) ,
258+ ) ,
259+ target : None ,
260+ } )
261+ }
262+
236263 /// Create a credential from an underlying item.
237264 ///
238265 /// The created credential will have all the attributes and label
@@ -293,27 +320,24 @@ impl SsCredential {
293320 let ss = SecretService :: connect ( session_type) . map_err ( platform_failure) ?;
294321 let attributes: HashMap < & str , & str > = self . search_attributes ( ) . into_iter ( ) . collect ( ) ;
295322 let search = ss. search_items ( attributes) . map_err ( decode_error) ?;
296- let target = self . target . as_ref ( ) . ok_or_else ( empty_target) ?;
297- let unlocked = matching_target_items ( & search. unlocked , target) ?;
298- let locked = matching_target_items ( & search. locked , target) ?;
299323 if require_unique {
300- let count = locked. len ( ) + unlocked. len ( ) ;
324+ let count = search . locked . len ( ) + search . unlocked . len ( ) ;
301325 if count == 0 {
302326 return Err ( ErrorCode :: NoEntry ) ;
303327 } else if count > 1 {
304328 let mut creds: Vec < Box < Credential > > = vec ! [ ] ;
305- for item in locked. into_iter ( ) . chain ( unlocked. into_iter ( ) ) {
329+ for item in search . locked . iter ( ) . chain ( search . unlocked . iter ( ) ) {
306330 let cred = Self :: new_from_item ( item) ?;
307331 creds. push ( Box :: new ( cred) )
308332 }
309333 return Err ( ErrorCode :: Ambiguous ( creds) ) ;
310334 }
311335 }
312336 let mut results: Vec < T > = vec ! [ ] ;
313- for item in unlocked. into_iter ( ) {
337+ for item in search . unlocked . iter ( ) {
314338 results. push ( f ( item) ?) ;
315339 }
316- for item in locked. into_iter ( ) {
340+ for item in search . locked . iter ( ) {
317341 item. unlock ( ) . map_err ( decode_error) ?;
318342 results. push ( f ( item) ?) ;
319343 }
@@ -335,6 +359,9 @@ impl SsCredential {
335359 /// but this just selects the ones we search on
336360 fn search_attributes ( & self ) -> HashMap < & str , & str > {
337361 let mut result: HashMap < & str , & str > = HashMap :: new ( ) ;
362+ if self . target . is_some ( ) {
363+ result. insert ( "target" , self . attributes [ "target" ] . as_str ( ) ) ;
364+ }
338365 result. insert ( "service" , self . attributes [ "service" ] . as_str ( ) ) ;
339366 result. insert ( "username" , self . attributes [ "username" ] . as_str ( ) ) ;
340367 result
@@ -429,25 +456,6 @@ pub fn delete_item(item: &Item) -> Result<()> {
429456 item. delete ( ) . map_err ( decode_error)
430457}
431458
432- /// Given a slice of items, filter out the ones that have an explicit target
433- /// attribute that doesn't match the given target.
434- ///
435- /// References to the matching items are returned in a new vector.
436- pub fn matching_target_items < ' a > (
437- source : & ' a [ Item < ' a > ] ,
438- target : & str ,
439- ) -> Result < Vec < & ' a Item < ' a > > > {
440- let mut result: Vec < & ' a Item < ' a > > = vec ! [ ] ;
441- for i in source. iter ( ) {
442- match i. get_attributes ( ) . map_err ( decode_error) ?. get ( "target" ) {
443- None => result. push ( i) ,
444- Some ( item_target) if target. eq ( item_target) => result. push ( i) ,
445- _ => { }
446- }
447- }
448- Ok ( result)
449- }
450-
451459//
452460// Error utilities
453461//
0 commit comments