gix_credentials/protocol/mod.rs
1use bstr::BString;
2
3use crate::helper;
4
5/// The outcome of the credentials top-level functions to obtain a complete identity.
6#[derive(Debug, Clone, Eq, PartialEq)]
7pub struct Outcome {
8 /// The identity provide by the helper.
9 pub identity: gix_sec::identity::Account,
10 /// A handle to the action to perform next in another call to [`helper::invoke()`][crate::helper::invoke()].
11 pub next: helper::NextAction,
12}
13
14/// The Result type used in credentials top-level functions to obtain a complete identity.
15pub type Result = std::result::Result<Option<Outcome>, Error>;
16
17/// The error returned top-level credential functions.
18#[derive(Debug, thiserror::Error)]
19#[allow(missing_docs)]
20pub enum Error {
21 #[error(transparent)]
22 UrlParse(#[from] gix_url::parse::Error),
23 #[error("The 'url' field must be set when performing a 'get/fill' action")]
24 UrlMissing,
25 #[error(transparent)]
26 ContextDecode(#[from] context::decode::Error),
27 #[error(transparent)]
28 InvokeHelper(#[from] helper::Error),
29 #[error("Could not obtain identity for context: {}", { let mut buf = Vec::<u8>::new(); context.write_to(&mut buf).ok(); String::from_utf8_lossy(&buf).into_owned() })]
30 IdentityMissing { context: Context },
31 #[error("The handler asked to stop trying to obtain credentials")]
32 Quit,
33 #[error("Couldn't obtain {prompt}")]
34 Prompt { prompt: String, source: gix_prompt::Error },
35}
36
37/// Additional context to be passed to the credentials helper.
38#[derive(Debug, Default, Clone, Eq, PartialEq)]
39pub struct Context {
40 /// The protocol over which the credential will be used (e.g., https).
41 pub protocol: Option<String>,
42 /// The remote hostname for a network credential. This includes the port number if one was specified (e.g., "example.com:8088").
43 pub host: Option<String>,
44 /// The path with which the credential will be used. E.g., for accessing a remote https repository, this will be the repository’s path on the server.
45 /// It can also be a path on the file system.
46 pub path: Option<BString>,
47 /// The credential’s username, if we already have one (e.g., from a URL, the configuration, the user, or from a previously run helper).
48 pub username: Option<String>,
49 /// The credential’s password, if we are asking it to be stored.
50 pub password: Option<String>,
51 /// An OAuth refresh token that may accompany a password. It is to be treated confidentially, just like the password.
52 pub oauth_refresh_token: Option<String>,
53 /// The expiry date of OAuth tokens as seconds from Unix epoch.
54 pub password_expiry_utc: Option<gix_date::SecondsSinceUnixEpoch>,
55 /// When this special attribute is read by git credential, the value is parsed as a URL and treated as if its constituent
56 /// parts were read (e.g., url=<https://example.com> would behave as if
57 /// protocol=https and host=example.com had been provided). This can help callers avoid parsing URLs themselves.
58 pub url: Option<BString>,
59 /// If true, the caller should stop asking for credentials immediately without calling more credential helpers in the chain.
60 pub quit: Option<bool>,
61}
62
63/// Convert the outcome of a helper invocation to a helper result, assuring that the identity is complete in the process.
64#[allow(clippy::result_large_err)]
65pub fn helper_outcome_to_result(outcome: Option<helper::Outcome>, action: helper::Action) -> Result {
66 match (action, outcome) {
67 (helper::Action::Get(ctx), None) => Err(Error::IdentityMissing {
68 context: ctx.redacted(),
69 }),
70 (helper::Action::Get(ctx), Some(mut outcome)) => match outcome.consume_identity() {
71 Some(identity) => Ok(Some(Outcome {
72 identity,
73 next: outcome.next,
74 })),
75 None => Err(if outcome.quit {
76 Error::Quit
77 } else {
78 Error::IdentityMissing {
79 context: ctx.redacted(),
80 }
81 }),
82 },
83 (helper::Action::Store(_) | helper::Action::Erase(_), _ignore) => Ok(None),
84 }
85}
86
87///
88pub mod context;