Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5cb0a6a

Browse files
author
Michelle Tilley
authored
Merge branch 'master' into mkt-if-you-liked-it-you-should-have-put-a-pane-around-it
2 parents af0a0df + 7febb28 commit 5cb0a6a

36 files changed

+1772
-348
lines changed

bin/git-askpass-atom.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const net = require('net');
2+
const {execFile} = require('child_process');
3+
4+
const sockPath = process.argv[2];
5+
const prompt = process.argv[3];
6+
7+
const diagnosticsEnabled = process.env.GIT_TRACE && process.env.GIT_TRACE.length !== 0;
8+
const userAskPass = process.env.ATOM_GITHUB_ORIGINAL_SSH_ASKPASS || '';
9+
const workdirPath = process.env.ATOM_GITHUB_WORKDIR_PATH;
10+
11+
function log(message) {
12+
if (!diagnosticsEnabled) {
13+
return;
14+
}
15+
16+
process.stderr.write(`git-askpass-atom: ${message}\n`);
17+
}
18+
19+
function userHelper() {
20+
return new Promise((resolve, reject) => {
21+
if (userAskPass.length === 0) {
22+
log('no user askpass specified');
23+
24+
reject(new Error('No user SSH_ASKPASS'));
25+
return;
26+
}
27+
28+
log(`attempting user askpass: ${userAskPass}`);
29+
execFile(userAskPass, [prompt], {cwd: workdirPath}, (err, stdout, stderr) => {
30+
if (err) {
31+
log(`user askpass failed. this is ok\n${err.stack}`);
32+
33+
reject(err);
34+
return;
35+
}
36+
37+
log('collected password from user askpass');
38+
resolve(stdout);
39+
});
40+
});
41+
}
42+
43+
function dialog() {
44+
const payload = {prompt, includeUsername: false};
45+
log('requesting dialog through Atom socket');
46+
log(`prompt = "${prompt}"`);
47+
48+
return new Promise((resolve, reject) => {
49+
const socket = net.connect(sockPath, () => {
50+
log('connection established');
51+
const parts = [];
52+
53+
socket.on('data', data => parts.push(data));
54+
socket.on('end', () => {
55+
log('Atom socket stream terminated');
56+
57+
try {
58+
const replyDocument = JSON.parse(parts.join(''));
59+
log('Atom reply parsed');
60+
resolve(replyDocument.password);
61+
} catch (err) {
62+
log('Unable to parse reply from Atom');
63+
reject(err);
64+
}
65+
});
66+
67+
log('writing payload');
68+
socket.write(JSON.stringify(payload) + '\u0000', 'utf8');
69+
log('payload written');
70+
});
71+
socket.setEncoding('utf8');
72+
});
73+
}
74+
75+
userHelper()
76+
.catch(() => dialog())
77+
.then(password => {
78+
process.stdout.write(password);
79+
log('success');
80+
process.exit(0);
81+
}, err => {
82+
process.stderr.write(`Unable to prompt through Atom:\n${err.stack}`);
83+
log('failure');
84+
process.exit(1);
85+
});

bin/git-askpass-atom.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
ELECTRON_RUN_AS_NODE=1 ELECTRON_NO_ATTACH_CONSOLE=1 "$ATOM_GITHUB_ELECTRON_PATH" "$ATOM_GITHUB_ASKPASS_PATH" "$ATOM_GITHUB_SOCK_PATH" "$@"

