Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: DH-18583: Saml Login #222

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open

feat: DH-18583: Saml Login #222

wants to merge 21 commits into from

Conversation

bmingles
Copy link
Collaborator

@bmingles bmingles commented Feb 26, 2025

DH-18583: Saml Login

Test Plan

You can use https://jxn-feature-test-saml.int.illumon.com:8123/ for testing Google SAML login (Note that Python isn't currently configured on the server, so you'll have to use Groovy)

e.g.

{
    "url": "https://jxn-feature-test-saml.int.illumon.com:8123/",
    "experimentalWorkerConfig": {
      "heapSize": 0.5,
      "scriptLanguage": "Groovy"
    }
},

Clicking on the DH server node for in VS Code for this server should now prompt with the option to login with Google or Basic Login.

Google login should work in the following scenarios.

  • Logged out of Google - should prompt to login. Once logged in should redirect to VS Code and start a worker in the Connections tree
  • Logged in with 1 user - should automatically redirect without requiring login and create worker
  • Logged in with multiple users - should redirect to user picker in Google. Pick DH / illumon user, should redirect back to VS code and create worker.

Basic Login should work as it always has. You can use iris user on this server.

Copy link

github-actions bot commented Feb 26, 2025

End-to-end Test Summary

Tests 📝Passed ✅Failed ❌Skipped ⏭️Pending ⏳Other ❓Flaky 🍂Duration ⏱️
660000005:09:21
A ctrf plugin

Detailed Test Results

NameStatusmsFlaky 🍂
should default to the correct settingspassed ✅2045
should return custom settings: Empty configspassed ✅338
should return custom settings: Populated configspassed ✅129
should be able to load VSCodepassed ✅1123
should only be visible when a supported file type is active: test.groovypassed ✅2945
should only be visible when a supported file type is active: test.pypassed ✅1030
A ctrf plugin

Failed Test Summary

No failed tests ✨

Flaky Test Summary

No flaky tests detected. ✨

Copy link

github-actions bot commented Feb 26, 2025

Unit Test Summary

Tests 📝Passed ✅Failed ❌Skipped ⏭️Pending ⏳Other ❓Flaky 🍂Duration ⏱️
1281280000000:00:00
A ctrf plugin

Detailed Test Results

