]>
Commit | Line | Data |
---|---|---|
add651ee FG |
1 | //! Cargo registry macos keychain credential process. |
2 | ||
4b012472 FG |
3 | #![allow(clippy::print_stderr)] |
4 | ||
add651ee FG |
5 | #[cfg(target_os = "macos")] |
6 | mod macos { | |
7 | use cargo_credential::{ | |
8 | read_token, Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo, | |
9 | }; | |
10 | use security_framework::os::macos::keychain::SecKeychain; | |
11 | ||
12 | pub struct MacKeychain; | |
13 | ||
14 | /// The account name is not used. | |
15 | const ACCOUNT: &'static str = ""; | |
16 | const NOT_FOUND: i32 = -25300; // errSecItemNotFound | |
17 | ||
18 | fn registry(index_url: &str) -> String { | |
19 | format!("cargo-registry:{}", index_url) | |
20 | } | |
21 | ||
22 | impl Credential for MacKeychain { | |
23 | fn perform( | |
24 | &self, | |
25 | reg: &RegistryInfo<'_>, | |
26 | action: &Action<'_>, | |
27 | _args: &[&str], | |
28 | ) -> Result<CredentialResponse, Error> { | |
29 | let keychain = SecKeychain::default().unwrap(); | |
30 | let service_name = registry(reg.index_url); | |
31 | let not_found = security_framework::base::Error::from(NOT_FOUND).code(); | |
32 | match action { | |
33 | Action::Get(_) => match keychain.find_generic_password(&service_name, ACCOUNT) { | |
34 | Err(e) if e.code() == not_found => Err(Error::NotFound), | |
35 | Err(e) => Err(Box::new(e).into()), | |
36 | Ok((pass, _)) => { | |
37 | let token = String::from_utf8(pass.as_ref().to_vec()).map_err(Box::new)?; | |
38 | Ok(CredentialResponse::Get { | |
39 | token: token.into(), | |
40 | cache: CacheControl::Session, | |
41 | operation_independent: true, | |
42 | }) | |
43 | } | |
44 | }, | |
45 | Action::Login(options) => { | |
46 | let token = read_token(options, reg)?; | |
47 | match keychain.find_generic_password(&service_name, ACCOUNT) { | |
48 | Err(e) => { | |
49 | if e.code() == not_found { | |
50 | keychain | |
51 | .add_generic_password( | |
52 | &service_name, | |
53 | ACCOUNT, | |
54 | token.expose().as_bytes(), | |
55 | ) | |
56 | .map_err(Box::new)?; | |
57 | } | |
58 | } | |
59 | Ok((_, mut item)) => { | |
60 | item.set_password(token.expose().as_bytes()) | |
61 | .map_err(Box::new)?; | |
62 | } | |
63 | } | |
64 | Ok(CredentialResponse::Login) | |
65 | } | |
66 | Action::Logout => match keychain.find_generic_password(&service_name, ACCOUNT) { | |
67 | Err(e) if e.code() == not_found => Err(Error::NotFound), | |
68 | Err(e) => Err(Box::new(e).into()), | |
69 | Ok((_, item)) => { | |
70 | item.delete(); | |
71 | Ok(CredentialResponse::Logout) | |
72 | } | |
73 | }, | |
74 | _ => Err(Error::OperationNotSupported), | |
75 | } | |
76 | } | |
77 | } | |
78 | } | |
79 | ||
80 | #[cfg(not(target_os = "macos"))] | |
81 | pub use cargo_credential::UnsupportedCredential as MacKeychain; | |
82 | #[cfg(target_os = "macos")] | |
83 | pub use macos::MacKeychain; |