Enum git_credentials::helper::Action
source · Expand description
The action to perform by the credentials helper.
Variants§
Get(Context)
Provide credentials using the given repository context, which must include the repository url.
Store(BString)
Approve the credentials as identified by the previous input provided as BString
, containing information from Context
.
Erase(BString)
Reject the credentials as identified by the previous input provided as BString
. containing information from Context
.
Implementations§
source§impl Action
impl Action
sourcepub fn send(&self, write: impl Write) -> Result<()>
pub fn send(&self, write: impl Write) -> Result<()>
Send ourselves to the given write
which is expected to be credentials-helper compatible
Examples found in repository?
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
pub(crate) fn raw(helper: &mut crate::Program, action: &Action) -> std::result::Result<Option<Vec<u8>>, Error> {
let (stdin, stdout) = helper.start(action)?;
if let (Action::Get(_), None) = (&action, &stdout) {
panic!("BUG: `Helper` impls must return an output handle to read output from if Action::Get is provided")
}
action.send(stdin)?;
let stdout = stdout
.map(|mut stdout| {
let mut buf = Vec::new();
stdout.read_to_end(&mut buf).map(|_| buf)
})
.transpose()
.map_err(|err| Error::CredentialsHelperFailed { source: err })?;
helper.finish().map_err(|err| {
if err.kind() == std::io::ErrorKind::Other {
Error::CredentialsHelperFailed { source: err }
} else {
err.into()
}
})?;
match matches!(action, Action::Get(_)).then(|| stdout).flatten() {
None => Ok(None),
Some(stdout) => Ok(Some(stdout)),
}
}
source§impl Action
impl Action
Initialization
sourcepub fn get_for_url(url: impl Into<BString>) -> Action
pub fn get_for_url(url: impl Into<BString>) -> Action
Create a Get
action with context containing the given URL.
Note that this creates an Action
suitable for the credential helper cascade only.
source§impl Action
impl Action
Access
sourcepub fn context(&self) -> Option<&Context>
pub fn context(&self) -> Option<&Context>
Return the context of a get operation, or None
.
The opposite of payload
.
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,
)
}
sourcepub fn context_mut(&mut self) -> Option<&mut Context>
pub fn context_mut(&mut self) -> Option<&mut Context>
Return the mutable context of a get operation, or None
.
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,
)
}
sourcepub fn expects_output(&self) -> bool
pub fn expects_output(&self) -> bool
Returns true if this action expects output from the helper.
Examples found in repository?
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
pub(crate) fn start(
&mut self,
action: &helper::Action,
) -> std::io::Result<(std::process::ChildStdin, Option<std::process::ChildStdout>)> {
assert!(self.child.is_none(), "BUG: must not call `start()` twice");
let mut cmd = match &self.kind {
Kind::Builtin => {
let mut cmd = Command::new(cfg!(windows).then(|| "git.exe").unwrap_or("git"));
cmd.arg("credential").arg(action.as_arg(false));
cmd
}
Kind::ExternalShellScript(for_shell)
| Kind::ExternalName {
name_and_args: for_shell,
}
| Kind::ExternalPath {
path_and_args: for_shell,
} => git_command::prepare(git_path::from_bstr(for_shell.as_bstr()).as_ref())
.with_shell()
.arg(action.as_arg(true))
.into(),
};
cmd.stdin(Stdio::piped())
.stdout(if action.expects_output() {
Stdio::piped()
} else {
Stdio::null()
})
.stderr(if self.stderr { Stdio::inherit() } else { Stdio::null() });
let mut child = cmd.spawn()?;
let stdin = child.stdin.take().expect("stdin to be configured");
let stdout = child.stdout.take();
self.child = child.into();
Ok((stdin, stdout))
}
sourcepub fn as_arg(&self, is_external: bool) -> &str
pub fn as_arg(&self, is_external: bool) -> &str
The name of the argument to describe this action. If is_external
is true, the target program is
a custom credentials helper, not a built-in one.
Examples found in repository?
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
pub(crate) fn start(
&mut self,
action: &helper::Action,
) -> std::io::Result<(std::process::ChildStdin, Option<std::process::ChildStdout>)> {
assert!(self.child.is_none(), "BUG: must not call `start()` twice");
let mut cmd = match &self.kind {
Kind::Builtin => {
let mut cmd = Command::new(cfg!(windows).then(|| "git.exe").unwrap_or("git"));
cmd.arg("credential").arg(action.as_arg(false));
cmd
}
Kind::ExternalShellScript(for_shell)
| Kind::ExternalName {
name_and_args: for_shell,
}
| Kind::ExternalPath {
path_and_args: for_shell,
} => git_command::prepare(git_path::from_bstr(for_shell.as_bstr()).as_ref())
.with_shell()
.arg(action.as_arg(true))
.into(),
};
cmd.stdin(Stdio::piped())
.stdout(if action.expects_output() {
Stdio::piped()
} else {
Stdio::null()
})
.stderr(if self.stderr { Stdio::inherit() } else { Stdio::null() });
let mut child = cmd.spawn()?;
let stdin = child.stdin.take().expect("stdin to be configured");
let stdout = child.stdout.take();
self.child = child.into();
Ok((stdin, stdout))
}