diff --git a/runtimes/protocol/identity-management.ts b/runtimes/protocol/identity-management.ts index 69f93bc5..d96cc783 100644 --- a/runtimes/protocol/identity-management.ts +++ b/runtimes/protocol/identity-management.ts @@ -141,7 +141,7 @@ export interface ListProfilesResult { // Potential error codes: E_UNKNOWN | E_TIMEOUT | E_RUNTIME_NOT_SUPPORTED | E_CANNOT_READ_SHARED_CONFIG export const listProfilesRequestType = new ProtocolRequestType< ListProfilesParams, - ListProfilesResult, + ListProfilesResult | string, never, AwsResponseError, void @@ -400,3 +400,13 @@ export interface StsCredentialChangedParams { export const stsCredentialChangedRequestType = new ProtocolNotificationType( 'aws/identity/stsCredentialChanged' ) + +// profileChanged +export interface ProfileChangedParams { + profiles: Profile[] + ssoSessions: SsoSession[] +} + +export const profileChangedRequestType = new ProtocolNotificationType( + 'aws/identity/stsCredentialChanged' +) diff --git a/runtimes/runtimes/auth/standalone/encryption.ts b/runtimes/runtimes/auth/standalone/encryption.ts index f5f3475b..001edcbd 100644 --- a/runtimes/runtimes/auth/standalone/encryption.ts +++ b/runtimes/runtimes/auth/standalone/encryption.ts @@ -1,5 +1,5 @@ import { Readable } from 'stream' -import { CompactEncrypt } from 'jose' +import { compactDecrypt, CompactEncrypt } from 'jose' import { GetIamCredentialResult, GetSsoTokenResult } from '../../../protocol' export function shouldWaitForEncryptionKey(): boolean { @@ -99,6 +99,15 @@ export function encryptObjectWithKey(request: Object, key: string, alg?: string, .encrypt(keyBuffer) } +/** + * Decrypt an object with the provided key + */ +export async function decryptObjectWithKey(request: string, key: string): Promise { + const keyBuffer = Buffer.from(key, 'base64') + const result = await compactDecrypt(request, keyBuffer) + return JSON.parse(new TextDecoder().decode(result.plaintext)) as T +} + /** * Encrypts the SSO access tokens inside the result object with the provided key */ @@ -124,12 +133,12 @@ export async function encryptIamResultWithKey( request: GetIamCredentialResult, key: string ): Promise { + const { accessKeyId, secretAccessKey, sessionToken, expiration } = request.credential.credentials request.credential.credentials = { - accessKeyId: await encryptObjectWithKey(request.credential.credentials.accessKeyId, key), - secretAccessKey: await encryptObjectWithKey(request.credential.credentials.secretAccessKey, key), - ...(request.credential.credentials.sessionToken - ? { sessionToken: await encryptObjectWithKey(request.credential.credentials.sessionToken, key) } - : {}), + accessKeyId: await encryptObjectWithKey(accessKeyId, key), + secretAccessKey: await encryptObjectWithKey(secretAccessKey, key), + sessionToken: sessionToken ? await encryptObjectWithKey(sessionToken, key) : undefined, + expiration: expiration, } if (!request.updateCredentialsParams.encrypted) { request.updateCredentialsParams.data = await encryptObjectWithKey( diff --git a/runtimes/runtimes/base-runtime.ts b/runtimes/runtimes/base-runtime.ts index 17591ba0..f1834491 100644 --- a/runtimes/runtimes/base-runtime.ts +++ b/runtimes/runtimes/base-runtime.ts @@ -94,6 +94,7 @@ import { updateProfileRequestType, stsCredentialChangedRequestType, getMfaCodeRequestType, + profileChangedRequestType, } from '../protocol/identity-management' import { IdentityManagement } from '../server-interface/identity-management' import { WebBase64Encoding } from './encoding' @@ -218,6 +219,7 @@ export const baseRuntime = (connections: { reader: MessageReader; writer: Messag onInvalidateStsCredential: handler => lspConnection.onRequest(invalidateStsCredentialRequestType, handler), sendSsoTokenChanged: params => lspConnection.sendNotification(ssoTokenChangedRequestType, params), sendStsCredentialChanged: params => lspConnection.sendNotification(stsCredentialChangedRequestType, params), + sendProfileChanged: params => lspConnection.sendNotification(profileChangedRequestType, params), sendGetMfaCode: params => lspConnection.sendRequest(getMfaCodeRequestType, params), } diff --git a/runtimes/runtimes/standalone.ts b/runtimes/runtimes/standalone.ts index 6e10e85f..9137e504 100644 --- a/runtimes/runtimes/standalone.ts +++ b/runtimes/runtimes/standalone.ts @@ -34,9 +34,13 @@ import { ShowOpenDialogRequestType, stsCredentialChangedRequestType, getMfaCodeRequestType, + profileChangedRequestType, + UpdateProfileParams, + ListProfilesParams, } from '../protocol' import { ProposedFeatures, createConnection } from 'vscode-languageserver/node' import { + decryptObjectWithKey, encryptIamResultWithKey, EncryptionInitialization, encryptObjectWithKey, @@ -304,8 +308,26 @@ export const standalone = (props: RuntimeProps) => { } const identityManagement: IdentityManagement = { - onListProfiles: handler => lspConnection.onRequest(listProfilesRequestType, handler), - onUpdateProfile: handler => lspConnection.onRequest(updateProfileRequestType, handler), + onListProfiles: handler => + lspConnection.onRequest( + listProfilesRequestType, + async (params: ListProfilesParams, token: CancellationToken) => { + let result = await handler(params, token) + if (result && !(result instanceof Error) && encryptionKey) { + return await encryptObjectWithKey(result, encryptionKey) + } + return result + } + ), + onUpdateProfile: handler => + lspConnection.onRequest( + updateProfileRequestType, + async (params: UpdateProfileParams | string, token: CancellationToken) => { + const decrypted: UpdateProfileParams = + typeof params === 'string' ? await decryptObjectWithKey(params, encryptionKey!) : params + return await handler(decrypted, token) + } + ), onGetSsoToken: handler => lspConnection.onRequest( getSsoTokenRequestType, @@ -332,6 +354,10 @@ export const standalone = (props: RuntimeProps) => { onInvalidateStsCredential: handler => lspConnection.onRequest(invalidateStsCredentialRequestType, handler), sendSsoTokenChanged: params => lspConnection.sendNotification(ssoTokenChangedRequestType, params), sendStsCredentialChanged: params => lspConnection.sendNotification(stsCredentialChangedRequestType, params), + sendProfileChanged: async params => { + const encrypted = encryptionKey ? await encryptObjectWithKey(params, encryptionKey) : params + lspConnection.sendNotification(profileChangedRequestType, encrypted) + }, sendGetMfaCode: params => lspConnection.sendRequest(getMfaCodeRequestType, params), } diff --git a/runtimes/server-interface/identity-management.ts b/runtimes/server-interface/identity-management.ts index 6e0de534..f03d9dae 100644 --- a/runtimes/server-interface/identity-management.ts +++ b/runtimes/server-interface/identity-management.ts @@ -16,6 +16,7 @@ import { UpdateProfileParams, UpdateProfileResult, GetMfaCodeResult, + ProfileChangedParams, } from '../protocol/identity-management' import { RequestHandler } from '../protocol' @@ -54,5 +55,7 @@ export type IdentityManagement = { sendStsCredentialChanged: (params: StsCredentialChangedParams) => void + sendProfileChanged: (params: ProfileChangedParams) => void + sendGetMfaCode: (params: GetMfaCodeParams) => Promise }