This repository has been archived by the owner on Apr 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Adapt the Secrets plugin API to use kubernetes secrets (#1166)
Rework the Secrets API from upstream theia to use kubernetes secrets
- Loading branch information
Showing
11 changed files
with
338 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,10 @@ | ||
conf | ||
node_modules | ||
dist | ||
coverage | ||
yarn-error.log | ||
.vscode | ||
lib | ||
*.tgz | ||
*.log | ||
.eslintcache |
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 @@ | ||
{ | ||
"name": "@eclipse-che/theia-credentials", | ||
"keywords": [ | ||
"theia-extension" | ||
], | ||
"version": "0.0.1", | ||
"description": "Eclipse Che - Theia credentials", | ||
"dependencies": { | ||
"@theia/core": "next", | ||
"@kubernetes/client-node": "^0.12.1", | ||
"@eclipse-che/theia-remote-impl-che-server": "0.0.1" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"theiaExtensions": [ | ||
{ | ||
"frontend": "lib/browser/credentials-frontend-module" | ||
}, | ||
{ | ||
"backend": "lib/node/che-theia-credentials-backend-module" | ||
} | ||
], | ||
"license": "EPL-2.0", | ||
"files": [ | ||
"lib", | ||
"src", | ||
"scripts", | ||
"conf" | ||
], | ||
"scripts": { | ||
"prepare": "yarn clean && yarn build && yarn test", | ||
"clean": "rimraf lib", | ||
"format": "if-env SKIP_FORMAT=true && echo 'skip format check' || prettier --check '{src,tests}/**/*.ts' package.json", | ||
"format:fix": "prettier --write '{src,tests}/**/*.ts' package.json", | ||
"lint": "if-env SKIP_LINT=true && echo 'skip lint check' || eslint --cache=true --no-error-on-unmatched-pattern=true '{src,tests}/**/*.ts'", | ||
"lint:fix": "eslint --fix --cache=true --no-error-on-unmatched-pattern=true \"{src,tests}/**/*.{ts,tsx}\"", | ||
"compile": "tsc", | ||
"build": "concurrently -n \"format,lint,compile\" -c \"red,green,blue\" \"yarn format\" \"yarn lint\" \"yarn compile\"", | ||
"watch": "tsc -w", | ||
"test": "if-env SKIP_TEST=true && echo 'skip test' || jest --forceExit" | ||
}, | ||
"jest": { | ||
"clearMocks": true, | ||
"collectCoverage": true, | ||
"collectCoverageFrom": [ | ||
"src/**/*.{ts,tsx}" | ||
], | ||
"coverageDirectory": "coverage", | ||
"modulePathIgnorePatterns": [ | ||
"<rootDir>/lib" | ||
], | ||
"preset": "ts-jest" | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
extensions/eclipse-che-theia-credentials/src/browser/che-credentials-service.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,56 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2019-2021 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 | ||
***********************************************************************/ | ||
|
||
import { CredentialsChangeEvent, CredentialsService } from '@theia/core/lib/browser/credentials-service'; | ||
import { Emitter, Event } from '@theia/core'; | ||
import { inject, injectable } from 'inversify'; | ||
|
||
import { CredentialsServer } from '../common/credentials-protocol'; | ||
|
||
@injectable() | ||
export class CheCredentialsService implements CredentialsService { | ||
@inject(CredentialsServer) | ||
private readonly credentialsServer: CredentialsServer; | ||
|
||
private readonly onDidChangePasswordEmitter = new Emitter<CredentialsChangeEvent>(); | ||
readonly onDidChangePassword: Event<CredentialsChangeEvent> = this.onDidChangePasswordEmitter.event; | ||
|
||
async deletePassword(service: string, account: string): Promise<boolean> { | ||
const result = await this.credentialsServer.deletePassword(this.getExtensionId(service), account); | ||
if (result) { | ||
this.onDidChangePasswordEmitter.fire({ service, account }); | ||
} | ||
return result; | ||
} | ||
|
||
findCredentials(service: string): Promise<Array<{ account: string; password: string }>> { | ||
return this.credentialsServer.findCredentials(this.getExtensionId(service)); | ||
} | ||
|
||
findPassword(service: string): Promise<string | undefined> { | ||
return this.credentialsServer.findPassword(this.getExtensionId(service)); | ||
} | ||
|
||
async getPassword(service: string, account: string): Promise<string | undefined> { | ||
const passwordContent = await this.credentialsServer.getPassword(this.getExtensionId(service), account); | ||
if (passwordContent) { | ||
return JSON.stringify(passwordContent); | ||
} | ||
} | ||
|
||
async setPassword(service: string, account: string, password: string): Promise<void> { | ||
await this.credentialsServer.setPassword(this.getExtensionId(service), account, JSON.parse(password)); | ||
this.onDidChangePasswordEmitter.fire({ service, account }); | ||
} | ||
|
||
private getExtensionId(service: string): string { | ||
return service.replace(window.location.hostname + '-', ''); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
extensions/eclipse-che-theia-credentials/src/browser/credentials-frontend-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,26 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 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 | ||
***********************************************************************/ | ||
|
||
import { CREDENTIALS_SERVICE_PATH, CredentialsServer } from '../common/credentials-protocol'; | ||
|
||
import { CheCredentialsService } from './che-credentials-service'; | ||
import { ContainerModule } from 'inversify'; | ||
import { CredentialsService } from '@theia/core/lib/browser/credentials-service'; | ||
import { WebSocketConnectionProvider } from '@theia/core/lib/browser'; | ||
|
||
export default new ContainerModule((bind, unbind, isBound, rebind) => { | ||
bind(CheCredentialsService).toSelf().inSingletonScope(); | ||
rebind(CredentialsService).to(CheCredentialsService).inSingletonScope(); | ||
bind(CredentialsServer) | ||
.toDynamicValue(context => | ||
context.container.get(WebSocketConnectionProvider).createProxy<CredentialsServer>(CREDENTIALS_SERVICE_PATH) | ||
) | ||
.inSingletonScope(); | ||
}); |
25 changes: 25 additions & 0 deletions
25
extensions/eclipse-che-theia-credentials/src/common/credentials-protocol.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,25 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 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 | ||
***********************************************************************/ | ||
|
||
export const CREDENTIALS_SERVICE_PATH = '/services/credentials'; | ||
export const CredentialsServer = Symbol('CredentialsServer'); | ||
|
||
export interface PasswordContent { | ||
extensionId: string; | ||
content: string; | ||
} | ||
|
||
export interface CredentialsServer { | ||
setPassword(service: string, account: string, passwordData: PasswordContent): Promise<void>; | ||
getPassword(service: string, account: string): Promise<PasswordContent | undefined>; | ||
deletePassword(service: string, account: string): Promise<boolean>; | ||
findPassword(service: string): Promise<string | undefined>; | ||
findCredentials(service: string): Promise<Array<{ account: string; password: string }>>; | ||
} |
111 changes: 111 additions & 0 deletions
111
extensions/eclipse-che-theia-credentials/src/node/che-credentials-server.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,111 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 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 | ||
***********************************************************************/ | ||
|
||
import * as k8s from '@kubernetes/client-node'; | ||
|
||
import { CredentialsServer, PasswordContent } from '../common/credentials-protocol'; | ||
import { inject, injectable } from 'inversify'; | ||
|
||
import { CheK8SServiceImpl } from '@eclipse-che/theia-remote-impl-che-server/lib/node/che-server-k8s-service-impl'; | ||
import { CheServerWorkspaceServiceImpl } from '@eclipse-che/theia-remote-impl-che-server/lib/node/che-server-workspace-service-impl'; | ||
|
||
@injectable() | ||
export class CheCredentialsServer implements CredentialsServer { | ||
@inject(CheK8SServiceImpl) | ||
private readonly cheK8SService: CheK8SServiceImpl; | ||
|
||
@inject(CheServerWorkspaceServiceImpl) | ||
private readonly workspaceService: CheServerWorkspaceServiceImpl; | ||
|
||
private readonly CREDENTIALS_SECRET_NAME = 'workspace-credentials-secret'; | ||
private readonly INFRASTRUCTURE_NAMESPACE = 'infrastructureNamespace'; | ||
|
||
async deletePassword(service: string, account: string): Promise<boolean> { | ||
try { | ||
const patch = [ | ||
{ | ||
op: 'remove', | ||
path: `/data/${this.getSecretDataItemName(service, account)}`, | ||
}, | ||
]; | ||
const client = this.cheK8SService.makeApiClient(k8s.CoreV1Api); | ||
client.defaultHeaders = { Accept: 'application/json', 'Content-Type': k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH }; | ||
await client.patchNamespacedSecret(this.CREDENTIALS_SECRET_NAME, await this.getWorkspaceNamespace(), patch); | ||
return true; | ||
} catch (e) { | ||
console.error(e); | ||
return false; | ||
} | ||
} | ||
|
||
async findCredentials(service: string): Promise<Array<{ account: string; password: string }>> { | ||
const secret = await this.cheK8SService | ||
.makeApiClient(k8s.CoreV1Api) | ||
.readNamespacedSecret(this.CREDENTIALS_SECRET_NAME, await this.getWorkspaceNamespace()); | ||
const data = secret.body.data; | ||
return data | ||
? Object.keys(data) | ||
.filter(key => key.startsWith(service)) | ||
.map(key => ({ | ||
account: key.substring(key.indexOf('_') + 1), | ||
password: Buffer.from(data[key], 'base64').toString('ascii'), | ||
})) | ||
: []; | ||
} | ||
|
||
async findPassword(service: string): Promise<string | undefined> { | ||
const secret = await this.cheK8SService | ||
.makeApiClient(k8s.CoreV1Api) | ||
.readNamespacedSecret(this.CREDENTIALS_SECRET_NAME, await this.getWorkspaceNamespace()); | ||
const data = secret.body.data; | ||
if (data) { | ||
const result = Object.keys(data).find(key => key.startsWith(service)); | ||
if (result) { | ||
return Buffer.from(data[result], 'base64').toString('ascii'); | ||
} | ||
} | ||
} | ||
|
||
async getPassword(service: string, account: string): Promise<PasswordContent | undefined> { | ||
const secret = await this.cheK8SService | ||
.makeApiClient(k8s.CoreV1Api) | ||
.readNamespacedSecret(this.CREDENTIALS_SECRET_NAME, await this.getWorkspaceNamespace()); | ||
const data = secret.body.data; | ||
if (data && data[this.getSecretDataItemName(service, account)]) { | ||
return { | ||
extensionId: service, | ||
content: Buffer.from(secret.body.data![this.getSecretDataItemName(service, account)], 'base64').toString( | ||
'ascii' | ||
), | ||
}; | ||
} | ||
} | ||
|
||
async setPassword(service: string, account: string, password: PasswordContent): Promise<void> { | ||
const client = this.cheK8SService.makeApiClient(k8s.CoreV1Api); | ||
client.defaultHeaders = { | ||
Accept: 'application/json', | ||
'Content-Type': k8s.PatchUtils.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, | ||
}; | ||
await client.patchNamespacedSecret(this.CREDENTIALS_SECRET_NAME, await this.getWorkspaceNamespace(), { | ||
data: { [this.getSecretDataItemName(service, account)]: Buffer.from(password.content).toString('base64') }, | ||
}); | ||
} | ||
|
||
private getSecretDataItemName(service: string, account: string): string { | ||
return `${service}_${account}`; | ||
} | ||
|
||
private async getWorkspaceNamespace(): Promise<string> { | ||
// grab current workspace | ||
const workspace = await this.workspaceService.currentWorkspace(); | ||
return workspace.attributes?.[this.INFRASTRUCTURE_NAMESPACE] || workspace.namespace || ''; | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
extensions/eclipse-che-theia-credentials/src/node/che-theia-credentials-backend-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) 2019-2021 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 | ||
***********************************************************************/ | ||
|
||
import { CREDENTIALS_SERVICE_PATH, CredentialsServer } from '../common/credentials-protocol'; | ||
import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core'; | ||
|
||
import { CheCredentialsServer } from './che-credentials-server'; | ||
import { ContainerModule } from 'inversify'; | ||
|
||
export default new ContainerModule(bind => { | ||
bind(CredentialsServer).to(CheCredentialsServer).inSingletonScope(); | ||
bind(ConnectionHandler) | ||
.toDynamicValue( | ||
context => new JsonRpcConnectionHandler(CREDENTIALS_SERVICE_PATH, () => context.container.get(CredentialsServer)) | ||
) | ||
.inSingletonScope(); | ||
}); |
13 changes: 13 additions & 0 deletions
13
extensions/eclipse-che-theia-credentials/tests/no-op.spec.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,13 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 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 | ||
***********************************************************************/ | ||
|
||
describe('no-op', function () { | ||
it('no-op', function () {}); | ||
}); |
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,14 @@ | ||
{ | ||
"extends": "../../configs/base.tsconfig.json", | ||
"compilerOptions": { | ||
"lib": [ | ||
"es6", | ||
"dom" | ||
], | ||
"rootDir": "src", | ||
"outDir": "lib" | ||
}, | ||
"include": [ | ||
"src" | ||
] | ||
} |
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