-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
- Loading branch information
Akos Kitta
committed
Nov 5, 2018
1 parent
3dd403d
commit 104f356
Showing
9 changed files
with
467 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
// Based on: https://github.com/Microsoft/THEIA/blob/dd3e2d94f81139f9d18ba15a24c16c6061880b93/extensions/git/src/askpass.ts | ||
|
||
import * as path from 'path'; | ||
import * as http from 'http'; | ||
import * as os from 'os'; | ||
import * as fs from 'fs'; | ||
import * as crypto from 'crypto'; | ||
import { Disposable } from '@theia/core/lib/common/disposable'; | ||
import { isWindows } from '@theia/core/lib/common/os'; | ||
|
||
export interface AskpassEnvironment { | ||
readonly GIT_ASKPASS: string; | ||
readonly ELECTRON_RUN_AS_NODE?: string; | ||
readonly THEIA_GIT_ASKPASS_NODE?: string; | ||
readonly THEIA_GIT_ASKPASS_MAIN?: string; | ||
readonly THEIA_GIT_ASKPASS_HANDLE?: string; | ||
} | ||
|
||
export class Askpass implements Disposable { | ||
|
||
private server: http.Server; | ||
private ipcHandlePathPromise: Promise<string>; | ||
private ipcHandlePath: string | undefined; | ||
private enabled = true; | ||
|
||
constructor() { | ||
this.server = http.createServer((req, res) => this.onRequest(req, res)); | ||
this.ipcHandlePathPromise = this.setup().catch(err => { | ||
console.error(err); | ||
return ''; | ||
}); | ||
} | ||
|
||
protected async setup(): Promise<string> { | ||
const buffer = await this.randomBytes(20); | ||
const nonce = buffer.toString('hex'); | ||
const ipcHandlePath = this.getIPCHandlePath(nonce); | ||
this.ipcHandlePath = ipcHandlePath; | ||
|
||
try { | ||
this.server.listen(ipcHandlePath); | ||
this.server.on('error', err => console.error(err)); | ||
} catch (err) { | ||
console.error('Could not launch git askpass helper.'); | ||
this.enabled = false; | ||
} | ||
|
||
return ipcHandlePath; | ||
} | ||
|
||
protected getIPCHandlePath(nonce: string): string { | ||
const fileName = `theia-git-askpass-${nonce}-sock`; | ||
if (isWindows) { | ||
return `\\\\.\\pipe\\${fileName}`; | ||
} | ||
|
||
if (process.env['XDG_RUNTIME_DIR']) { | ||
return path.join(process.env['XDG_RUNTIME_DIR'] as string, fileName); | ||
} | ||
|
||
return path.join(os.tmpdir(), fileName); | ||
} | ||
|
||
protected onRequest(req: http.ServerRequest, res: http.ServerResponse): void { | ||
const chunks: string[] = []; | ||
req.setEncoding('utf8'); | ||
req.on('data', (d: string) => chunks.push(d)); | ||
req.on('end', () => { | ||
const { request, host } = JSON.parse(chunks.join('')); | ||
|
||
this.prompt(host, request).then(result => { | ||
res.writeHead(200); | ||
res.end(JSON.stringify(result)); | ||
}, () => { | ||
res.writeHead(500); | ||
res.end(); | ||
}); | ||
}); | ||
} | ||
|
||
protected async prompt(host: string, request: string): Promise<string> { | ||
const options: InputBoxOptions = { | ||
password: /password/i.test(request), | ||
placeHolder: request, | ||
prompt: `Git: ${host}`, | ||
ignoreFocusOut: true | ||
}; | ||
|
||
return await window.showInputBox(options) || ''; | ||
} | ||
|
||
protected async randomBytes(size: number): Promise<Buffer> { | ||
return new Promise<Buffer>((resolve, reject) => { | ||
crypto.randomBytes(size, (error: Error, buffer: Buffer) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
resolve(buffer); | ||
}); | ||
}); | ||
} | ||
|
||
async getEnv(): Promise<AskpassEnvironment> { | ||
if (!this.enabled) { | ||
return { | ||
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') | ||
}; | ||
} | ||
|
||
return { | ||
ELECTRON_RUN_AS_NODE: '1', | ||
GIT_ASKPASS: path.join(__dirname, 'askpass.sh'), | ||
THEIA_GIT_ASKPASS_NODE: process.execPath, | ||
THEIA_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), | ||
THEIA_GIT_ASKPASS_HANDLE: await this.ipcHandlePathPromise | ||
}; | ||
} | ||
|
||
dispose(): void { | ||
this.server.close(); | ||
if (this.ipcHandlePath && !isWindows) { | ||
fs.unlinkSync(this.ipcHandlePath); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
// Based on: https://github.com/Microsoft/THEIA/blob/dd3e2d94f81139f9d18ba15a24c16c6061880b93/extensions/git/src/askpass-main.ts. | ||
|
||
import * as http from 'http'; | ||
import * as fs from 'fs'; | ||
|
||
// tslint:disable-next-line:no-any | ||
function fatal(err: any): void { | ||
console.error('Missing or invalid credentials.'); | ||
console.error(err); | ||
process.exit(1); | ||
} | ||
|
||
function main(argv: string[]): void { | ||
if (argv.length !== 5) { | ||
return fatal('Wrong number of arguments'); | ||
} | ||
|
||
if (!process.env['THEIA_GIT_ASKPASS_HANDLE']) { | ||
return fatal('Missing handle'); | ||
} | ||
|
||
if (!process.env['THEIA_GIT_ASKPASS_PIPE']) { | ||
return fatal('Missing pipe'); | ||
} | ||
|
||
if (process.env['THEIA_GIT_COMMAND'] === 'fetch') { | ||
return fatal('Skip fetch commands'); | ||
} | ||
|
||
const output = process.env['THEIA_GIT_ASKPASS_PIPE'] as string; | ||
const socketPath = process.env['THEIA_GIT_ASKPASS_HANDLE'] as string; | ||
const request = argv[2]; | ||
const host = argv[4].substring(1, argv[4].length - 2); | ||
const opts: http.RequestOptions = { | ||
socketPath, | ||
path: '/', | ||
method: 'POST' | ||
}; | ||
|
||
const req = http.request(opts, res => { | ||
if (res.statusCode !== 200) { | ||
return fatal(`Bad status code: ${res.statusCode}`); | ||
} | ||
|
||
const chunks: string[] = []; | ||
res.setEncoding('utf8'); | ||
res.on('data', (d: string) => chunks.push(d)); | ||
res.on('end', () => { | ||
const raw = chunks.join(''); | ||
|
||
try { | ||
const result = JSON.parse(raw); | ||
fs.writeFileSync(output, result + '\n'); | ||
} catch (err) { | ||
return fatal('Error parsing response'); | ||
} | ||
|
||
setTimeout(() => process.exit(0), 0); | ||
}); | ||
}); | ||
|
||
req.on('error', () => fatal('Error in request')); | ||
req.write(JSON.stringify({ request, host })); | ||
req.end(); | ||
} | ||
|
||
main(process.argv); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { injectable } from 'inversify'; | ||
import { getPassword, setPassword, deletePassword } from 'keytar'; | ||
|
||
/** | ||
* Shared access to the host system's keychain for Git: | ||
* - on OS X the passwords are managed by the `Keychain`, | ||
* - on Linux they are managed by the `Secret Service API`/`libsecret`, and | ||
* - on Windows they are managed by `Credential Vault`. | ||
* | ||
* **Note**: There is a caveat on Linux. Since the underlying `keytar` library uses `libsecret` so you may need to install it before running npm install. | ||
* Depending on your distribution, you will need to run the following command: | ||
* - Debian/Ubuntu: `sudo apt-get install libsecret-1-dev`. | ||
* - Red Hat-based: `sudo yum install libsecret-devel`. | ||
* - Arch Linux: `sudo pacman -S libsecret`. | ||
*/ | ||
@injectable() | ||
export class GitTokenStore { | ||
|
||
/** | ||
* Adds the password for the service and account to the keychain. | ||
*/ | ||
async set(serviceKey: string, account: string, password: string): Promise<void> { | ||
return setPassword(serviceKey, account, password); | ||
} | ||
|
||
/** | ||
* Resolves to the password for the service and account. `undefined` if the password cannot be retrieved. | ||
*/ | ||
async get(serviceKey: string, account: string): Promise<string | undefined> { | ||
const password = await getPassword(serviceKey, account); | ||
// Do not let `null` to leak into the application code. | ||
if (password === null) { | ||
return undefined; | ||
} | ||
return password; | ||
} | ||
|
||
/** | ||
* Deletes the stored password for the service and account. | ||
* As a result, it resolves to the status of the operation; `true` if it was successful. Otherwise, `false`. | ||
*/ | ||
async delete(key: string, login: string): Promise<boolean> { | ||
return deletePassword(key, login); | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
packages/git/src/electron-node/env/electron-git-env-module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { ContainerModule } from 'inversify'; | ||
import { GitEnvProvider } from '../../node/env/git-env-provider'; | ||
import { ElectronGitEnvProvider } from './electron-git-env-provider'; | ||
|
||
export default new ContainerModule(bind => { | ||
bind(ElectronGitEnvProvider).toSelf().inSingletonScope(); | ||
bind(GitEnvProvider).toService(ElectronGitEnvProvider); | ||
}); |
30 changes: 30 additions & 0 deletions
30
packages/git/src/electron-node/env/electron-git-env-provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { injectable } from 'inversify'; | ||
import { DefaultGitEnvProvider } from '../../node/env/git-env-provider'; | ||
|
||
/** | ||
* Git environment provider for Electron. | ||
* | ||
* TODO: describe this!!! | ||
*/ | ||
@injectable() | ||
export class ElectronGitEnvProvider extends DefaultGitEnvProvider { | ||
|
||
// TODO ASKPASS | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2018 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { ContainerModule } from 'inversify'; | ||
import { GitEnvProvider, DefaultGitEnvProvider } from './git-env-provider'; | ||
|
||
export default new ContainerModule(bind => { | ||
bind(DefaultGitEnvProvider).toSelf().inSingletonScope(); | ||
bind(GitEnvProvider).toService(DefaultGitEnvProvider); | ||
}); |
Oops, something went wrong.