171 lines · 7.5 KB · Rust
RawDownload
use crate::{    Remote,    remote::{Connection, connection::AuthenticateFn, connection::ConnectionDetached},};#[cfg(feature = "async-network-client")]use gix_transport::client::async_io::Transport;#[cfg(feature = "blocking-network-client")]use gix_transport::client::blocking_io::Transport; /// Builderimpl<'remote, 'repo, T> Connection<'remote, '_, 'repo, T>where    T: Transport,{    /// Set a custom credentials callback to provide credentials if the remotes require authentication.    ///    /// Otherwise, we will use the git configuration to perform the same task as the `git credential` helper program,    /// which is calling other helper programs in succession while resorting to a prompt to obtain credentials from the    /// user.    ///    /// A custom function may also be used to prevent accessing resources with authentication.    ///    /// Use the [`configured_credentials()`](Connection::configured_credentials()) method to obtain the implementation    /// that would otherwise be used, which can be useful to proxy the default configuration and obtain information about the    /// URLs to authenticate with.    pub fn with_credentials<'b>(        self,        helper: impl FnMut(gix_credentials::helper::Action) -> gix_credentials::protocol::Result + 'b,    ) -> Connection<'remote, 'b, 'repo, T> {        Connection {            remote: self.remote,            authenticate: Some(Box::new(helper)),            transport_options: self.transport_options,            transport: self.transport,            handshake: self.handshake,            trace: self.trace,        }    }     /// Provide configuration to be used before the first handshake is conducted.    /// It's typically created by initializing it with [`Repository::transport_options()`](crate::Repository::transport_options()),    /// which is also the default if this isn't set explicitly. Note that all the default configuration is created from `git`    /// configuration, which can also be manipulated through overrides to affect the default configuration.    ///    /// Use this method to provide transport configuration with custom backend configuration that is not configurable by other means and    /// custom to the application at hand.    pub fn with_transport_options(mut self, config: Box<dyn std::any::Any>) -> Self {        self.transport_options = Some(config);        self    }} /// Mutationimpl<T> ConnectionDetached<'_, T>where    T: Transport,{    pub(crate) fn configured_credentials_for_current_url(&self, repo: &crate::Repository) -> AuthenticateFn<'static> {        configured_credentials_for_current_url(repo.clone())    }} /// Mutationimpl<'auth, T> Connection<'_, 'auth, '_, T>where    T: Transport,{    /// Like [`with_credentials()`](Self::with_credentials()), but without consuming the connection.    pub fn set_credentials(        &mut self,        helper: impl FnMut(gix_credentials::helper::Action) -> gix_credentials::protocol::Result + 'auth,    ) -> &mut Self {        self.authenticate = Some(Box::new(helper));        self    }     /// Like [`with_transport_options()`](Self::with_transport_options()), but without consuming the connection.    pub fn set_transport_options(&mut self, config: Box<dyn std::any::Any>) -> &mut Self {        self.transport_options = Some(config);        self    }} /// Accessimpl<'auth, 'repo, T> Connection<'_, 'auth, 'repo, T>where    T: Transport,{    /// A utility to return a function that will use this repository's configuration to obtain credentials for `url`,    /// similar to what `git credential` is doing.    ///    /// It's meant to be used by users of the [`with_credentials()`](Self::with_credentials()) builder to gain access to the    /// default way of handling credentials, which they can call as fallback.    pub fn configured_credentials(        &self,        url: gix_url::Url,    ) -> Result<AuthenticateFn<'static>, crate::config::credential_helpers::Error> {        let (mut cascade, _action_with_normalized_url, prompt_opts) =            self.remote.repo.config_snapshot().credential_helpers(url)?;        Ok(Box::new(move |action| cascade.invoke(action, prompt_opts.clone())) as AuthenticateFn<'_>)    }     /// A utility to return a function that uses each    /// [`Get`](gix_credentials::helper::Action::Get) action's context to obtain credentials from this repository's    /// configuration.    ///    /// The transport creates these actions from its current URL, which means authentication naturally follows redirects.    pub fn configured_credentials_for_current_url(&self) -> AuthenticateFn<'static> {        configured_credentials_for_current_url(self.remote.repo.clone())    }     /// Return the underlying remote that instantiate this connection.    pub fn remote(&self) -> &Remote<'repo> {        self.remote    }     /// Provide a mutable transport to allow interacting with it according to its actual type.    /// Note that the caller _should not_ call [`configure()`](gix_protocol::transport::client::TransportWithoutIO::configure())    /// as we will call it automatically before performing the handshake. Instead, to bring in custom configuration,    /// call [`with_transport_options()`](Connection::with_transport_options()).    pub fn transport_mut(&mut self) -> &mut T {        &mut self.transport.inner    }     pub(crate) fn into_detached(self) -> ConnectionDetached<'auth, T> {        ConnectionDetached {            remote: self.remote.detached(),            authenticate: self.authenticate,            transport_options: self.transport_options,            transport: self.transport,            handshake: self.handshake,            trace: self.trace,        }    }} fn configured_credentials_for_current_url(repo: crate::Repository) -> AuthenticateFn<'static> {    let mut previous_cascade_and_prompt = None;    Box::new(move |action| {        if matches!(&action, gix_credentials::helper::Action::Get(_)) {            // The handshake creates the `Get` action from the transport's current URL. That URL may            // differ from the initial remote URL after redirects, and credential configuration can be            // URL-specific. Configure the cascade for this action URL, then keep it for the matching            // `Store` or `Erase` follow-up actions whose context is carried as an encoded payload,            // and is less convenient to use.            let url = action                .context()                .and_then(|ctx| ctx.url.clone().or_else(|| ctx.to_url()))                .ok_or(gix_credentials::protocol::Error::UrlMissing)?;            let (mut cascade, _action_with_normalized_url, prompt_opts) = repo                .config_snapshot()                .credential_helpers(gix_url::parse(url.as_ref())?)                .map_err(|source| gix_credentials::protocol::Error::ConfigureCredentialHelpers {                    source: Box::new(source),                })?;            let outcome = cascade.invoke(action, prompt_opts.clone());            previous_cascade_and_prompt = Some((cascade, prompt_opts));            outcome        } else {            match previous_cascade_and_prompt.as_mut() {                Some((cascade, prompt_opts)) => cascade.invoke(action, prompt_opts.clone()),                None => {                    gix_trace::warn!(                        "credential Store/Erase follow-up was invoked without a preceding Get; ignoring advisory action"                    );                    Ok(None)                }            }        }    }) as AuthenticateFn<'_>}