NameStatusmsFlaky 🍂
src/dh/dhe.spec.ts: getDheAuthConfig > should return auth config: Undefined passwords config, Full SAML configpassed ✅7
src/dh/dhe.spec.ts: getDheAuthConfig > should return auth config: Undefined password config, Partial SAML configpassed ✅3
src/dh/dhe.spec.ts: getDheAuthConfig > should return auth config: Passwords enabled config, Full SAML configpassed ✅1
src/dh/dhe.spec.ts: getDheAuthConfig > should return auth config: Passwords disabled config, Full SAML configpassed ✅2
src/dh/dhe.spec.ts: getDheAuthConfig > should return auth config: Passwords disabled config, Partial SAML configpassed ✅1
src/services/ConfigService.spec.ts: getCoreServers > should return core servers: Empty configpassed ✅2
src/services/ConfigService.spec.ts: getCoreServers > should return core servers: String configpassed ✅1
src/services/ConfigService.spec.ts: getCoreServers > should return core servers: No labelpassed ✅0
src/services/ConfigService.spec.ts: getCoreServers > should return core servers: Full configpassed ✅0
src/services/ConfigService.spec.ts: getEnterpriseServers > should return enterprise servers: Empty configpassed ✅0
src/services/ConfigService.spec.ts: getEnterpriseServers > should return enterprise servers: String configpassed ✅0
src/services/PollingService.spec.ts: pollUntilTrue > should resolve when poll function returns truepassed ✅8
src/services/PollingService.spec.ts: pollUntilTrue > should cancel polling if timeout exceededpassed ✅2
src/services/PollingService.spec.ts: pollUntilTrue > should cancel polling if cancel explicitly calledpassed ✅1
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > should key data by value equalitypassed ✅2
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > entries > should provide value equality entriespassed ✅2
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > keys > should provide value equality keyspassed ✅1
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > forEach > should pass value equality keys to callback: undefinedpassed ✅2
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > forEach > should pass value equality keys to callback: {}passed ✅1
src/services/SerializedKeyMap.spec.ts: SerializedKeyMap > values > should provide valuespassed ✅1
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: {}passed ✅2
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: testpassed ✅0
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: 999passed ✅0
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: truepassed ✅1
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: falsepassed ✅0
src/util/assertUtil.spec.ts: assertDefined > should not throw if value is defined: 2025-02-27T19:49:08.354Zpassed ✅0
src/util/assertUtil.spec.ts: assertDefined > should throw an error for null or undefined values: nullpassed ✅1
src/util/assertUtil.spec.ts: assertDefined > should throw an error for null or undefined values: undefinedpassed ✅1
src/util/dataUtils.spec.ts: parseSamlScopes > should return null if no SAML scopes are foundpassed ✅1
src/util/dataUtils.spec.ts: parseSamlScopes > should return the SAML scopes if foundpassed ✅0
src/util/promiseUtils.spec.ts: rejectAfterTimeout > should return a Promise that rejects after a given timeoutpassed ✅5
src/util/promiseUtils.spec.ts: rejectAfterTimeout > should clear the timeout when the subscriptions are disposedpassed ✅1
src/util/promiseUtils.spec.ts: waitFor > should return a Promise that resolves after a given timeoutpassed ✅1
src/util/promiseUtils.spec.ts: withResolvers > should return a promise that resolves when resolve function is calledpassed ✅3
src/util/promiseUtils.spec.ts: withResolvers > should return a promise that rejects when reject function is calledpassed ✅1
src/util/serverUtils.spec.ts: getInitialServerStates > should derive server states from configpassed ✅5
src/util/serverUtils.spec.ts: getPipServerUrl > should return a localhost url based on given portpassed ✅1
src/util/serverUtils.spec.ts: parsePort > should parse port from stringpassed ✅0
src/util/serverUtils.spec.ts: parsePort > should throw error when port is not a numberpassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should create temp directory if it does not already exist: true, undefinedpassed ✅4
src/util/tmpUtils.spec.ts: getTempDir > should create temp directory if it does not already exist: true, subDirectorypassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should create temp directory if it does not already exist: false, undefinedpassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should create temp directory if it does not already exist: false, subDirectorypassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should remove directory if recreate is true: true, undefined, /home/runner/work/vscode-deephaven/vscode-deephaven/src/common/tmppassed ✅3
src/util/tmpUtils.spec.ts: getTempDir > should remove directory if recreate is true: true, subDirectory, /home/runner/work/vscode-deephaven/vscode-deephaven/src/common/tmp/subDirectorypassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should remove directory if recreate is true: false, undefined, /home/runner/work/vscode-deephaven/vscode-deephaven/src/common/tmppassed ✅1
src/util/tmpUtils.spec.ts: getTempDir > should remove directory if recreate is true: false, subDirectory, /home/runner/work/vscode-deephaven/vscode-deephaven/src/common/tmp/subDirectorypassed ✅0
src/util/treeViewUtils.spec.ts: getPanelConnectionTreeItem > should return panel connection tree item: isConnected:true, isInitialized:truepassed ✅4
src/util/treeViewUtils.spec.ts: getPanelConnectionTreeItem > should return panel connection tree item: isConnected:true, isInitialized:falsepassed ✅1
src/util/treeViewUtils.spec.ts: getPanelConnectionTreeItem > should return panel connection tree item: isConnected:false, isInitialized:truepassed ✅1
src/util/treeViewUtils.spec.ts: getPanelConnectionTreeItem > should return panel connection tree item: isConnected:false, isInitialized:falsepassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:deephaven.plot.express.DeephavenFigurepassed ✅1
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:deephaven.ui.Elementpassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:Figurepassed ✅2
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:HierarchicalTablepassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:OtherWidgetpassed ✅1
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:pandas.DataFramepassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:PartitionedTablepassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:Tablepassed ✅0
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:TableMappassed ✅1
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:Treemappassed ✅1
src/util/treeViewUtils.spec.ts: getPanelVariableTreeItem > should return panel variable tree item: type:TreeTablepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=true, isManaged=true, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=true, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=true, isManaged=false, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=true, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=false, isManaged=true, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=false, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=false, isManaged=false, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerContextValue > should return contextValue based on server state: isConnected=false, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=0, isManaged=true, label=some labelpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=0, isManaged=true, label=undefinedpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=0, isManaged=false, label=some labelpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=0, isManaged=false, label=undefinedpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=1, isManaged=true, label=some labelpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=1, isManaged=true, label=undefinedpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=1, isManaged=false, label=some labelpassed ✅0
src/util/treeViewUtils.spec.ts: getServerDescription > should return server description based on parameters: connectionCount=1, isManaged=false, label=undefinedpassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupContextValue > should return context value when servers can be managed: group=Managed, canStartServer=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerGroupContextValue > should return context value when servers can be managed: group=Managed, canStartServer=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupContextValue > should return context value when servers can be managed: group=Running, canStartServer=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupContextValue > should return context value when servers can be managed: group=Running, canStartServer=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupTreeItem > should return server group tree item: group=Managed, canStartServer=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerGroupTreeItem > should return server group tree item: group=Managed, canStartServer=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupTreeItem > should return server group tree item: group=Running, canStartServer=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerGroupTreeItem > should return server group tree item: group=Running, canStartServer=falsepassed ✅1
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=true, isManaged=true, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=true, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=true, isManaged=false, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=true, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=false, isManaged=true, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=false, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=false, isManaged=false, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerIconID > should return icon id based on server state: isConnected=false, isManaged=false, isRunning=falsepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=true, isManaged=true, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=true, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=true, isManaged=false, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=true, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=false, isManaged=true, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=false, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=false, isManaged=false, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHC, isConnected=false, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=true, isManaged=true, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=true, isManaged=true, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=true, isManaged=false, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=true, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=false, isManaged=true, isRunning=truepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=false, isManaged=true, isRunning=falsepassed ✅1
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=false, isManaged=false, isRunning=truepassed ✅0
src/util/treeViewUtils.spec.ts: getServerTreeItem > should return server tree item: type=DHE, isConnected=false, isManaged=false, isRunning=falsepassed ✅0
src/util/treeViewUtils.spec.ts: getVariableIconPath > should return icon path for variableTypepassed ✅1
src/util/treeViewUtils.spec.ts: groupServers > should group servers by statepassed ✅1
src/util/uiUtils.spec.ts: createConnectionOption > should return connection option: 'DHC', { label: 'python', url: URL{} }passed ✅4
src/util/uiUtils.spec.ts: createConnectionOption > should return connection option: 'DHC', { label: 'groovy', url: URL{} }passed ✅1
src/util/uiUtils.spec.ts: createConnectionQuickPickOptions > should return quick pick options: editorActiveConnectionUrl=No activepassed ✅15
src/util/uiUtils.spec.ts: createConnectionQuickPickOptions > should return quick pick options: editorActiveConnectionUrl=Active Apassed ✅1
src/util/uiUtils.spec.ts: createConnectionQuickPickOptions > should throw if no servers or connectionspassed ✅2
src/util/uiUtils.spec.ts: createConnectText > should return text and tooltip: 'connecting'passed ✅0
src/util/uiUtils.spec.ts: createConnectText > should return text and tooltip: 'connected'passed ✅0
src/util/uiUtils.spec.ts: createConnectText > should return text and tooltip: 'disconnected'passed ✅0
src/util/uiUtils.spec.ts: updateConnectionStatusBarItem > should update connection status bar item: 'connecting'passed ✅1
src/util/uiUtils.spec.ts: updateConnectionStatusBarItem > should update connection status bar item: 'connected'passed ✅0
src/util/uiUtils.spec.ts: updateConnectionStatusBarItem > should update connection status bar item: 'disconnected'passed ✅0
src/util/uiUtils.spec.ts: createSeparatorPickItem > should create a separator quick pick item with labelpassed ✅1
src/util/urlUtils.spec.ts: urlUtils > should convert url to host_port string: http://localhost:4000, localhost_4000passed ✅2
src/util/urlUtils.spec.ts: urlUtils > should convert url to host_port string: https://localhost:5000, localhost_5000passed ✅0
src/util/urlUtils.spec.ts: urlUtils > should convert url to host_port string: http://www.acme.com:6000, www_acme_com_6000passed ✅1
src/util/urlUtils.spec.ts: urlUtils > should convert url to host_port string: https://www.acme.com:7000, www_acme_com_7000passed ✅0
A ctrf plugin