bin/git-credential-atom.js

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
const net = require('net');
2+
const readline = require('readline');
3+
const url = require('url');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const {execFile} = require('child_process');
7+
const {GitProcess} = require(process.env.ATOM_GITHUB_DUGITE_PATH);
8+
9+
const diagnosticsEnabled = process.env.GIT_TRACE && process.env.GIT_TRACE.length !== 0;
10+
const workdirPath = process.env.ATOM_GITHUB_WORKDIR_PATH;
11+
const sockPath = process.argv[2];
12+
const action = process.argv[3];
13+
14+
const rememberFile = path.join(__dirname, 'remember');
15+
16+
/*
17+
* Emit diagnostic messages to stderr if GIT_TRACE is set to a non-empty value.
18+
*/
19+
function log(message) {
20+
if (!diagnosticsEnabled) {
21+
return;
22+
}
23+
24+
process.stderr.write(`git-credential-atom: ${message}\n`);
25+
}
26+
27+
/*
28+
* Because the git within dugite was (possibly) built with a different $PREFIX than the user's native git,
29+
* credential helpers or other config settings from the system configuration may not be discovered. Attempt
30+
* to collect them by running the native git, if one is present.
31+
*/
32+
function systemCredentialHelpers() {
33+
return new Promise(resolve => {
34+
const env = {
35+
PATH: process.env.ATOM_GITHUB_ORIGINAL_PATH || '',
36+
GIT_CONFIG_PARAMETERS: '',
37+
};
38+
39+
log('discover credential helpers from system git configuration');
40+
log(`PATH = ${env.PATH}`);
41+
42+
execFile('git', ['config', '--system', '--get-all', 'credential.helper'], {env}, (error, stdout, stderr) => {
43+
if (error) {
44+
log(`failed to list credential helpers. this is ok\n${error.stack}`);
45+
46+
// Oh well, c'est la vie
47+
resolve([]);
48+
return;
49+
}
50+
51+
const helpers = stdout.split(/\n+/).map(line => line.trim()).filter(each => each.length > 0);
52+
log(`discovered system credential helpers: ${helpers.map(h => `"${h}"`).join(', ')}`);
53+
resolve(helpers);
54+
});
55+
});
56+
}
57+
58+
/*
59+
* Dispatch a `git credential` subcommand to all configured credential helpers. Return a Promise that
60+
* resolves with the exit status, stdout, and stderr of the subcommand.
61+
*/
62+
function withAllHelpers(query, subAction) {
63+
return systemCredentialHelpers()
64+
.then(systemHelpers => {
65+
const env = {
66+
GIT_ASKPASS: process.env.ATOM_GITHUB_ORIGINAL_GIT_ASKPASS || '',
67+
SSH_ASKPASS: process.env.ATOM_GITHUB_ORIGINAL_SSH_ASKPASS || '',
68+
GIT_CONFIG_PARAMETERS: '', // Only you can prevent forkbombs
69+
};
70+
71+
const stdin = Object.keys(query).map(k => `${k}=${query[k]}\n`).join('') + '\n';
72+
const stdinEncoding = 'utf8';
73+
74+
const args = [];
75+
systemHelpers.forEach(helper => args.push('-c', `credential.helper=${helper}`));
76+
args.push('credential', subAction);
77+
78+
log(`attempting to run ${subAction} with user-configured credential helpers`);
79+
log(`GIT_ASKPASS = ${env.GIT_ASKPASS}`);
80+
log(`SSH_ASKPASS = ${env.SSH_ASKPASS}`);
81+
log(`arguments = ${args.join(' ')}`);
82+
log(`stdin =\n${stdin.replace(/password=[^\n]+/, '******')}`);
83+
84+
return GitProcess.exec(args, workdirPath, {env, stdin, stdinEncoding});
85+
});
86+
}
87+
88+
/*
89+
* Parse `key=value` lines from stdin until EOF or the first blank line.
90+
*/
91+
function parse() {
92+
return new Promise((resolve, reject) => {
93+
const rl = readline.createInterface({input: process.stdin});
94+
95+
let resolved = false;
96+
const query = {};
97+
98+
rl.on('line', line => {
99+
if (resolved) {
100+
return;
101+
}
102+
103+
if (line.length === 0) {
104+
log('all input received: blank line received');
105+
resolved = true;
106+
resolve(query);
107+
return;
108+
}
109+
110+
const ind = line.indexOf('=');
111+
if (ind === -1) {
112+
reject(new Error(`Unable to parse credential line: ${line}`));
113+
return;
114+
}
115+
116+
const key = line.substring(0, ind);
117+
const value = line.substring(ind + 1).replace(/\n$/, '');
118+
log(`parsed from stdin: [${key}] = [${key === 'password' ? '******' : value}]`);
119+
120+
query[key] = value;
121+
});
122+
123+
rl.on('close', () => {
124+
if (resolved) {
125+
return;
126+
}
127+
128+
log('all input received: EOF from stdin');
129+
resolved = true;
130+
resolve(query);
131+
});
132+
});
133+
}
134+
135+
/*
136+
* Attempt to use user-configured credential handlers through the normal git channels. If they actually work,
137+
* hooray! Report the results to stdout. Otherwise, reject the promise and collect credentials through Atom.
138+
*/
139+
function fromOtherHelpers(query) {
140+
return withAllHelpers(query, 'fill')
141+
.then(({stdout, stderr, exitCode}) => {
142+
if (exitCode !== 0) {
143+
log(`stdout:\n${stdout}`);
144+
log(`stderr:\n${stderr}`);
145+
log(`user-configured credential helpers failed with exit code ${exitCode}. this is ok`);
146+
147+
throw new Error('git-credential fill failed');
148+
}
149+
150+
if (/password=/.test(stdout)) {
151+
log('password received from user-configured credential helper');
152+
153+
return stdout;
154+
} else {
155+
log(`no password received from user-configured credential helper:\n${stdout}`);
156+
157+
throw new Error('No password reported from upstream git-credential fill');
158+
}
159+
});
160+
}
161+
162+
/*
163+
* This is a placeholder for eventual support of storage of credentials in an OS keychain.
164+
*/
165+
function fromKeytar(query) {
166+
return Promise.reject(new Error('Not implemented'));
167+
}
168+
169+
/*
170+
* Request a dialog in Atom by writing a null-delimited JSON query to the socket we were given.
171+
*/
172+
function dialog(query) {
173+
if (query.username) {
174+
query.auth = query.username;
175+
}
176+
const prompt = 'Please enter your credentials for ' + url.format(query);
177+
const includeUsername = !query.username;
178+
179+
const payload = {prompt, includeUsername};
180+
181+
return new Promise((resolve, reject) => {
182+
log('requesting dialog through Atom socket');
183+
log(`prompt = "${prompt}" includeUsername = ${includeUsername}`);
184+
185+
const socket = net.connect(sockPath, () => {
186+
log('connection established');
187+
188+
const parts = [];
189+
190+
socket.on('data', data => parts.push(data));
191+
socket.on('end', () => {
192+
log('Atom socket stream terminated');
193+
194+
try {
195+
const reply = JSON.parse(parts.join(''));
196+
197+
const writeReply = function() {
198+
const lines = [];
199+
['protocol', 'host', 'username', 'password'].forEach(k => {
200+
const value = reply[k] !== undefined ? reply[k] : query[k];
201+
lines.push(`${k}=${value}\n`);
202+
});
203+
204+
log('Atom reply parsed');
205+
resolve(lines.join('') + 'quit=true\n');
206+
};
207+
208+
if (reply.remember) {
209+
fs.writeFile(rememberFile, writeReply);
210+
} else {
211+
writeReply();
212+
}
213+
} catch (e) {
214+
log(`Unable to parse reply from Atom:\n${e.stack}`);
215+
reject(e);
216+
}
217+
});
218+
219+
log('writing payload');
220+
socket.write(JSON.stringify(payload) + '\u0000', 'utf8');
221+
log('payload written');
222+
});
223+
socket.setEncoding('utf8');
224+
});
225+
}
226+
227+
function get() {
228+
parse()
229+
.then(query => {
230+
return fromOtherHelpers(query)
231+
.catch(() => fromKeytar(query))
232+
.catch(() => dialog(query));
233+
})
234+
.then(reply => {
235+
process.stdout.write(reply);
236+
log('success');
237+
process.exit(0);
238+
}, err => {
239+
process.stderr.write(`Unable to prompt through Atom:\n${err.stack}`);
240+
log('failure');
241+
process.stdout.write('quit=true\n\n');
242+
process.exit(0);
243+
});
244+
}
245+
246+
function store() {
247+
parse()
248+
.then(query => withAllHelpers(query, 'approve'))
249+
.then(() => process.exit(0), () => process.exit(1));
250+
}
251+
252+
function erase() {
253+
parse()
254+
.then(query => withAllHelpers(query, 'reject'))
255+
.then(() => process.exit(0), () => process.exit(1));
256+
}
257+
258+
log(`working directory = ${workdirPath}`);
259+
log(`socket path = ${sockPath}`);
260+
log(`action = ${action}`);
261+
262+
switch (action) {
263+
case 'get':
264+
get();
265+
break;
266+
case 'store':
267+
store();
268+
break;
269+
case 'erase':
270+
erase();
271+
break;
272+
default:
273+
log(`Unrecognized command: ${action}`);
274+
process.exit(0);
275+
break;
276+
}

