Struct git_credentials::protocol::Context
source · pub struct Context {
pub protocol: Option<String>,
pub host: Option<String>,
pub path: Option<BString>,
pub username: Option<String>,
pub password: Option<String>,
pub url: Option<BString>,
pub quit: Option<bool>,
}
Expand description
Additional context to be passed to the credentials helper.
Fields§
§protocol: Option<String>
The protocol over which the credential will be used (e.g., https).
host: Option<String>
The remote hostname for a network credential. This includes the port number if one was specified (e.g., “example.com:8088”).
path: Option<BString>
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.
username: Option<String>
The credential’s username, if we already have one (e.g., from a URL, the configuration, the user, or from a previously run helper).
password: Option<String>
The credential’s password, if we are asking it to be stored.
url: Option<BString>
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.
quit: Option<bool>
If true, the caller should stop asking for credentials immediately without calling more credential helpers in the chain.
Implementations§
source§impl Context
impl Context
sourcepub fn to_url(&self) -> Option<BString>
pub fn to_url(&self) -> Option<BString>
Convert all relevant fields into a URL for consumption.
sourcepub fn to_prompt(&self, field: &str) -> String
pub fn to_prompt(&self, field: &str) -> String
Compute a prompt to obtain the given value.
Examples found in repository?
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
pub fn invoke(&mut self, mut action: helper::Action, mut prompt: git_prompt::Options<'_>) -> protocol::Result {
let mut url = action
.context_mut()
.map(|ctx| ctx.destructure_url_in_place(self.use_http_path))
.transpose()?
.and_then(|ctx| ctx.url.take());
for program in &mut self.programs {
program.stderr = self.stderr;
match helper::invoke::raw(program, &action) {
Ok(None) => {}
Ok(Some(stdout)) => {
let ctx = Context::from_bytes(&stdout)?;
if let Some(dst_ctx) = action.context_mut() {
if let Some(src) = ctx.path {
dst_ctx.path = Some(src);
}
for (src, dst) in [
(ctx.protocol, &mut dst_ctx.protocol),
(ctx.host, &mut dst_ctx.host),
(ctx.username, &mut dst_ctx.username),
(ctx.password, &mut dst_ctx.password),
] {
if let Some(src) = src {
*dst = Some(src);
}
}
if let Some(src) = ctx.url {
dst_ctx.url = Some(src);
url = dst_ctx.destructure_url_in_place(self.use_http_path)?.url.take();
}
if dst_ctx.username.is_some() && dst_ctx.password.is_some() {
break;
}
if ctx.quit.unwrap_or_default() {
dst_ctx.quit = ctx.quit;
break;
}
}
}
Err(helper::Error::CredentialsHelperFailed { .. }) => continue, // ignore helpers that we can't call
Err(err) if action.context().is_some() => return Err(err.into()), // communication errors are fatal when getting credentials
Err(_) => {} // for other actions, ignore everything, try the operation
}
}
if prompt.mode != git_prompt::Mode::Disable {
if let Some(ctx) = action.context_mut() {
ctx.url = url;
if ctx.username.is_none() {
let message = ctx.to_prompt("Username");
prompt.mode = git_prompt::Mode::Visible;
ctx.username = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
if ctx.password.is_none() {
let message = ctx.to_prompt("Password");
prompt.mode = git_prompt::Mode::Hidden;
ctx.password = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
}
}
protocol::helper_outcome_to_result(
action.context().map(|ctx| helper::Outcome {
username: ctx.username.clone(),
password: ctx.password.clone(),
quit: ctx.quit.unwrap_or(false),
next: ctx.to_owned().into(),
}),
action,
)
}
source§impl Context
impl Context
In-place mutation
sourcepub fn destructure_url_in_place(
&mut self,
use_http_path: bool
) -> Result<&mut Self, Error>
pub fn destructure_url_in_place(
&mut self,
use_http_path: bool
) -> Result<&mut Self, Error>
Destructure the url at our url
field into parts like protocol, host, username and path and store
them in our respective fields. If use_http_path
is set, http paths are significant even though
normally this isn’t the case.
Examples found in repository?
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
pub fn invoke(&mut self, mut action: helper::Action, mut prompt: git_prompt::Options<'_>) -> protocol::Result {
let mut url = action
.context_mut()
.map(|ctx| ctx.destructure_url_in_place(self.use_http_path))
.transpose()?
.and_then(|ctx| ctx.url.take());
for program in &mut self.programs {
program.stderr = self.stderr;
match helper::invoke::raw(program, &action) {
Ok(None) => {}
Ok(Some(stdout)) => {
let ctx = Context::from_bytes(&stdout)?;
if let Some(dst_ctx) = action.context_mut() {
if let Some(src) = ctx.path {
dst_ctx.path = Some(src);
}
for (src, dst) in [
(ctx.protocol, &mut dst_ctx.protocol),
(ctx.host, &mut dst_ctx.host),
(ctx.username, &mut dst_ctx.username),
(ctx.password, &mut dst_ctx.password),
] {
if let Some(src) = src {
*dst = Some(src);
}
}
if let Some(src) = ctx.url {
dst_ctx.url = Some(src);
url = dst_ctx.destructure_url_in_place(self.use_http_path)?.url.take();
}
if dst_ctx.username.is_some() && dst_ctx.password.is_some() {
break;
}
if ctx.quit.unwrap_or_default() {
dst_ctx.quit = ctx.quit;
break;
}
}
}
Err(helper::Error::CredentialsHelperFailed { .. }) => continue, // ignore helpers that we can't call
Err(err) if action.context().is_some() => return Err(err.into()), // communication errors are fatal when getting credentials
Err(_) => {} // for other actions, ignore everything, try the operation
}
}
if prompt.mode != git_prompt::Mode::Disable {
if let Some(ctx) = action.context_mut() {
ctx.url = url;
if ctx.username.is_none() {
let message = ctx.to_prompt("Username");
prompt.mode = git_prompt::Mode::Visible;
ctx.username = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
if ctx.password.is_none() {
let message = ctx.to_prompt("Password");
prompt.mode = git_prompt::Mode::Hidden;
ctx.password = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
}
}
protocol::helper_outcome_to_result(
action.context().map(|ctx| helper::Outcome {
username: ctx.username.clone(),
password: ctx.password.clone(),
quit: ctx.quit.unwrap_or(false),
next: ctx.to_owned().into(),
}),
action,
)
}
source§impl Context
impl Context
sourcepub fn write_to(&self, out: impl Write) -> Result<()>
pub fn write_to(&self, out: impl Write) -> Result<()>
Write ourselves to out
such that from_bytes()
can decode it losslessly.
Examples found in repository?
More examples
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
pub fn main<CredentialsFn, E>(
args: impl IntoIterator<Item = OsString>,
mut stdin: impl std::io::Read,
stdout: impl std::io::Write,
credentials: CredentialsFn,
) -> Result<(), Error>
where
CredentialsFn: FnOnce(Action, Context) -> Result<Option<Context>, E>,
E: std::error::Error + Send + Sync + 'static,
{
let action: Action = args.into_iter().next().ok_or(Error::ActionMissing)?.try_into()?;
let mut buf = Vec::<u8>::with_capacity(512);
stdin.read_to_end(&mut buf)?;
let ctx = Context::from_bytes(&buf)?;
if ctx.url.is_none() {
return Err(Error::UrlMissing);
}
let res = credentials(action, ctx).map_err(|err| Error::Helper { source: Box::new(err) })?;
match (action, res) {
(Action::Get, None) => {
return Err(Error::CredentialsMissing {
url: Context::from_bytes(&buf)?.url.expect("present and checked above"),
})
}
(Action::Get, Some(ctx)) => ctx.write_to(stdout)?,
(Action::Erase | Action::Store, None) => {}
(Action::Erase | Action::Store, Some(_)) => {
panic!("BUG: credentials helper must not return context for erase or store actions")
}
}
Ok(())
}
sourcepub fn to_bstring(&self) -> BString
pub fn to_bstring(&self) -> BString
Like write_to()
, but writes infallibly into memory.
Examples found in repository?
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
pub fn main() -> Result<(), git_credentials::program::main::Error> {
git_credentials::program::main(
std::env::args_os().skip(1),
std::io::stdin(),
std::io::stdout(),
|action, context| {
use git_credentials::program::main::Action::*;
git_credentials::helper::Cascade::default()
.invoke(
match action {
Get => git_credentials::helper::Action::Get(context),
Erase => git_credentials::helper::Action::Erase(context.to_bstring()),
Store => git_credentials::helper::Action::Store(context.to_bstring()),
},
git_prompt::Options::default().apply_environment(true, true, true),
)
.map(|outcome| outcome.and_then(|outcome| (&outcome.next).try_into().ok()))
},
)
}
source§impl Context
impl Context
sourcepub fn from_bytes(input: &[u8]) -> Result<Self, Error>
pub fn from_bytes(input: &[u8]) -> Result<Self, Error>
Decode ourselves from input
which is the format written by write_to()
.
Examples found in repository?
More examples
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
pub fn invoke(helper: &mut crate::Program, action: &Action) -> Result {
match raw(helper, action)? {
None => Ok(None),
Some(stdout) => {
let ctx = Context::from_bytes(stdout.as_slice())?;
Ok(Some(Outcome {
username: ctx.username,
password: ctx.password,
quit: ctx.quit.unwrap_or(false),
next: NextAction {
previous_output: stdout.into(),
},
}))
}
}
}
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
pub fn main<CredentialsFn, E>(
args: impl IntoIterator<Item = OsString>,
mut stdin: impl std::io::Read,
stdout: impl std::io::Write,
credentials: CredentialsFn,
) -> Result<(), Error>
where
CredentialsFn: FnOnce(Action, Context) -> Result<Option<Context>, E>,
E: std::error::Error + Send + Sync + 'static,
{
let action: Action = args.into_iter().next().ok_or(Error::ActionMissing)?.try_into()?;
let mut buf = Vec::<u8>::with_capacity(512);
stdin.read_to_end(&mut buf)?;
let ctx = Context::from_bytes(&buf)?;
if ctx.url.is_none() {
return Err(Error::UrlMissing);
}
let res = credentials(action, ctx).map_err(|err| Error::Helper { source: Box::new(err) })?;
match (action, res) {
(Action::Get, None) => {
return Err(Error::CredentialsMissing {
url: Context::from_bytes(&buf)?.url.expect("present and checked above"),
})
}
(Action::Get, Some(ctx)) => ctx.write_to(stdout)?,
(Action::Erase | Action::Store, None) => {}
(Action::Erase | Action::Store, Some(_)) => {
panic!("BUG: credentials helper must not return context for erase or store actions")
}
}
Ok(())
}
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
pub fn invoke(&mut self, mut action: helper::Action, mut prompt: git_prompt::Options<'_>) -> protocol::Result {
let mut url = action
.context_mut()
.map(|ctx| ctx.destructure_url_in_place(self.use_http_path))
.transpose()?
.and_then(|ctx| ctx.url.take());
for program in &mut self.programs {
program.stderr = self.stderr;
match helper::invoke::raw(program, &action) {
Ok(None) => {}
Ok(Some(stdout)) => {
let ctx = Context::from_bytes(&stdout)?;
if let Some(dst_ctx) = action.context_mut() {
if let Some(src) = ctx.path {
dst_ctx.path = Some(src);
}
for (src, dst) in [
(ctx.protocol, &mut dst_ctx.protocol),
(ctx.host, &mut dst_ctx.host),
(ctx.username, &mut dst_ctx.username),
(ctx.password, &mut dst_ctx.password),
] {
if let Some(src) = src {
*dst = Some(src);
}
}
if let Some(src) = ctx.url {
dst_ctx.url = Some(src);
url = dst_ctx.destructure_url_in_place(self.use_http_path)?.url.take();
}
if dst_ctx.username.is_some() && dst_ctx.password.is_some() {
break;
}
if ctx.quit.unwrap_or_default() {
dst_ctx.quit = ctx.quit;
break;
}
}
}
Err(helper::Error::CredentialsHelperFailed { .. }) => continue, // ignore helpers that we can't call
Err(err) if action.context().is_some() => return Err(err.into()), // communication errors are fatal when getting credentials
Err(_) => {} // for other actions, ignore everything, try the operation
}
}
if prompt.mode != git_prompt::Mode::Disable {
if let Some(ctx) = action.context_mut() {
ctx.url = url;
if ctx.username.is_none() {
let message = ctx.to_prompt("Username");
prompt.mode = git_prompt::Mode::Visible;
ctx.username = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
if ctx.password.is_none() {
let message = ctx.to_prompt("Password");
prompt.mode = git_prompt::Mode::Hidden;
ctx.password = git_prompt::ask(&message, &prompt)
.map_err(|err| protocol::Error::Prompt {
prompt: message,
source: err,
})?
.into();
}
}
}
protocol::helper_outcome_to_result(
action.context().map(|ctx| helper::Outcome {
username: ctx.username.clone(),
password: ctx.password.clone(),
quit: ctx.quit.unwrap_or(false),
next: ctx.to_owned().into(),
}),
action,
)
}