Skip to content

Commit ac92679

Browse files
authored
Merge pull request #8 from aws-spenceng/staging_1.83.1-pr4
Initialized patches and applied sagemaker-extension.diff
2 parents 0eb3b4c + 4f2605f commit ac92679

File tree

14 files changed

+814
-0
lines changed

14 files changed

+814
-0
lines changed

patched-vscode/build/gulpfile.extensions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const compilations = [
6363
'php-language-features/tsconfig.json',
6464
'search-result/tsconfig.json',
6565
'references-view/tsconfig.json',
66+
'sagemaker-extension/tsconfig.json',
6667
'simple-browser/tsconfig.json',
6768
'tunnel-forwarding/tsconfig.json',
6869
'typescript-language-features/test-workspace/tsconfig.json',

patched-vscode/build/npm/dirs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const dirs = [
3939
'extensions/npm',
4040
'extensions/php-language-features',
4141
'extensions/references-view',
42+
'extensions/sagemaker-extension',
4243
'extensions/search-result',
4344
'extensions/simple-browser',
4445
'extensions/tunnel-forwarding',
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.vscode/**
2+
.vscode-test/**
3+
out/test/**
4+
out/**
5+
test/**
6+
src/**
7+
tsconfig.json
8+
out/test/**
9+
out/**
10+
cgmanifest.json
11+
yarn.lock
12+
preview-src/**
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Sagemaker Extension
2+
3+
The SageMaker Extension alerts users to sign in again before their session expires. It remains active continuously and cannot be disabled.
4+
5+
Session Management: The extension monitors the SageMaker cookie to ensure seamless session continuity. It proactively alerts users to sign in again before the session expires, enhancing the user experience by preventing unexpected session interruptions.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright Amazon.com Inc. or its affiliates. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
//@ts-check
7+
8+
'use strict';
9+
10+
const withBrowserDefaults = require('../shared.webpack.config').browser;
11+
12+
module.exports = withBrowserDefaults({
13+
context: __dirname,
14+
entry: {
15+
extension: './src/extension.ts'
16+
},
17+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright Amazon.com Inc. or its affiliates. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
//@ts-check
7+
8+
'use strict';
9+
10+
const withDefaults = require('../shared.webpack.config');
11+
12+
module.exports = withDefaults({
13+
context: __dirname,
14+
resolve: {
15+
mainFields: ['module', 'main']
16+
},
17+
entry: {
18+
extension: './src/extension.ts',
19+
}
20+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "sagemaker-extension",
3+
"displayName": "Sagemaker Extension",
4+
"description": "Sagemaker Extension",
5+
"extensionKind": [
6+
"workspace"
7+
],
8+
"version": "1.0.0",
9+
"publisher": "sagemaker",
10+
"license": "MIT",
11+
"engines": {
12+
"vscode": "^1.70.0"
13+
},
14+
"main": "./out/extension",
15+
"categories": [
16+
"Other"
17+
],
18+
"activationEvents": [
19+
"*"
20+
],
21+
"capabilities": {
22+
"virtualWorkspaces": true,
23+
"untrustedWorkspaces": {
24+
"supported": true
25+
}
26+
},
27+
"contributes": {
28+
"configuration": {
29+
"type": "object",
30+
"title": "Sagemaker Extension",
31+
"properties": {}
32+
},
33+
"commands": [
34+
]
35+
},
36+
"scripts": {
37+
"compile": "gulp compile-extension:sagemaker-extension",
38+
"watch": "npm run build-preview && gulp watch-extension:sagemaker-extension",
39+
"vscode:prepublish": "npm run build-ext",
40+
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:sagemaker-extension ./tsconfig.json"
41+
},
42+
"dependencies": {
43+
},
44+
"repository": {
45+
}
46+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Constants
2+
export const WARNING_TIME_HEADER = 'Session expiring soon';
3+
4+
export const WARNING_BUTTON_REMIND_ME_IN_5_MINS = 'Remind me in 5 minutes';
5+
export const WARNING_BUTTON_SAVE = 'Save';
6+
export const WARNING_BUTTON_SAVE_AND_RENEW_SESSION = 'Save and renew session';
7+
export const WARNING_TIME_BUTTONS = {
8+
SSO: [WARNING_BUTTON_REMIND_ME_IN_5_MINS, WARNING_BUTTON_SAVE],
9+
IAM: [WARNING_BUTTON_REMIND_ME_IN_5_MINS, WARNING_BUTTON_SAVE_AND_RENEW_SESSION]
10+
};
11+
12+
// Constants for signInWarning
13+
export const SIGN_IN_HEADER = 'Please sign in again';
14+
export const SIGN_IN_MESSAGE = "You were logged out of your account. Choose 'Sign In' to continue using this workplace.";
15+
export const SIGN_IN_MESSAGE_WHEN_REDIRECT_URL_DOES_NOT_EXIST = "You were logged out of your account. You are not able to\n" +
16+
" perform actions in your workplace at this time. Please start a\n" +
17+
" new session.";
18+
export const SIGN_IN_BUTTON = 'Sign In';
19+
export const SSO_MESSAGE = 'To renew the session, log out from Studio App via "File" -> "Log Out" and then "Sign out" from AWS IAM Identity Center (successor to AWS SSO) user portal. Do you want to save all changes now?';
20+
export const IAM_MESSAGE = 'Do you want to renew your session now?'
21+
export enum AUTH_MODE {
22+
SSO = "Sso",
23+
IAM = "Iam"
24+
}
25+
export const FIFTEEN_MINUTES_INTERVAL_MILLIS = 15 * 60 * 1000;
26+
export const FIVE_MINUTES_INTERVAL_MILLIS = 5 * 60 * 1000;
27+
28+
export const SAGEMAKER_METADATA_PATH = '/opt/ml/metadata/resource-metadata.json';
29+
30+
export class SagemakerCookie {
31+
authMode: string
32+
expiryTime: number
33+
ssoExpiryTimestamp: number
34+
studioUserProfileName: string
35+
redirectURL: string
36+
37+
constructor(
38+
authMode: string,
39+
expiryTime: number,
40+
ssoExpiryTimestamp: number,
41+
studioUserProfileName: string,
42+
redirectURL: string
43+
) {
44+
this.authMode = authMode;
45+
this.expiryTime = expiryTime;
46+
this.ssoExpiryTimestamp = ssoExpiryTimestamp
47+
this.studioUserProfileName = studioUserProfileName
48+
this.redirectURL = redirectURL
49+
}
50+
};
51+
52+
export class SagemakerResourceMetadata {
53+
AppType?: string
54+
DomainId?: string
55+
SpaceName?: string
56+
ResourceArn?: string
57+
ResourceName?: string
58+
AppImageVersion?: string
59+
};
60+
export function isSSOMode(cookie: SagemakerCookie) {
61+
return (cookie.authMode === AUTH_MODE.SSO)
62+
}
63+
64+
export function getExpiryTime(cookie: SagemakerCookie): number {
65+
if (AUTH_MODE.SSO === cookie.authMode) {
66+
return cookie.ssoExpiryTimestamp;
67+
} else if (AUTH_MODE.IAM === cookie.authMode) {
68+
return cookie.expiryTime;
69+
} else {
70+
return -1;
71+
}
72+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import * as vscode from 'vscode';
2+
import * as fs from 'fs';
3+
import { SessionWarning } from "./sessionWarning";
4+
import {
5+
FIFTEEN_MINUTES_INTERVAL_MILLIS,
6+
FIVE_MINUTES_INTERVAL_MILLIS,
7+
SAGEMAKER_METADATA_PATH,
8+
SIGN_IN_BUTTON,
9+
WARNING_BUTTON_REMIND_ME_IN_5_MINS,
10+
WARNING_BUTTON_SAVE,
11+
WARNING_BUTTON_SAVE_AND_RENEW_SESSION,
12+
SagemakerCookie,
13+
SagemakerResourceMetadata,
14+
getExpiryTime
15+
} from "./constant";
16+
import * as console from "console";
17+
18+
19+
const PARSE_SAGEMAKER_COOKIE_COMMAND = 'sagemaker.parseCookies';
20+
21+
function showWarningDialog() {
22+
vscode.commands.executeCommand(PARSE_SAGEMAKER_COOKIE_COMMAND).then(response => {
23+
24+
const sagemakerCookie: SagemakerCookie = response as SagemakerCookie
25+
const remainingTime: number = getExpiryTime(sagemakerCookie) - Date.now();
26+
27+
if(!(Object.keys(sagemakerCookie).length === 0)) {
28+
if (getExpiryTime(sagemakerCookie) != null && remainingTime > FIFTEEN_MINUTES_INTERVAL_MILLIS) {
29+
// This means cookie has been reset, reinitializing again
30+
initialize(sagemakerCookie);
31+
} else if (getExpiryTime(sagemakerCookie) != null && remainingTime > 0) {
32+
// READ COOKIE again to decide to show this up
33+
34+
SessionWarning.sessionExpiringWarning(remainingTime, sagemakerCookie)
35+
.then((selection) => {
36+
if (selection === WARNING_BUTTON_REMIND_ME_IN_5_MINS) {
37+
// Trigger the function to show the warning again after 5 minutes.
38+
setTimeout(showWarningDialog, FIVE_MINUTES_INTERVAL_MILLIS);
39+
} else if (selection === WARNING_BUTTON_SAVE) {
40+
saveWorkspace();
41+
} else if (selection === WARNING_BUTTON_SAVE_AND_RENEW_SESSION) {
42+
saveWorkspace();
43+
// Trigger the function to make an API call to renew the session.
44+
renewSession(sagemakerCookie);
45+
}
46+
});
47+
48+
} else {
49+
// this means expiryTime cookie is either invalid or <0
50+
signInError(sagemakerCookie);
51+
}
52+
} else {
53+
// no cookie found so assuming its running locally
54+
}
55+
56+
});
57+
58+
}
59+
60+
function signInError(sagemakerCookie: SagemakerCookie) {
61+
// The session has expired
62+
SessionWarning.signInWarning(sagemakerCookie)
63+
.then((selection) => {
64+
if (selection === SIGN_IN_BUTTON) {
65+
vscode.env.openExternal(vscode.Uri.parse(<string>sagemakerCookie.redirectURL));
66+
}
67+
});
68+
}
69+
70+
function initialize(sagemakerCookie: SagemakerCookie) {
71+
const currentTime = Date.now();
72+
const timeToExpiry = getExpiryTime(sagemakerCookie) - currentTime;
73+
74+
if (timeToExpiry <= 0) {
75+
signInError(sagemakerCookie);
76+
} else if (timeToExpiry >= FIFTEEN_MINUTES_INTERVAL_MILLIS) {
77+
const warningTime = timeToExpiry - FIFTEEN_MINUTES_INTERVAL_MILLIS;
78+
setTimeout(() => {
79+
showWarningDialog();
80+
}, warningTime);
81+
} else {
82+
// If less than or equal to 15 minutes left, set a timer for the remaining time
83+
const warningTime = timeToExpiry % FIVE_MINUTES_INTERVAL_MILLIS;
84+
setTimeout(() => {
85+
showWarningDialog();
86+
}, warningTime);
87+
}
88+
}
89+
90+
function saveWorkspace() {
91+
vscode.workspace.saveAll().then(() => {
92+
// TODO: log workspace saved
93+
});
94+
}
95+
function renewSession(sagemakerCookie: SagemakerCookie) {
96+
// TODO: Log and trigger a Signin
97+
vscode.env.openExternal(vscode.Uri.parse(<string>sagemakerCookie.redirectURL));
98+
// Trigger the function to show the warning again after 5 minutes again to validate.
99+
setTimeout(showWarningDialog, FIVE_MINUTES_INTERVAL_MILLIS);
100+
}
101+
102+
function updateStatusItemWithMetadata(context: vscode.ExtensionContext) {
103+
fs.readFile(SAGEMAKER_METADATA_PATH, 'utf-8', (err, data) => {
104+
if (err) {
105+
// fail silently not to block users
106+
} else {
107+
try {
108+
const jsonData = JSON.parse(data) as SagemakerResourceMetadata;
109+
const spaceName = jsonData.SpaceName;
110+
111+
if (spaceName != null) {
112+
let spaceNameStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
113+
spaceNameStatusBarItem.text = `Space: ${spaceName}`;
114+
spaceNameStatusBarItem.show();
115+
context.subscriptions.push(spaceNameStatusBarItem);
116+
}
117+
} catch (jsonError) {
118+
// fail silently not to block users
119+
}
120+
}
121+
});
122+
}
123+
124+
export function activate(context: vscode.ExtensionContext) {
125+
126+
// TODO: log activation of extension
127+
console.log('Activating Sagemaker Extension...');
128+
129+
// execute the get cookie command and save the data to cookies
130+
vscode.commands.executeCommand(PARSE_SAGEMAKER_COOKIE_COMMAND).then(r => {
131+
132+
const sagemakerCookie: SagemakerCookie = r as SagemakerCookie
133+
134+
initialize(sagemakerCookie);
135+
updateStatusItemWithMetadata(context);
136+
});
137+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as vscode from "vscode";
2+
import {
3+
IAM_MESSAGE,
4+
isSSOMode, SagemakerCookie,
5+
SIGN_IN_BUTTON,
6+
SIGN_IN_HEADER,
7+
SIGN_IN_MESSAGE, SIGN_IN_MESSAGE_WHEN_REDIRECT_URL_DOES_NOT_EXIST, SSO_MESSAGE,
8+
WARNING_TIME_BUTTONS,
9+
WARNING_TIME_HEADER
10+
} from "./constant";
11+
12+
export class SessionWarning {
13+
14+
public static sessionExpiringWarning (warningTime: number, cookie: SagemakerCookie): Thenable<string | undefined> {
15+
// convert warningTime from ms to minutes;
16+
const warningTimeInMinutes: number = Math.floor(warningTime / 60000);
17+
const detail: string = `Your session will expire in ${warningTimeInMinutes} minutes. If your session expires, you could lose unsaved changes \n ${isSSOMode(cookie) ? SSO_MESSAGE : IAM_MESSAGE}`
18+
const sessionExpiringOptions: vscode.MessageOptions = {
19+
detail: detail,
20+
modal: true
21+
};
22+
23+
// Session expiration warning...
24+
if (isSSOMode(cookie)) {
25+
return vscode.window.showWarningMessage(WARNING_TIME_HEADER, sessionExpiringOptions, ...WARNING_TIME_BUTTONS.SSO);
26+
} else {
27+
return vscode.window.showWarningMessage(WARNING_TIME_HEADER, sessionExpiringOptions, ...WARNING_TIME_BUTTONS.IAM);
28+
}
29+
}
30+
31+
public static signInWarning (cookie: SagemakerCookie): Thenable<string | undefined> {
32+
const signInOptions: vscode.MessageOptions = {
33+
detail: cookie.redirectURL ? SIGN_IN_MESSAGE : SIGN_IN_MESSAGE_WHEN_REDIRECT_URL_DOES_NOT_EXIST,
34+
modal: true
35+
};
36+
37+
// SignIn warning...
38+
if (cookie.redirectURL) {
39+
return vscode.window.showErrorMessage(SIGN_IN_HEADER, signInOptions, SIGN_IN_BUTTON);
40+
} else {
41+
return vscode.window.showErrorMessage(SIGN_IN_HEADER, signInOptions);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)