-
Notifications
You must be signed in to change notification settings - Fork 36
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: Roman Nikitenko <rnikiten@redhat.com>
- Loading branch information
1 parent
a73eee1
commit 1aa2f55
Showing
11 changed files
with
249 additions
and
30 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
78 changes: 78 additions & 0 deletions
78
code/extensions/che-github-authentication/src/device-activation.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,78 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2023 Red Hat, Inc. | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
***********************************************************************/ | ||
|
||
/* eslint-disable header/header */ | ||
|
||
import * as k8s from '@kubernetes/client-node'; | ||
import { inject, injectable } from 'inversify'; | ||
import * as vscode from 'vscode'; | ||
import { K8sHelper, base64Encode, createLabelsSelector } from './k8s-helper'; | ||
|
||
const GIT_CREDENTIAL_LABEL = { | ||
'controller.devfile.io/git-credential': 'true' | ||
}; | ||
const SCM_URL_ATTRIBUTE = 'che.eclipse.org/scm-url'; | ||
const GITHUB_URL = 'https://github.com'; | ||
const GIT_CREDENTIALS_LABEL_SELECTOR: string = createLabelsSelector(GIT_CREDENTIAL_LABEL); | ||
|
||
@injectable() | ||
export class DeviceActivation { | ||
|
||
@inject(K8sHelper) | ||
private k8sHelper!: K8sHelper; | ||
|
||
@inject(Symbol.for('GithubServiceInstance')) | ||
private readonly githubService!: { | ||
updateToken(token: string): Promise<void> | ||
} | ||
|
||
async initialize(context: vscode.ExtensionContext): Promise<void> { | ||
// const isDeviceCodeFlowEnabled = context.workspaceState.get('github-authentication.device-code-flow.enabled'); | ||
|
||
vscode.commands.executeCommand('setContext', 'github-authentication.device-code-flow.activation-enabled', true); | ||
context.subscriptions.push( | ||
vscode.commands.registerCommand('github-authentication.device-code-flow.activation', async () => { | ||
const token = await vscode.commands.executeCommand<string>('github-authentication.device-code-flow'); | ||
await this.updateToken(token); | ||
}), | ||
); | ||
} | ||
|
||
protected async getGithubSecret(): Promise<k8s.V1Secret | undefined> { | ||
const gitCredentialSecrets = await this.k8sHelper.getSecret(GIT_CREDENTIALS_LABEL_SELECTOR); | ||
if (gitCredentialSecrets.length === 0) { | ||
return undefined | ||
} | ||
|
||
if (gitCredentialSecrets.length === 1) { | ||
gitCredentialSecrets[0]; | ||
} | ||
return gitCredentialSecrets.find(secret => secret.metadata?.annotations?.[SCM_URL_ATTRIBUTE] === GITHUB_URL); | ||
} | ||
|
||
protected async updateToken(token: string): Promise<void> { | ||
this.githubService.updateToken(token); | ||
|
||
const gitCredentialSecret = await this.getGithubSecret(); | ||
if (!gitCredentialSecret) { | ||
// todo - create a new secret | ||
throw new Error('git credential secret is not found'); | ||
} | ||
|
||
const data = { | ||
credentials: base64Encode(`https://oauth2:${token}@github.com`) | ||
}; | ||
|
||
const newSecret = { ...gitCredentialSecret, data }; | ||
const name = gitCredentialSecret.metadata?.name || 'git-credentials-secret'; | ||
return this.k8sHelper.replaceNamespacedSecret(name, newSecret); | ||
} | ||
} | ||
|
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
100 changes: 100 additions & 0 deletions
100
code/extensions/github-authentication/src/device-flow.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,100 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2023 Red Hat, Inc. | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
***********************************************************************/ | ||
|
||
/* eslint-disable header/header */ | ||
|
||
import * as vscode from 'vscode'; | ||
import { Log } from './common/logger'; | ||
import { crypto } from './node/crypto'; | ||
import { ExtensionHost, GitHubTarget, getFlows } from './flows'; | ||
import { UriEventHandler, AuthProviderType } from './github'; | ||
|
||
interface FlowTriggerOptions { | ||
scopes: string; | ||
baseUri: vscode.Uri; | ||
logger: Log; | ||
redirectUri: vscode.Uri; | ||
nonce: string; | ||
callbackUri: vscode.Uri; | ||
uriHandler: UriEventHandler; | ||
enterpriseUri?: vscode.Uri; | ||
} | ||
|
||
interface Flow { | ||
label: string; | ||
trigger(options: FlowTriggerOptions): Promise<string>; | ||
} | ||
|
||
let logger: Log; | ||
let deviceCodeFlow: Flow | undefined; | ||
let flowOptions: FlowTriggerOptions; | ||
|
||
export async function initialize(context: vscode.ExtensionContext): Promise<void> { | ||
logger = new Log(AuthProviderType.github); | ||
deviceCodeFlow = await getDeviceCodeFlow(); | ||
|
||
if (deviceCodeFlow) { | ||
context.workspaceState.update('github-authentication.device-code-flow.enabled', 'true'); | ||
|
||
context.subscriptions.push( | ||
vscode.commands.registerCommand('github-authentication.device-code-flow', (scopes: string) => { | ||
return getToken(scopes); | ||
}), | ||
); | ||
} else { | ||
console.error('!!! Device Flow initialize !!!! NOT ENABLE '); | ||
} | ||
} | ||
|
||
async function getFlowTriggerOptions(scopes?: string): Promise<FlowTriggerOptions> { | ||
if (flowOptions) { | ||
return scopes ? { ...flowOptions, scopes } : flowOptions; | ||
} | ||
|
||
const nonce: string = crypto.getRandomValues(new Uint32Array(2)).reduce((prev, curr) => prev += curr.toString(16), ''); | ||
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate?nonce=${encodeURIComponent(nonce)}`)); | ||
|
||
flowOptions = { | ||
logger, | ||
nonce, | ||
callbackUri, | ||
scopes: scopes ? scopes : 'user:email', | ||
uriHandler: new UriEventHandler(), | ||
baseUri: vscode.Uri.parse('https://github.com/'), | ||
redirectUri: vscode.Uri.parse('https://vscode.dev/redirect'), | ||
} | ||
return flowOptions; | ||
} | ||
|
||
async function getDeviceCodeFlow(): Promise<Flow | undefined> { | ||
const flows = getFlows({ target: GitHubTarget.DotCom, extensionHost: ExtensionHost.Remote, isSupportedClient: true }); | ||
const filteredFlows = flows.filter(flow => { | ||
const deviceCodeLabel = vscode.l10n.t('device code'); | ||
return flow.label === deviceCodeLabel; | ||
}); | ||
|
||
return filteredFlows.length > 0 ? filteredFlows[0] : undefined; | ||
} | ||
|
||
export async function getToken(scopes: string): Promise<string> { | ||
if (!deviceCodeFlow) { | ||
throw new Error('Device Code Flow is not available'); | ||
} | ||
|
||
const flowOptions = await getFlowTriggerOptions(scopes); | ||
logger.info(`using ${deviceCodeFlow.label} flow with ${flowOptions.scopes} scopes to get token...`); | ||
|
||
const token = await deviceCodeFlow.trigger(flowOptions); | ||
|
||
logger.info(`the token was provided successfully!`); | ||
return token; | ||
} | ||
|
||
|
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
Oops, something went wrong.