Skip to content

Commit 6c643c2

Browse files
committed
Always include the target when searching for credentials.
This fixes #204. This fix means that v1 keyring credentials in secret-service stores will no longer be found by v3. This has been documented in the module docs, but also needs to be added to the README. Also fixed the out-of-date documentation about the requirement for async runtimes.
1 parent 0b2ced7 commit 6c643c2

File tree

1 file changed

+46
-38
lines changed

1 file changed

+46
-38
lines changed

src/secret_service.rs

+46-38
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,26 @@ This provides better compatibility with 3rd party clients that may already
2828
have created items that match the entry, and reduces the chance
2929
of 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

Comments
 (0)