-
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.
fix: extension secrets should not be lost when workspace restart (#340)
Signed-off-by: vitaliy-guliy <[email protected]> Co-authored-by: Valerii Svydenko <[email protected]>
- Loading branch information
1 parent
a67cd09
commit a553e5c
Showing
8 changed files
with
183 additions
and
13 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
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,81 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2024 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 { FILE_WORKBENCH } from './files'; | ||
import * as fs from './fs-extra'; | ||
|
||
const SERVER_KEY_MASK = '{{LOCAL-STORAGE}}/{{SECURE-KEY}}'; | ||
|
||
const CERTS_DIR = '/etc/ssh'; | ||
|
||
/** | ||
* Finds a public key in `/etc/ssh` and initializes VS Code with 32 bytes (every fourth character) of the key. | ||
* The key is used to encrypt/decrypt the extension secrets stored in browser local storage. | ||
*/ | ||
export class LocalStorageKeyProvider { | ||
async configure(): Promise<void> { | ||
console.log('# Injecting server public key to che-code...'); | ||
|
||
try { | ||
const publicKeyFile = await this.findPublicKeyFile(); | ||
console.log(` > found key file ${publicKeyFile}`); | ||
|
||
const secret = await this.getPartOfPublicKey(publicKeyFile); | ||
await this.update(FILE_WORKBENCH, SERVER_KEY_MASK, secret); | ||
} catch (err) { | ||
console.error(err.message); | ||
} | ||
} | ||
|
||
async findPublicKeyFile(): Promise<string> { | ||
// Check for public certificates in /public-certs | ||
if (await fs.pathExists(CERTS_DIR)) { | ||
const dir = await fs.readdir(CERTS_DIR); | ||
|
||
for (const item of dir) { | ||
const file = `${CERTS_DIR}/${item}`; | ||
|
||
if (await fs.isFile(file)) { | ||
// check for it's public part | ||
const publicKey = file + '.pub'; | ||
if ((await fs.pathExists(publicKey)) && (await fs.isFile(publicKey))) { | ||
return publicKey; | ||
} | ||
} | ||
} | ||
} | ||
|
||
throw new Error(`Public key file is not found in ${CERTS_DIR}`); | ||
} | ||
|
||
async getPartOfPublicKey(file: string): Promise<string> { | ||
let content = await fs.readFile(file); | ||
content = content.substring(content.indexOf(' ') + 1); | ||
|
||
let secret = ''; | ||
for (let i = 0; i < 32; i++) { | ||
secret += content.charAt(i * 4); | ||
} | ||
|
||
return secret; | ||
} | ||
|
||
async update(file: string, text: string, newText: string): Promise<void> { | ||
const content = await fs.readFile(file); | ||
const newContent = content.replace(text, newText); | ||
|
||
if (content === newContent) { | ||
console.log(` > ${file} is not updated`); | ||
} else { | ||
await fs.writeFile(file, newContent); | ||
console.log(` > ${file} has been updated`); | ||
} | ||
} | ||
} |
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,73 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2024 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 fs from '../src/fs-extra'; | ||
import { LocalStorageKeyProvider } from '../src/local-storage-key-provider'; | ||
|
||
const ORIGIN_WORKBENCH_FILE = ` | ||
some code, some code, a mask to be replaced {{LOCAL-STORAGE}}/{{SECURE-KEY}}, some code | ||
`; | ||
|
||
const NEW_WORKBENCH_FILE = ` | ||
some code, some code, a mask to be replaced 1234567890ABCDEFGHIJKLMNOPQRSTUV, some code | ||
`; | ||
|
||
describe('Test setting of Local Storage public key to VS Code', () => { | ||
beforeEach(() => { | ||
Object.assign(fs, { | ||
pathExists: jest.fn(), | ||
isFile: jest.fn(), | ||
readdir: jest.fn(), | ||
readFile: jest.fn(), | ||
writeFile: jest.fn(), | ||
}); | ||
}); | ||
|
||
test('should return if env.DEVWORKSPACE_ID is not set', async () => { | ||
const pathExistsMock = jest.fn(); | ||
const readdirMock = jest.fn(); | ||
const isFileMock = jest.fn(); | ||
const readFileMock = jest.fn(); | ||
const writeFileMock = jest.fn(); | ||
Object.assign(fs, { | ||
pathExists: pathExistsMock, | ||
readdir: readdirMock, | ||
isFile: isFileMock, | ||
readFile: readFileMock, | ||
writeFile: writeFileMock, | ||
}); | ||
|
||
pathExistsMock.mockImplementation(async (path: string) => { | ||
return '/etc/ssh' === path || '/etc/ssh/first-key.pub' === path; | ||
}); | ||
|
||
readdirMock.mockImplementation(async (path: string) => { | ||
return ['some-file', 'first-key', 'second-key', 'first-key.pub', 'second-key.pub']; | ||
}); | ||
|
||
isFileMock.mockImplementation(async (path: string) => { | ||
return '/etc/ssh/first-key' === path || '/etc/ssh/first-key.pub' === path; | ||
}); | ||
|
||
readFileMock.mockImplementation(async (file: string) => { | ||
switch (file) { | ||
case '/etc/ssh/first-key.pub': | ||
return 'ssh-rsa 1111222233334444555566667777888899990000AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ'; | ||
case 'out/vs/code/browser/workbench/workbench.js': | ||
return ORIGIN_WORKBENCH_FILE; | ||
} | ||
}); | ||
|
||
const localStorageKeyProvider = new LocalStorageKeyProvider(); | ||
await localStorageKeyProvider.configure(); | ||
|
||
expect(writeFileMock).toBeCalledWith('out/vs/code/browser/workbench/workbench.js', NEW_WORKBENCH_FILE); | ||
}); | ||
}); |
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