Skip to content

Commit b1cda5f

Browse files
committed
refactor key class
1 parent dc2b756 commit b1cda5f

File tree

4 files changed

+26
-46
lines changed

4 files changed

+26
-46
lines changed

packages/core/src/awsService/ec2/model.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import * as vscode from 'vscode'
6-
import * as path from 'path'
76
import { Session } from 'aws-sdk/clients/ssm'
87
import { IAM, SSM } from 'aws-sdk'
98
import { Ec2Selection } from './prompter'
@@ -28,7 +27,6 @@ import { CancellationError, Timeout } from '../../shared/utilities/timeoutUtils'
2827
import { showMessageWithCancel } from '../../shared/utilities/messages'
2928
import { SshConfig, sshLogFileLocation } from '../../shared/sshConfig'
3029
import { SshKeyPair } from './sshKeyPair'
31-
import globals from '../../shared/extensionGlobals'
3230

3331
export type Ec2ConnectErrorCode = 'EC2SSMStatus' | 'EC2SSMPermission' | 'EC2SSMConnect' | 'EC2SSMAgentStatus'
3432

@@ -235,8 +233,7 @@ export class Ec2ConnectionManager {
235233
}
236234

237235
public async configureSshKeys(selection: Ec2Selection, remoteUser: string): Promise<SshKeyPair> {
238-
const keyPath = path.join(globals.context.globalStorageUri.fsPath, `aws-ec2-key`)
239-
const keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
236+
const keyPair = await SshKeyPair.getSshKeyPair(`aws-ec2-key`, 30000)
240237
await this.sendSshKeyToInstance(selection, keyPair, remoteUser)
241238
return keyPair
242239
}

packages/core/src/awsService/ec2/sshKeyPair.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,27 @@ export class SshKeyPair {
2020
private readonly keyPath: string,
2121
lifetime: number
2222
) {
23-
this.publicKeyPath = `${keyPath}.pub`
23+
this.publicKeyPath = `${this.keyPath}.pub`
2424
this.lifeTimeout = new Timeout(lifetime)
2525

2626
this.lifeTimeout.onCompletion(async () => {
2727
await this.delete()
2828
})
2929
}
3030