Failed Test Summary

No failed tests ✨

Flaky Test Summary

No flaky tests detected. ✨

@bmingles bmingles requested review from Copilot and mofojed February 26, 2025 23:35
@bmingles bmingles changed the title DH-18583: Saml Login feat: DH-18583: Saml Login Feb 26, 2025
@bmingles bmingles marked this pull request as ready for review February 26, 2025 23:35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Overview

This PR adds support for SAML login in the extension by integrating SAML authentication flow and UI components. Key changes include:

  • New SAML auth provider implementation with workflow and URI handling.
  • Updates to authentication UI prompts and login flow in the user controller.
  • Enhancements to utility functions and constants to support SAML scopes and session keys.

Reviewed Changes

File Description
src/util/dataUtils.spec.ts Adds tests for SAML scope parsing functionality
src/providers/SamlAuthProvider.ts Introduces SAML auth provider and associated workflow methods
src/services/UriEventHandler.ts Provides a new URI event handler implementation
src/util/idUtils.ts Implements session key generation with a base64-encoded random key
src/util/promiseUtils.ts Adds helper for promise rejection with timeout
src/util/dataUtils.ts Refactors SAML scopes parsing and authentication flow related utils
src/controllers/UserLoginController.ts Updates login workflow to incorporate SAML authentication
src/util/uiUtils.ts Refines UI prompts for multi authentication flows
src/common/constants.ts Adds new constants for SAML provider configuration
src/controllers/ExtensionController.ts Integrates the new SAML auth provider initialization in the extension
src/providers/index.ts Exports the new SAML auth provider for usage by the extension

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (2)

src/util/idUtils.ts:24

  • [nitpick] Consider replacing the custom random char generation with a cryptographically secure random method (e.g. using Node's crypto.randomBytes) to ensure the generated session key has proper entropy.
key += String.fromCharCode(Math.floor(Math.random() * 255));

src/providers/SamlAuthProvider.ts:96

  • Ensure that the URI path is not empty or malformed before calling substring, so that an unexpected URI does not lead to a runtime error.
const stateId = uri.path.substring(1);
* @param authConfig
* @returns The selected auth flow or null if cancelled.
*/
export async function promptForAuthFlow(
Copy link
Collaborator Author

@bmingles bmingles Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just a function + return type rename. Shouldn't be any logic change.

@bmingles bmingles requested a review from Copilot February 27, 2025 16:14

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant