Skip to content

Commit

Permalink
feat: support oauth authentication [HEAD-114] (#354)
Browse files Browse the repository at this point in the history
Co-authored-by: Bastian Doetsch <[email protected]>
Co-authored-by: Peter Schäfer <[email protected]>
Co-authored-by: Asaf Agami <[email protected]>
  • Loading branch information
3 people authored May 17, 2023
1 parent 0471b55 commit 73df773
Show file tree
Hide file tree
Showing 33 changed files with 377 additions and 728 deletions.
25 changes: 25 additions & 0 deletions .runUnit/Unit Tests.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Unit Tests" type="mocha-javascript-test-runner">
<node-interpreter>project</node-interpreter>
<node-options />
<mocha-package>$PROJECT_DIR$/node_modules/mocha</mocha-package>
<working-directory>$PROJECT_DIR$/</working-directory>
<pass-parent-env>true</pass-parent-env>
<ui>tdd</ui>
<extra-mocha-options>-c 'out/test/unit/**/*.test.js'</extra-mocha-options>
<test-kind>DIRECTORY</test-kind>
<test-directory>$PROJECT_DIR$/src/test/unit</test-directory>
<recursive>true</recursive>
<method v="2">
<option name="NpmBeforeRunTask" enabled="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="build" />
</scripts>
<node-interpreter value="project" />
<envs />
</option>
</method>
</configuration>
</component>
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Snyk Security - Code and Open Source Dependencies Changelog
## [1.18.3]

### Added
- Added support for OAuth2 authentication
- Snyk Learn: now uses language server to retrieve lessons

## [1.19.1]

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@
{
"id": "snyk.views.analysis.code.security",
"name": "Code Security",
"when": "snyk:lsCodePreview && snyk:loggedIn && snyk:featuresSelected && snyk:codeEnabled && !snyk:codeLocalEngineEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:lsCodePreview && snyk:loggedIn && snyk:codeEnabled && !snyk:codeLocalEngineEnabled && snyk:featuresSelected && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.analysis.configuration",
Expand All @@ -275,17 +275,17 @@
{
"id": "snyk.views.analysis.code.quality",
"name": "Code Quality",
"when": "snyk:lsCodePreview && snyk:loggedIn && snyk:featuresSelected && snyk:codeEnabled && !snyk:codeLocalEngineEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:lsCodePreview && snyk:loggedIn && snyk:codeEnabled && !snyk:codeLocalEngineEnabled && snyk:featuresSelected && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.analysis.code.enablement",
"name": "Code Security & Quality",
"when": "snyk:loggedIn && !snyk:lsCodePreview && snyk:featuresSelected && !snyk:codeEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:loggedIn && snyk:featuresSelected && !snyk:codeEnabled && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.analysis.code.localEngine",
"name": "Code Security & Quality",
"when": "snyk:loggedIn && !snyk:lsCodePreview && snyk:featuresSelected && snyk:codeEnabled && snyk:codeLocalEngineEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:loggedIn && snyk:featuresSelected && snyk:codeEnabled && snyk:codeLocalEngineEnabled && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.support",
Expand Down Expand Up @@ -402,7 +402,7 @@
},
"scripts": {
"vscode:prepublish": "tsc -p ./ && sass media --no-source-map",
"build": "tsc -p ./",
"build": "npm run vscode:prepublish",
"watch": "tsc -watch -p ./",
"watch-resources": "sass media --no-source-map --watch",
"watch-all": "concurrently --kill-others 'npm run watch' 'npm run watch-resources'",
Expand Down
9 changes: 2 additions & 7 deletions src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { AdvisorApiClient, IAdvisorApiClient } from '../../advisor/services/advi
import AdvisorProvider from '../../advisor/services/advisorProvider';
import { AdvisorService } from '../../advisor/services/advisorService';
import { IAnalytics } from '../../common/analytics/itly';
import { ISnykApiClient, SnykApiClient } from '../../common/api/apiСlient';
import { CommandController } from '../../common/commands/commandController';
import { configuration } from '../../common/configuration/instance';
import { IWorkspaceTrust, WorkspaceTrust } from '../../common/configuration/trustedFolders';
Expand All @@ -25,7 +24,7 @@ import { IMarkdownStringAdapter, MarkdownStringAdapter } from '../../common/vsco
import { vsCodeWorkspace } from '../../common/vscode/workspace';
import { IWatcher } from '../../common/watchers/interfaces';
import { ISnykCodeServiceOld } from '../../snykCode/codeServiceOld';
import { CodeSettings, ICodeSettings } from '../../snykCode/codeSettings';
import { ICodeSettings } from '../../snykCode/codeSettings';
import { ISnykCodeErrorHandler, SnykCodeErrorHandler } from '../../snykCode/error/snykCodeErrorHandler';
import { FalsePositiveApi, IFalsePositiveApi } from '../../snykCode/falsePositive/api/falsePositiveApi';
import SnykEditorsWatcher from '../../snykCode/watchers/editorsWatcher';
Expand All @@ -52,7 +51,6 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
protected downloadService: DownloadService;
protected ossService?: OssService;
protected advisorService?: AdvisorProvider;
protected learnService: LearnService;
protected commandController: CommandController;
protected scanModeService: ScanModeService;
protected ossVulnerabilityCountService: OssVulnerabilityCountService;
Expand All @@ -62,7 +60,6 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
protected notificationService: INotificationService;
protected analytics: IAnalytics;

protected snykApiClient: ISnykApiClient;
protected advisorApiClient: IAdvisorApiClient;
protected falsePositiveApi: IFalsePositiveApi;
snykCodeOld: ISnykCodeServiceOld;
Expand All @@ -75,6 +72,7 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
readonly loadingBadge: ILoadingBadge;
protected user: User;
protected experimentService: ExperimentService;
protected learnService: LearnService;
protected snykCodeErrorHandler: ISnykCodeErrorHandler;

protected markdownStringAdapter: IMarkdownStringAdapter;
Expand All @@ -88,8 +86,6 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
this.contextService = new ContextService();
this.openerService = new OpenerService();
this.loadingBadge = new LoadingBadge();
this.learnService = new LearnService(configuration, Logger);
this.snykApiClient = new SnykApiClient(configuration, vsCodeWorkspace, Logger);
this.falsePositiveApi = new FalsePositiveApi(configuration, vsCodeWorkspace, Logger);
this.snykCodeErrorHandler = new SnykCodeErrorHandler(
this.contextService,
Expand All @@ -98,7 +94,6 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
this,
configuration,
);
this.codeSettings = new CodeSettings(this.snykApiClient, this.contextService, configuration, this.openerService);
this.advisorApiClient = new AdvisorApiClient(configuration, Logger);
this.markdownStringAdapter = new MarkdownStringAdapter();
this.workspaceTrust = new WorkspaceTrust();
Expand Down
3 changes: 2 additions & 1 deletion src/snyk/base/modules/snykLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Logger } from '../../common/logger/logger';
import { vsCodeWorkspace } from '../../common/vscode/workspace';
import BaseSnykModule from './baseSnykModule';
import { ISnykLib } from './interfaces';
import { vsCodeCommands } from '../../common/vscode/commands';

export default class SnykLib extends BaseSnykModule implements ISnykLib {
private async runFullScan_(manual = false): Promise<void> {
Expand Down Expand Up @@ -39,7 +40,7 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib {
const workspacePaths = vsCodeWorkspace.getWorkspaceFolders();
await this.setWorkspaceContext(workspacePaths);

await this.user.identify(this.snykApiClient, this.analytics);
await this.user.identify(vsCodeCommands, this.analytics);

if (workspacePaths.length) {
this.logFullAnalysisIsTriggered(manual);
Expand Down
34 changes: 31 additions & 3 deletions src/snyk/base/services/authenticationService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { validate as uuidValidate } from 'uuid';
import { IAnalytics } from '../../common/analytics/itly';
import { IConfiguration } from '../../common/configuration/configuration';
import { DID_CHANGE_CONFIGURATION_METHOD, SNYK_WORKSPACE_SCAN_COMMAND } from '../../common/constants/languageServer';
import { SNYK_WORKSPACE_SCAN_COMMAND } from '../../common/constants/commands';
import { DID_CHANGE_CONFIGURATION_METHOD } from '../../common/constants/languageServer';
import { SNYK_CONTEXT } from '../../common/constants/views';
import { ILog } from '../../common/logger/interfaces';
import { IContextService } from '../../common/services/contextService';
Expand All @@ -12,11 +13,20 @@ import { IBaseSnykModule } from '../modules/interfaces';

export interface IAuthenticationService {
initiateLogin(): Promise<void>;

initiateLogout(): Promise<void>;

setToken(): Promise<void>;

updateToken(token: string): Promise<void>;
}

export type OAuthToken = {
access_token: string;
expiry: string;
refresh_token: string;
};

export class AuthenticationService implements IAuthenticationService {
constructor(
private readonly contextService: IContextService,
Expand Down Expand Up @@ -45,7 +55,7 @@ export class AuthenticationService implements IAuthenticationService {
placeHolder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
password: true,
validateInput: token => {
const valid = uuidValidate(token);
const valid = this.validateToken(token);
if (!valid) {
return 'The entered token has an invalid format.';
}
Expand All @@ -57,11 +67,29 @@ export class AuthenticationService implements IAuthenticationService {
return await this.clientAdapter.getLanguageClient().sendNotification(DID_CHANGE_CONFIGURATION_METHOD, {});
}

validateToken(token: string) {
let valid = uuidValidate(token);
if (valid) return true;

// try to parse as json (oauth2 token)
try {
const oauthToken = JSON.parse(token) as OAuthToken;
valid =
oauthToken.access_token.length > 0 &&
Date.parse(oauthToken.expiry) > Date.now() &&
oauthToken.refresh_token.length > 0;
this.logger.debug(`Token ${token} parsed`);
} catch (e) {
this.logger.warn(`Token ${token} is not a valid uuid or json string: ${e}`);
}
return valid;
}

async updateToken(token: string): Promise<void> {
if (!token) {
await this.initiateLogout();
} else {
if (!uuidValidate(token)) return Promise.reject(new Error('The entered token has an invalid format.'));
if (!this.validateToken(token)) return Promise.reject(new Error('The entered token has an invalid format.'));

await this.configuration.setToken(token);
await this.contextService.setContext(SNYK_CONTEXT.AUTHENTICATING, false);
Expand Down
18 changes: 15 additions & 3 deletions src/snyk/cli/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { getVsCodeProxy } from '../common/proxy';
import { IVSCodeWorkspace } from '../common/vscode/workspace';
import { CLI_INTEGRATION_NAME } from './contants/integration';
import { CliError } from './services/cliService';
import { OAuthToken } from '../base/services/authenticationService';

export class CliProcess {
private readonly successExitCodes = [0, 1];

private runningProcess: ChildProcessWithoutNullStreams | null;

constructor(
Expand Down Expand Up @@ -59,7 +58,6 @@ export class CliProcess {
let env = {
SNYK_INTEGRATION_NAME: CLI_INTEGRATION_NAME,
SNYK_INTEGRATION_VERSION: await Configuration.getVersion(),
SNYK_TOKEN: await this.config.getToken(),
SNYK_API: this.config.snykOssApiEndpoint,
SNYK_CFG_ORG: this.config.organization,
} as NodeJS.ProcessEnv;
Expand All @@ -77,6 +75,20 @@ export class CliProcess {
};
}

const token = await this.config.getToken();
if (token && this.config.snykOssApiEndpoint.indexOf('snykgov.io') > 1) {
const oauthToken = JSON.parse(token) as OAuthToken;
env = {
...env,
SNYK_OAUTH_TOKEN: oauthToken.access_token,
};
} else {
env = {
...env,
SNYK_TOKEN: token,
};
}

return env;
}

Expand Down
3 changes: 2 additions & 1 deletion src/snyk/common/commands/commandController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import { OssIssueCommandArg } from '../../snykOss/views/ossVulnerabilityTreeProv
import { IAnalytics } from '../analytics/itly';
import {
SNYK_INITIATE_LOGIN_COMMAND,
SNYK_LOGIN_COMMAND,
SNYK_OPEN_BROWSER_COMMAND,
SNYK_SET_TOKEN_COMMAND,
SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND,
VSCODE_GO_TO_SETTINGS_COMMAND,
} from '../constants/commands';
import { COMMAND_DEBOUNCE_INTERVAL, IDE_NAME, SNYK_NAME_EXTENSION, SNYK_PUBLISHER } from '../constants/general';
import { SNYK_LOGIN_COMMAND, SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND } from '../constants/languageServer';
import { ErrorHandler } from '../error/errorHandler';
import { ILanguageServer } from '../languageServer/languageServer';
import { CodeIssueData, IacIssueData } from '../languageServer/types';
Expand Down
7 changes: 7 additions & 0 deletions src/snyk/common/constants/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export const SNYK_OPEN_ISSUE_COMMAND = 'snyk.showissue';
export const SNYK_IGNORE_ISSUE_COMMAND = 'snyk.ignoreissue';
export const SNYK_SHOW_OUTPUT_COMMAND = 'snyk.showOutputChannel';
export const SNYK_SHOW_LS_OUTPUT_COMMAND = 'snyk.showLsOutputChannel';
export const SNYK_GET_LESSON_COMMAND = 'snyk.getLearnLesson';
export const SNYK_GET_SETTINGS_SAST_ENABLED = 'snyk.getSettingsSastEnabled';
// commands
export const SNYK_LOGIN_COMMAND = 'snyk.login';
export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan';
export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders';
export const SNYK_GET_ACTIVE_USER = 'snyk.getActiveUser';

// custom Snyk constants used in commands
export const SNYK_CONTEXT_PREFIX = 'snyk:';
7 changes: 1 addition & 6 deletions src/snyk/common/constants/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Language Server name, used e.g. for the output channel
export const SNYK_LANGUAGE_SERVER_NAME = 'Snyk Language Server';
// The internal language server protocol version for custom messages and configuration
export const PROTOCOL_VERSION = 9;
export const PROTOCOL_VERSION = 10;

// LS protocol methods (needed for not having to rely on vscode dependencies in testing)
export const DID_CHANGE_CONFIGURATION_METHOD = 'workspace/didChangeConfiguration';
Expand All @@ -12,8 +12,3 @@ export const SNYK_HAS_AUTHENTICATED = '$/snyk.hasAuthenticated';
export const SNYK_CLI_PATH = '$/snyk.isAvailableCli';
export const SNYK_ADD_TRUSTED_FOLDERS = '$/snyk.addTrustedFolders';
export const SNYK_SCAN = '$/snyk.scan';

// commands
export const SNYK_LOGIN_COMMAND = 'snyk.login';
export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan';
export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders';
25 changes: 0 additions & 25 deletions src/snyk/common/services/cliConfigService.ts

This file was deleted.

Loading

0 comments on commit 73df773

Please sign in to comment.