-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GitHub authentication provider extension, closes #90384
- Loading branch information
Rachel Macfarlane
authored
Feb 20, 2020
1 parent
ebb6aaa
commit eed3932
Showing
18 changed files
with
1,087 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
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 @@ | ||
src/common/config.json |
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,7 @@ | ||
# GitHub Authentication for Visual Studio Code | ||
|
||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. | ||
|
||
## Features | ||
|
||
This extension provides support for authenticating to GitHub. |
20 changes: 20 additions & 0 deletions
20
extensions/github-authentication/extension.webpack.config.js
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,20 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
//@ts-check | ||
|
||
'use strict'; | ||
|
||
const withDefaults = require('../shared.webpack.config'); | ||
|
||
module.exports = withDefaults({ | ||
context: __dirname, | ||
entry: { | ||
extension: './src/extension.ts', | ||
}, | ||
externals: { | ||
'keytar': 'commonjs keytar' | ||
} | ||
}); |
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,32 @@ | ||
{ | ||
"name": "github-authentication", | ||
"displayName": "%displayName%", | ||
"description": "%description%", | ||
"publisher": "vscode", | ||
"version": "0.0.1", | ||
"engines": { | ||
"vscode": "^1.41.0" | ||
}, | ||
"enableProposedApi": true, | ||
"categories": [ | ||
"Other" | ||
], | ||
"activationEvents": [ | ||
"*" | ||
], | ||
"main": "./out/extension.js", | ||
"scripts": { | ||
"vscode:prepublish": "npm run compile", | ||
"compile": "gulp compile-extension:github-authentication", | ||
"watch": "gulp watch-extension:github-authentication" | ||
}, | ||
"dependencies": { | ||
"uuid": "^3.3.3" | ||
}, | ||
"devDependencies": { | ||
"@types/keytar": "^4.4.2", | ||
"@types/node": "^10.12.21", | ||
"@types/uuid": "^3.4.6", | ||
"typescript": "^3.7.5" | ||
} | ||
} |
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,4 @@ | ||
{ | ||
"displayName": "GitHub Authentication", | ||
"description": "GitHub Authentication Provider" | ||
} |
46 changes: 46 additions & 0 deletions
46
extensions/github-authentication/src/common/clientRegistrar.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,46 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
export interface ClientDetails { | ||
id?: string; | ||
secret?: string; | ||
} | ||
|
||
export interface ClientConfig { | ||
OSS: ClientDetails; | ||
INSIDERS: ClientDetails; | ||
} | ||
|
||
export class Registrar { | ||
private _config: ClientConfig; | ||
|
||
constructor() { | ||
this._config = require('./config.json') as ClientConfig; | ||
} | ||
getClientDetails(product: string): ClientDetails { | ||
let details: ClientDetails | undefined; | ||
switch (product) { | ||
case 'code-oss': | ||
details = this._config.OSS; | ||
break; | ||
|
||
case 'vscode-insiders': | ||
details = this._config.INSIDERS; | ||
break; | ||
|
||
default: | ||
throw new Error(`Unrecognized product ${product}`); | ||
} | ||
|
||
if (!details.id || !details.secret) { | ||
throw new Error(`No client configuration available for ${product}`); | ||
} | ||
|
||
return details; | ||
} | ||
} | ||
|
||
const ClientRegistrar = new Registrar(); | ||
export default ClientRegistrar; |
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,73 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
// keytar depends on a native module shipped in vscode, so this is | ||
// how we load it | ||
import * as keytarType from 'keytar'; | ||
import { env } from 'vscode'; | ||
import Logger from './logger'; | ||
|
||
function getKeytar(): Keytar | undefined { | ||
try { | ||
return require('keytar'); | ||
} catch (err) { | ||
console.log(err); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
export type Keytar = { | ||
getPassword: typeof keytarType['getPassword']; | ||
setPassword: typeof keytarType['setPassword']; | ||
deletePassword: typeof keytarType['deletePassword']; | ||
}; | ||
|
||
const SERVICE_ID = `${env.uriScheme}-github.login`; | ||
const ACCOUNT_ID = 'account'; | ||
|
||
export class Keychain { | ||
private keytar: Keytar; | ||
|
||
constructor() { | ||
const keytar = getKeytar(); | ||
if (!keytar) { | ||
throw new Error('System keychain unavailable'); | ||
} | ||
|
||
this.keytar = keytar; | ||
} | ||
|
||
async setToken(token: string): Promise<void> { | ||
try { | ||
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token); | ||
} catch (e) { | ||
// Ignore | ||
Logger.error(`Setting token failed: ${e}`); | ||
} | ||
} | ||
|
||
async getToken(): Promise<string | null | undefined> { | ||
try { | ||
return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID); | ||
} catch (e) { | ||
// Ignore | ||
Logger.error(`Getting token failed: ${e}`); | ||
return Promise.resolve(undefined); | ||
} | ||
} | ||
|
||
async deleteToken(): Promise<boolean | undefined> { | ||
try { | ||
return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID); | ||
} catch (e) { | ||
// Ignore | ||
Logger.error(`Deleting token failed: ${e}`); | ||
return Promise.resolve(undefined); | ||
} | ||
} | ||
} | ||
|
||
export const keychain = new Keychain(); |
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,55 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
|
||
type LogLevel = 'Trace' | 'Info' | 'Error'; | ||
|
||
class Log { | ||
private output: vscode.OutputChannel; | ||
|
||
constructor() { | ||
this.output = vscode.window.createOutputChannel('GitHub Authentication'); | ||
} | ||
|
||
private data2String(data: any): string { | ||
if (data instanceof Error) { | ||
return data.stack || data.message; | ||
} | ||
if (data.success === false && data.message) { | ||
return data.message; | ||
} | ||
return data.toString(); | ||
} | ||
|
||
public info(message: string, data?: any): void { | ||
this.logLevel('Info', message, data); | ||
} | ||
|
||
public error(message: string, data?: any): void { | ||
this.logLevel('Error', message, data); | ||
} | ||
|
||
public logLevel(level: LogLevel, message: string, data?: any): void { | ||
this.output.appendLine(`[${level} - ${this.now()}] ${message}`); | ||
if (data) { | ||
this.output.appendLine(this.data2String(data)); | ||
} | ||
} | ||
|
||
private now(): string { | ||
const now = new Date(); | ||
return padLeft(now.getUTCHours() + '', 2, '0') | ||
+ ':' + padLeft(now.getMinutes() + '', 2, '0') | ||
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); | ||
} | ||
} | ||
|
||
function padLeft(s: string, n: number, pad = ' ') { | ||
return pad.repeat(Math.max(0, n - s.length)) + s; | ||
} | ||
|
||
const Logger = new Log(); | ||
export default Logger; |
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,73 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { Event, Disposable } from 'vscode'; | ||
|
||
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> { | ||
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); | ||
} | ||
|
||
export function onceEvent<T>(event: Event<T>): Event<T> { | ||
return (listener, thisArgs = null, disposables?) => { | ||
const result = event(e => { | ||
result.dispose(); | ||
return listener.call(thisArgs, e); | ||
}, null, disposables); | ||
|
||
return result; | ||
}; | ||
} | ||
|
||
|
||
export interface PromiseAdapter<T, U> { | ||
( | ||
value: T, | ||
resolve: | ||
(value?: U | PromiseLike<U>) => void, | ||
reject: | ||
(reason: any) => void | ||
): any; | ||
} | ||
|
||
const passthrough = (value: any, resolve: (value?: any) => void) => resolve(value); | ||
|
||
/** | ||
* Return a promise that resolves with the next emitted event, or with some future | ||
* event as decided by an adapter. | ||
* | ||
* If specified, the adapter is a function that will be called with | ||
* `(event, resolve, reject)`. It will be called once per event until it resolves or | ||
* rejects. | ||
* | ||
* The default adapter is the passthrough function `(value, resolve) => resolve(value)`. | ||
* | ||
* @param event the event | ||
* @param adapter controls resolution of the returned promise | ||
* @returns a promise that resolves or rejects as specified by the adapter | ||
*/ | ||
export async function promiseFromEvent<T, U>( | ||
event: Event<T>, | ||
adapter: PromiseAdapter<T, U> = passthrough): Promise<U> { | ||
let subscription: Disposable; | ||
return new Promise<U>((resolve, reject) => | ||
subscription = event((value: T) => { | ||
try { | ||
Promise.resolve(adapter(value, resolve, reject)) | ||
.catch(reject); | ||
} catch (error) { | ||
reject(error); | ||
} | ||
}) | ||
).then( | ||
(result: U) => { | ||
subscription.dispose(); | ||
return result; | ||
}, | ||
error => { | ||
subscription.dispose(); | ||
throw error; | ||
} | ||
); | ||
} |
Oops, something went wrong.