From 467e33bece96be2c07d0cf0939895f0591712a0f Mon Sep 17 00:00:00 2001 From: Michel Kaporin Date: Fri, 16 Jul 2021 14:20:01 +0200 Subject: [PATCH] feat: improve configuration settings UX, increase auth waiting time (#26) --- package.json | 51 ++++++------------ src/snyk/configuration.ts | 57 +++++++++++++------- src/snyk/constants/settings.ts | 11 +++- src/snyk/constants/views.ts | 1 - src/snyk/lib/modules/LoginModule.ts | 11 ++-- src/snyk/lib/watchers/SnykSettingsWatcher.ts | 17 ++++-- 6 files changed, 83 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index cc957b228..ab569fe2e 100644 --- a/package.json +++ b/package.json @@ -46,42 +46,24 @@ { "title": "Snyk Vulnerability Scanner", "properties": { - "snyk.advancedMode": { - "type": "boolean", - "default": false, - "description": "Toggle advanced tools for expert users", - "scope": "application" - }, "snyk.token": { "type": "string", "default": "", "description": "API key", "scope": "application" }, - "snyk.uploadApproved": { - "type": "boolean", - "default": false, - "markdownDescription": "User consent for code upload to Snyk in agreement with our [terms and conditions](https://snyk.io/policies/terms-of-service/?utm_source=vsc). **This setting will be removed soon. Please use `snyk.codeEnabled` instead.**", - "scope": "window" - }, - "snyk.codeEnabled": { - "type": "boolean", - "description": "Snyk Code is enabled. If Snyk organization settings has Snyk Code disabled, this flag will be automatically set to false.", - "scope": "window", - "default": null - }, "snyk.yesCrashReport": { "//": "Name starts with y to put it at the end, as configs are sorted alphbetically", "type": "boolean", "default": true, - "markdownDescription": "Allow crash reports to be reported to Snyk", + "markdownDescription": "Allow crash reports to be reported to Snyk.", "scope": "application" }, "snyk.yesTelemetry": { "//": "Name starts with y to put it at the end, as configs are sorted alphbetically", "type": "boolean", "default": true, - "markdownDescription": "Allow extension's telemetry to be sent to Snyk", + "markdownDescription": "Allow extension's telemetry to be sent to Snyk.", "scope": "application" }, "snyk.yesWelcomeNotification": { @@ -90,6 +72,18 @@ "default": true, "markdownDescription": "Show welcome notification after installation and restart", "scope": "application" + }, + "snyk.advanced.advancedMode": { + "type": "boolean", + "default": false, + "description": "Allows the user to configure if Snyk analysis is run automatically, manually or every 30 minutes. Default is automatically on save.", + "scope": "application" + }, + "snyk.advanced.codeEnabled": { + "type": "boolean", + "markdownDescription": "Mirrors if Snyk Code is enabled or disabled for [your Snyk organisation](https://app.snyk.io/manage/snyk-code). Please do not manually edit this configuration as it might cause unpredictable behaviour.", + "scope": "window", + "default": null } } } @@ -143,24 +137,9 @@ }, { "view": "snyk.views.analysis", - "contents": "Thanks for connecting with Snyk. ✅\n 👉 You are almost set 🤗.", - "when": "!snyk:error && snyk:loggedIn && !snyk:codeEnabled && !snyk:uploadApproved" - }, - { - "view": "snyk.views.analysis", - "contents": "👋 Thank you for using Snyk Code! To minimize the chances of unwanted source code upload, we've introduced a new flag, to control if source code is uploaded from a single place. This flag is valid for your Snyk organization and supersedes previous upload approval.", - "when": "!snyk:error && snyk:loggedIn && !snyk:codeEnabled && snyk:uploadApproved" - }, - { - "view": "snyk.views.analysis", - "contents": "[Enable Snyk Code and start analysing](command:snyk.enableCode 'Upload code to Snyk')", + "contents": "Thanks for connecting with Snyk. ✅\n 👉 You are almost set 🤗.\n[Enable Snyk Code and start analysing](command:snyk.enableCode 'Upload code to Snyk')\nIt looks like your organization's configuration is disabled, that's why you are seeing this message. You can easily enable it by pressing the above button and switching it on.\nWe apologize for the inconvenience and please [contact us](https://snyk.io/contact-us/?utm_source=vsc) if you have any other questions or concerns!", "when": "!snyk:error && snyk:loggedIn && !snyk:codeEnabled" }, - { - "view": "snyk.views.analysis", - "contents": "It looks like your organization's configuration is disabled, that's why you are seeing this message. You can easily enable it by pressing the above button and switching it on.\nWe apologize for the inconvenience and please [contact us](https://snyk.io/contact-us/?utm_source=vsc) if you have any other questions or concerns!", - "when": "!snyk:error && snyk:loggedIn && !snyk:codeEnabled && snyk:uploadApproved" - }, { "view": "snyk.views.analysis", "contents": "Open a workspace or a folder in Visual Studio Code to start the analysis.", diff --git a/src/snyk/configuration.ts b/src/snyk/configuration.ts index 7fa69c3c2..0710f5f03 100644 --- a/src/snyk/configuration.ts +++ b/src/snyk/configuration.ts @@ -1,6 +1,15 @@ import { URL } from 'url'; import * as vscode from 'vscode'; import { IDE_NAME } from './constants/general'; +import { + ADVANCED_ADVANCED_CODE_ENABLED_SETTING, + ADVANCED_ADVANCED_MODE_SETTING, + CONFIGURATION_IDENTIFIER, + TOKEN_SETTING, + YES_CRASH_REPORT_SETTING, + YES_TELEMETRY_SETTING, + YES_WELCOME_NOTIFICATION_SETTING, +} from './constants/settings'; export interface IConfiguration { isDevelopment: boolean; @@ -10,7 +19,6 @@ export interface IConfiguration { snykCodeUrl: string; token: string | undefined; setToken(token: string): Promise; - uploadApproved: boolean; codeEnabled: boolean | undefined; shouldReportErrors: boolean; shouldReportEvents: boolean; @@ -49,57 +57,70 @@ export class Configuration implements IConfiguration { } get token(): string | undefined { - return this.staticToken || this.vscodeWorkspace.getConfiguration('snyk').get('token'); + return ( + this.staticToken || + this.vscodeWorkspace.getConfiguration(CONFIGURATION_IDENTIFIER).get(this.getConfigName(TOKEN_SETTING)) + ); } async setToken(token: string): Promise { this.staticToken = ''; - await this.vscodeWorkspace.getConfiguration('snyk').update('token', token, true); + await this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .update(this.getConfigName(TOKEN_SETTING), token, true); } get source(): string { return this.processEnv.GITPOD_WORKSPACE_ID ? 'gitpod' : IDE_NAME; } - get uploadApproved(): boolean { - return ( - this.staticCodeEnabled || - this.source !== IDE_NAME || - !!this.vscodeWorkspace.getConfiguration('snyk').get('uploadApproved') // TODO: remove once grace period is out - ); - } - get codeEnabled(): boolean | undefined { return ( this.staticCodeEnabled || this.source !== IDE_NAME || - this.vscodeWorkspace.getConfiguration('snyk').get('codeEnabled') + this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .get(this.getConfigName(ADVANCED_ADVANCED_CODE_ENABLED_SETTING)) ); } async setCodeEnabled(value = true): Promise { - await this.vscodeWorkspace.getConfiguration('snyk').update('codeEnabled', value, true); + await this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .update(this.getConfigName(ADVANCED_ADVANCED_CODE_ENABLED_SETTING), value, true); } get shouldReportErrors(): boolean { - return !!this.vscodeWorkspace.getConfiguration('snyk').get('yesCrashReport'); + return !!this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .get(this.getConfigName(YES_CRASH_REPORT_SETTING)); } get shouldReportEvents(): boolean { - return !!this.vscodeWorkspace.getConfiguration('snyk').get('yesTelemetry'); + return !!this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .get(this.getConfigName(YES_TELEMETRY_SETTING)); } get shouldShowWelcomeNotification(): boolean { - return !!this.vscodeWorkspace.getConfiguration('snyk').get('yesWelcomeNotification'); + return !!this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .get(this.getConfigName(YES_WELCOME_NOTIFICATION_SETTING)); } async hideWelcomeNotification(): Promise { - await this.vscodeWorkspace.getConfiguration('snyk').update('yesWelcomeNotification', false, true); + await this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .update(this.getConfigName(YES_WELCOME_NOTIFICATION_SETTING), false, true); } get shouldShowAdvancedView(): boolean { - return !!this.vscodeWorkspace.getConfiguration('snyk').get('advancedMode'); + return !!this.vscodeWorkspace + .getConfiguration(CONFIGURATION_IDENTIFIER) + .get(this.getConfigName(ADVANCED_ADVANCED_MODE_SETTING)); } + + private getConfigName = (setting: string) => setting.replace(`${CONFIGURATION_IDENTIFIER}.`, ''); } export const configuration = new Configuration(); diff --git a/src/snyk/constants/settings.ts b/src/snyk/constants/settings.ts index 8848ee84a..ee547ae7d 100644 --- a/src/snyk/constants/settings.ts +++ b/src/snyk/constants/settings.ts @@ -1 +1,10 @@ -export const REVIEW_RESULTS_SETTINGS = 'snyk.review.results'; +// VS Code configuration settings +// Ensure consistency with package.json when changing these constants +export const CONFIGURATION_IDENTIFIER = 'snyk'; + +export const TOKEN_SETTING = `${CONFIGURATION_IDENTIFIER}.token`; +export const YES_CRASH_REPORT_SETTING = `${CONFIGURATION_IDENTIFIER}.yesCrashReport`; +export const YES_TELEMETRY_SETTING = `${CONFIGURATION_IDENTIFIER}.yesTelemetry`; +export const YES_WELCOME_NOTIFICATION_SETTING = `${CONFIGURATION_IDENTIFIER}.yesWelcomeNotification`; +export const ADVANCED_ADVANCED_MODE_SETTING = `${CONFIGURATION_IDENTIFIER}.advanced.advancedMode`; +export const ADVANCED_ADVANCED_CODE_ENABLED_SETTING = `${CONFIGURATION_IDENTIFIER}.advanced.codeEnabled`; diff --git a/src/snyk/constants/views.ts b/src/snyk/constants/views.ts index d1d055d98..1ed6b8645 100644 --- a/src/snyk/constants/views.ts +++ b/src/snyk/constants/views.ts @@ -8,7 +8,6 @@ export const SNYK_VIEW_SUGGESTION = 'snyk.views.suggestion'; export const SNYK_CONTEXT = { LOGGEDIN: 'loggedIn', AUTHENTICATING: 'authenticating', - APPROVED: 'uploadApproved', // todo: removed once 'uploadApproved' is deprecated CODE_ENABLED: 'codeEnabled', WORKSPACE_FOUND: 'workspaceFound', ERROR: 'error', diff --git a/src/snyk/lib/modules/LoginModule.ts b/src/snyk/lib/modules/LoginModule.ts index 0641b32b0..b5a1d9b47 100644 --- a/src/snyk/lib/modules/LoginModule.ts +++ b/src/snyk/lib/modules/LoginModule.ts @@ -78,15 +78,19 @@ abstract class LoginModule extends ReportModule implements LoginModuleInterface } private async waitLoginConfirmation(draftToken: string): Promise { - // 20 attempts to wait for user's login & consent - for (let i = 0; i < 20; i += 1) { - await sleep(1000); + // 90 seconds to wait for user's authentication + /* eslint-disable no-await-in-loop */ + for (let i = 0; i < 50; i += 1) { + const waitTime = i < 30 ? 1000 : 3000; // wait 1s for the first 30s, then poll each 3s + await sleep(waitTime); const token = await this.checkSession(draftToken); if (token) { return token; } } + /* eslint-enable no-await-in-loop */ + return ''; } @@ -94,7 +98,6 @@ abstract class LoginModule extends ReportModule implements LoginModuleInterface const enabled = await this.snykCode.isEnabled(); await this.contextService.setContext(SNYK_CONTEXT.CODE_ENABLED, enabled); - await this.contextService.setContext(SNYK_CONTEXT.APPROVED, configuration.uploadApproved); // todo: removed once 'uploadApproved' is deprecated if (!enabled) { this.loadAnalytics(); diff --git a/src/snyk/lib/watchers/SnykSettingsWatcher.ts b/src/snyk/lib/watchers/SnykSettingsWatcher.ts index 09a7e7e77..60507fc29 100644 --- a/src/snyk/lib/watchers/SnykSettingsWatcher.ts +++ b/src/snyk/lib/watchers/SnykSettingsWatcher.ts @@ -1,12 +1,17 @@ import * as vscode from 'vscode'; import { ExtensionInterface, SnykWatcherInterface } from '../../../interfaces/SnykInterfaces'; +import { + ADVANCED_ADVANCED_CODE_ENABLED_SETTING, + ADVANCED_ADVANCED_MODE_SETTING, + TOKEN_SETTING, +} from '../../constants/settings'; import { errorsLogs } from '../../messages/errorsServerLogMessages'; class SnykSettingsWatcher implements SnykWatcherInterface { private async onChangeConfiguration(extension: ExtensionInterface, key: string): Promise { - if (key === 'snyk.advancedMode') { + if (key === ADVANCED_ADVANCED_MODE_SETTING) { return extension.checkAdvancedMode(); - } else if (key === 'snyk.codeEnabled') { + } else if (key === ADVANCED_ADVANCED_CODE_ENABLED_SETTING) { void extension.checkCodeEnabled(); return; } @@ -25,9 +30,11 @@ class SnykSettingsWatcher implements SnykWatcherInterface { public activate(extension: ExtensionInterface): void { vscode.workspace.onDidChangeConfiguration( async (event: vscode.ConfigurationChangeEvent): Promise => { - const change = ['snyk.url', 'snyk.token', 'snyk.codeEnabled', 'snyk.advancedMode'].find(config => - event.affectsConfiguration(config), - ); + const change = [ + TOKEN_SETTING, + ADVANCED_ADVANCED_CODE_ENABLED_SETTING, + ADVANCED_ADVANCED_MODE_SETTING, + ].find(config => event.affectsConfiguration(config)); if (change) { try { await this.onChangeConfiguration(extension, change);