bin/git-credential-atom.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
ELECTRON_RUN_AS_NODE=1 ELECTRON_NO_ATTACH_CONSOLE=1 "$ATOM_GITHUB_ELECTRON_PATH" "$ATOM_GITHUB_CREDENTIAL_PATH" "$ATOM_GITHUB_SOCK_PATH" "$@"

bin/git-prompt-server-launcher.sh

Lines changed: 0 additions & 2 deletions
This file was deleted.

bin/git-prompt-server-script.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

bin/gpg-no-tty.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ unset GIT_CONFIG_PARAMETERS
77
GPG_PROGRAM=$(git config gpg.program || echo 'gpg')
88
PASSPHRASE_ARG=
99

10-
if [ -n "${ATOM_GITHUB_CREDENTIAL_HELPER_SCRIPT_PATH:-}" ] && [ -n "${GIT_ASKPASS:-}" ]; then
10+
if [ -n "${ATOM_GITHUB_ASKPASS_PATH:-}" ] && [ -n "${GIT_ASKPASS:-}" ]; then
1111
SIGNING_KEY=$(git config user.signingkey)
1212
if [ -n "${SIGNING_KEY}" ]; then
1313
PROMPT="Please enter the passphrase for the GPG key '${SIGNING_KEY}'."

0 commit comments

Comments
 (0)