31-
public static async getSshKeyPair(keyPath: string, lifetime: number) {
32-
SshKeyPair.assertValidKeypath(
33-
keyPath,
34-
`ec2: unable to generate key outside of global storage in path ${keyPath}`
35-
)
31+
private static getKeypath(keyName: string): string {
32+
return path.join(globals.context.globalStorageUri.fsPath, keyName)
33+
}
34+
35+
public static async getSshKeyPair(keyName: string, lifetime: number) {
36+
const keyPath = SshKeyPair.getKeypath(keyName)
3637
await SshKeyPair.generateSshKeyPair(keyPath)
3738
return new SshKeyPair(keyPath, lifetime)
3839
}
3940

4041
private static isValidKeyPath(keyPath: string): boolean {
4142
const relative = path.relative(globals.context.globalStorageUri.fsPath, keyPath)
42-
return relative !== undefined && !relative.startsWith('..') && !path.isAbsolute(relative)
43+
return relative !== undefined && !relative.startsWith('..') && !path.isAbsolute(relative) && keyPath.length > 4
4344
}
4445

4546
private static assertValidKeypath(keyPath: string, message: string): void | never {

packages/core/src/test/awsService/ec2/model.test.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import { ToolkitError } from '../../../shared/errors'
1313
import { IAM } from 'aws-sdk'
1414
import { SshKeyPair } from '../../../awsService/ec2/sshKeyPair'
1515
import { DefaultIamClient } from '../../../shared/clients/iamClient'
16-
import path from 'path'
17-
import { fs, globals } from '../../../shared'
1816

1917
describe('Ec2ConnectClient', function () {
2018
let client: Ec2ConnectionManager
@@ -138,15 +136,11 @@ describe('Ec2ConnectClient', function () {
138136
instanceId: 'test-id',
139137
region: 'test-region',
140138
}
141-
const temporaryDirectory = path.join(globals.context.globalStorageUri.fsPath, 'ModelTests')
142-
await fs.mkdir(temporaryDirectory)
143139

144-
const keys = await SshKeyPair.getSshKeyPair(path.join(temporaryDirectory, 'key'), 30000)
140+
const keys = await SshKeyPair.getSshKeyPair('key', 30000)
145141
await client.sendSshKeyToInstance(testSelection, keys, 'test-user')
146142
sinon.assert.calledWith(sendCommandStub, testSelection.instanceId, 'AWS-RunShellScript')
147143
sinon.restore()
148-
149-
await fs.delete(temporaryDirectory, { force: true, recursive: true })
150144
})
151145
})
152146

packages/core/src/test/awsService/ec2/sshKeyPair.test.ts

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import * as vscode from 'vscode'
65
import assert from 'assert'
76
import nodefs from 'fs' // eslint-disable-line no-restricted-imports
87
import * as sinon from 'sinon'
9-
import * as path from 'path'
108
import * as os from 'os'
119
import { SshKeyPair } from '../../../awsService/ec2/sshKeyPair'
1210
import { installFakeClock } from '../../testUtil'
@@ -15,44 +13,38 @@ import { ChildProcess } from '../../../shared/utilities/processUtils'
1513
import { fs, globals } from '../../../shared'
1614

1715
describe('SshKeyUtility', async function () {
18-
let temporaryDirectory: string
19-
let keyPath: string
20-
let keyPair: SshKeyPair
2116
let clock: InstalledClock
17+
let keyPair: SshKeyPair
18+
let keyName: string
2219

2320
before(async function () {
24-
// Setup a temporary directory inside of globalStorage since keys need to be inside globalStorage
25-
temporaryDirectory = path.join(globals.context.globalStorageUri.fsPath, 'SshKeyUtilityTests')
26-
await fs.mkdir(temporaryDirectory)
27-
28-
keyPath = path.join(temporaryDirectory, 'testKeyPair')
2921
clock = installFakeClock()
3022
})
3123

3224
beforeEach(async function () {
33-
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
25+
keyName = 'testKeyPair'
26+
keyPair = await SshKeyPair.getSshKeyPair(keyName, 30000)
3427
})
3528

3629
afterEach(async function () {
3730
await keyPair.delete()
3831
})
3932

4033
after(async function () {
41-
await fs.delete(temporaryDirectory, { recursive: true })
4234
clock.uninstall()
4335
sinon.restore()
4436
})
4537

4638
it('generates key in target file', async function () {
47-
const contents = await fs.readFileBytes(vscode.Uri.file(keyPath))
39+
const contents = await fs.readFileBytes(keyPair.getPrivateKeyPath())
4840
assert.notStrictEqual(contents.length, 0)
4941
})
5042

5143
it('generates unique key each time', async function () {
52-
const beforeContent = await fs.readFileBytes(vscode.Uri.file(keyPath))
53-
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
54-
const afterContent = await fs.readFileBytes(vscode.Uri.file(keyPath))
55-
assert.notStrictEqual(beforeContent, afterContent)
44+
const keyPair2 = await SshKeyPair.getSshKeyPair(`${keyName}2`, 30000)
45+
const content1 = await fs.readFileBytes(keyPair2.getPrivateKeyPath())
46+
const content2 = await fs.readFileBytes(keyPair.getPrivateKeyPath())
47+
assert.notStrictEqual(content1, content2)
5648
})
5749

5850
it('sets permission of the file to read/write owner', async function () {
@@ -63,7 +55,7 @@ describe('SshKeyUtility', async function () {
6355
})
6456

6557
it('defaults to ed25519 key type', async function () {
66-
const process = new ChildProcess(`ssh-keygen`, ['-vvv', '-l', '-f', keyPath])
58+
const process = new ChildProcess(`ssh-keygen`, ['-vvv', '-l', '-f', keyPair.getPrivateKeyPath()])
6759
const result = await process.run()
6860
// Check private key header for algorithm name
6961
assert.strictEqual(result.stdout.includes('[ED25519 256]'), true)
@@ -74,29 +66,25 @@ describe('SshKeyUtility', async function () {
7466
const stub = sinon.stub(SshKeyPair, 'tryKeyGen')
7567
stub.onFirstCall().resolves(false)
7668
stub.callThrough()
77-
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
78-
const process = new ChildProcess(`ssh-keygen`, ['-vvv', '-l', '-f', keyPath])
69+
const rsaKey = await SshKeyPair.getSshKeyPair('rsa', 30000)
70+
const process = new ChildProcess(`ssh-keygen`, ['-vvv', '-l', '-f', rsaKey.getPrivateKeyPath()])
7971
const result = await process.run()
8072
// Check private key header for algorithm name
8173
assert.strictEqual(result.stdout.includes('[RSA'), true)
8274
stub.restore()
8375
})
8476

85-
it('properly names the public key', function () {
86-
assert.strictEqual(keyPair.getPublicKeyPath(), `${keyPath}.pub`)
87-
})
88-
8977
it('reads in public ssh key that is non-empty', async function () {
9078
const key = await keyPair.getPublicKey()
9179
assert.notStrictEqual(key.length, 0)
9280
})
9381

9482
it('does overwrite existing keys on get call', async function () {
9583
const generateStub = sinon.spy(SshKeyPair, 'generateSshKeyPair')
96-
const keyBefore = await fs.readFileBytes(vscode.Uri.file(keyPath))
97-
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 30000)
84+
const keyBefore = await fs.readFileBytes(keyPair.getPrivateKeyPath())
85+
keyPair = await SshKeyPair.getSshKeyPair(keyName, 30000)
9886

99-
const keyAfter = await fs.readFileBytes(vscode.Uri.file(keyPath))
87+
const keyAfter = await fs.readFileBytes(keyPair.getPrivateKeyPath())
10088
sinon.assert.calledOnce(generateStub)
10189

10290
assert.notStrictEqual(keyBefore, keyAfter)
@@ -122,7 +110,7 @@ describe('SshKeyUtility', async function () {
122110
sinon.stub(SshKeyPair, 'generateSshKeyPair')
123111
const deleteStub = sinon.stub(SshKeyPair.prototype, 'delete')
124112

125-
keyPair = await SshKeyPair.getSshKeyPair(keyPath, 50)
113+
keyPair = await SshKeyPair.getSshKeyPair(keyName, 50)
126114
await clock.tickAsync(10)
127115
sinon.assert.notCalled(deleteStub)
128116
await clock.tickAsync(100)

0 commit comments

Comments
 (0)