113 lines · 4.8 KB · Rust
RawDownload
use bstr::BString; use crate::helper; /// The outcome of the credentials top-level functions to obtain a complete identity.#[derive(Debug, Clone, Eq, PartialEq)]pub struct Outcome {    /// The identity provide by the helper.    pub identity: gix_sec::identity::Account,    /// A handle to the action to perform next in another call to [`helper::invoke()`][crate::helper::invoke()].    pub next: helper::NextAction,} /// The Result type used in credentials top-level functions to obtain a complete identity.pub type Result = std::result::Result<Option<Outcome>, Error>; /// The error returned top-level credential functions.#[derive(Debug, thiserror::Error)]#[expect(missing_docs)]pub enum Error {    #[error(transparent)]    UrlParse(#[from] gix_url::parse::Error),    #[error("Either 'url' field or both 'protocol' and 'host' fields must be provided")]    UrlMissing,    #[error(transparent)]    ContextDecode(#[from] context::decode::Error),    #[error(transparent)]    InvokeHelper(#[from] helper::Error),    #[error("Could not configure credential helpers")]    ConfigureCredentialHelpers {        #[source]        source: Box<dyn std::error::Error + Send + Sync + 'static>,    },    #[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() })]    IdentityMissing { context: Context },    #[error("The handler asked to stop trying to obtain credentials")]    Quit,    #[error("Couldn't obtain {prompt}")]    Prompt { prompt: String, source: gix_prompt::Error },} /// Additional context to be passed to the credentials helper.#[derive(Debug, Default, Clone, Eq, PartialEq)]pub struct Context {    /// Options controlling how this context is encoded and decoded.    pub options: ContextOptions,    /// The protocol over which the credential will be used (e.g., https).    pub protocol: Option<String>,    /// The remote hostname for a network credential. This includes the port number if one was specified (e.g., "example.com:8088").    pub host: Option<String>,    /// 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.    /// It can also be a path on the file system.    pub path: Option<BString>,    /// The credential’s username, if we already have one (e.g., from a URL, the configuration, the user, or from a previously run helper).    pub username: Option<String>,    /// The credential’s password, if we are asking it to be stored.    pub password: Option<String>,    /// An OAuth refresh token that may accompany a password. It is to be treated confidentially, just like the password.    pub oauth_refresh_token: Option<String>,    /// The expiry date of OAuth tokens as seconds from Unix epoch.    pub password_expiry_utc: Option<gix_date::SecondsSinceUnixEpoch>,    /// When this special attribute is read by git credential, the value is parsed as a URL and treated as if its constituent    /// parts were read (e.g., url=<https://example.com> would behave as if    /// protocol=https and host=example.com had been provided). This can help callers avoid parsing URLs themselves.    pub url: Option<BString>,    /// If true, the caller should stop asking for credentials immediately without calling more credential helpers in the chain.    pub quit: Option<bool>,} /// Options for encoding and decoding a [`Context`].#[derive(Debug, Clone, Copy, Eq, PartialEq)]pub struct ContextOptions {    /// If true, carriage returns in credential values are rejected to protect credential-protocol parsing.    ///    /// NUL bytes and newlines are always rejected.    pub protect_protocol: bool,} impl Default for ContextOptions {    fn default() -> Self {        ContextOptions { protect_protocol: true }    }} /// Convert the outcome of a helper invocation to a helper result, assuring that the identity is complete in the process.#[expect(    clippy::result_large_err,    reason = "will be removed once `gix-error` is used consistently")]pub fn helper_outcome_to_result(outcome: Option<helper::Outcome>, action: helper::Action) -> Result {    match (action, outcome) {        (helper::Action::Get(ctx), None) => Err(Error::IdentityMissing {            context: ctx.redacted(),        }),        (helper::Action::Get(ctx), Some(mut outcome)) => match outcome.consume_identity() {            Some(identity) => Ok(Some(Outcome {                identity,                next: outcome.next,            })),            None => Err(if outcome.quit {                Error::Quit            } else {                Error::IdentityMissing {                    context: ctx.redacted(),                }            }),        },        (helper::Action::Store(_) | helper::Action::Erase(_), _ignore) => Ok(None),    }} ///pub mod context;