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

Carlosguerravz/devcerts check #5589

Merged
merged 16 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions debugger-launchjson.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,16 @@ Example:
```json
"targetArchitecture": "arm64"
```

## Check for DevCert
If unspecified, it will be enabled when `serverReadyAction` is set AND when `pipeTransport` is NOT set.
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved

When `true` and if VS is runing on Windows or MacOS, the extension will try to find if your system contains a development certificate by running `dotnet dev-certs https --check`, if no certs are found it will prompt the user to suggest creating them. If approved by the user, the extension will run `dotnet dev-certs https --trust` to create self signed certificates.
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved

You can override this behavior by setting `checkForDevCert` to false in your `launch.json`.

Example:

```json
"checkForDevCert": "false"
```
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1982,6 +1982,11 @@
"targetArchitecture": {
"type": "string",
"description": "[Only supported in local macOS debugging] The architecture of the debuggee. This will automatically be detected unless this parameter is set. Allowed values are x86_64 or arm64."
},
"checkForDevCert": {
"type": "boolean",
"description": "When true, the presence of dev certificates will be checked by runing dotnet `dev-certs https --check`, and if that fails the user will be prompted to create dev certs by running `dotnet dev-certs https --trust`",
"default": true
}
}
},
Expand Down Expand Up @@ -3109,6 +3114,11 @@
"targetArchitecture": {
"type": "string",
"description": "[Only supported in local macOS debugging] The architecture of the debuggee. This will automatically be detected unless this parameter is set. Allowed values are x86_64 or arm64."
},
"checkForDevCert": {
"type": "boolean",
"description": "When true, the presence of dev certificates will be checked by runing dotnet `dev-certs https --check`, and if that fails the user will be prompted to create dev certs by running `dotnet dev-certs https --trust`",
"default": true
}
}
},
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr-debug/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export async function activate(thisExtension: vscode.Extension<CSharpExtensionEx
}

const factory = new DebugAdapterExecutableFactory(debugUtil, platformInformation, eventStream, thisExtension.packageJSON, thisExtension.extensionPath, options);
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('coreclr', new DotnetDebugConfigurationProvider(platformInformation)));
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('clr', new DotnetDebugConfigurationProvider(platformInformation)));
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('coreclr', new DotnetDebugConfigurationProvider(platformInformation, options)));
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('clr', new DotnetDebugConfigurationProvider(platformInformation, options)));
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('coreclr', factory));
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('clr', factory));
}
Expand Down
46 changes: 44 additions & 2 deletions src/coreclr-debug/debugConfigurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import * as vscode from 'vscode';

import { RemoteAttachPicker, DotNetAttachItemsProviderFactory, AttachPicker, AttachItem } from '../features/processPicker';
import { Options } from '../omnisharp/options';
import { PlatformInformation } from '../platform';

import { hasDotnetDevCertsHttps, createSelfSignedCert } from '../utils/DotnetDevCertsHttps';

export class DotnetDebugConfigurationProvider implements vscode.DebugConfigurationProvider {
constructor(public platformInformation: PlatformInformation) {}
constructor(public platformInformation: PlatformInformation, private options: Options) {}

public async resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration | null | undefined>
{
Expand Down Expand Up @@ -59,6 +61,46 @@ export class DotnetDebugConfigurationProvider implements vscode.DebugConfigurati
return undefined;
}
}

// We want to ask the user if we should run dotnet dev-certs https --trust, but this doesn't work in a few cases --
// Linux -- not supported by the .NET CLI as there isn't a single root cert store
// VS Code remoting/Web UI -- the trusted cert work would need to happen on the client machine, but we don't have a way to run code there currently
// pipeTransport -- the dev cert on the server will be different from the client
if (!this.platformInformation.isLinux() && !vscode.env.remoteName && vscode.env.uiKind != vscode.UIKind.Web && !debugConfiguration.pipeTransport)
{
if(debugConfiguration.checkForDevCert === undefined && debugConfiguration.serverReadyAction)
{
debugConfiguration.checkForDevCert = true;
}

if (debugConfiguration.checkForDevCert)
{
hasDotnetDevCertsHttps(this.options.dotNetCliPaths).then(async (hasDevCert) => {
if(!hasDevCert)
{
const result = await vscode.window.showInformationMessage(
"The selected launch configuration is configured to launch a web browser but no trusted development certificate was found. Create a trusted self-signed certificate?",
'Yes', "Don't Ask Again", 'Not Now'
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
);
if (result === 'Yes')
{
if (await createSelfSignedCert(this.options.dotNetCliPaths))
{
vscode.window.showInformationMessage('Self-signed certificate sucessfully created');
}
else
{
vscode.window.showWarningMessage("Couldn't create self-signed certificate");
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
}
}
else if (result === "Don't Ask Again")
{
await vscode.window.showInformationMessage("To don't see this message again set launch option 'checkForDevCert' to 'false' in launch.json", { modal: true });
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
}
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
}
});
}
}

return debugConfiguration;
}
Expand Down
5 changes: 5 additions & 0 deletions src/tools/OptionsSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@
"targetArchitecture": {
"type": "string",
"description": "[Only supported in local macOS debugging] The architecture of the debuggee. This will automatically be detected unless this parameter is set. Allowed values are x86_64 or arm64."
},
"checkForDevCert": {
"type": "boolean",
"description": "When true, the presence of dev certificates will be checked by runing dotnet `dev-certs https --check`, and if that fails the user will be prompted to create dev certs by running `dotnet dev-certs https --trust`",
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
"default": true
}
}
},
Expand Down
50 changes: 50 additions & 0 deletions src/utils/DotnetDevCertsHttps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { join } from "path";
import { execChildProcess } from "../common";
import { CoreClrDebugUtil } from "../coreclr-debug/util";

// Will return true if `dotnet dev-certs https --check` succesfully finds a development certificate.
export async function hasDotnetDevCertsHttps(dotNetCliPaths: string[]): Promise<Boolean> {

let dotnetExecutablePath = getDotNetExecutablePath(dotNetCliPaths);

try {
await execChildProcess(`${dotnetExecutablePath ?? 'dotnet'} dev-certs https --check`, process.cwd(), process.env);
return true;
}
catch (err) { // execChildProcess will throw if the process returns anything but 0
return false;
}
}

// Will run `dotnet dev-certs https --trust` to prompt the user to create self signed certificates. Retruns true if sucessfull.
export async function createSelfSignedCert(dotNetCliPaths: string[]): Promise<Boolean> {
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved

let dotnetExecutablePath = getDotNetExecutablePath(dotNetCliPaths);

try {
await execChildProcess(`${dotnetExecutablePath ?? 'dotnet'} dev-certs https --trust`, process.cwd(), process.env);
return true;
}
catch (err) { // execChildProcess will throw if the process returns anything but 0
return false;
}
}

function getDotNetExecutablePath(dotNetCliPaths: string[]): string | undefined{
CarlosGuerraVz marked this conversation as resolved.
Show resolved Hide resolved
let dotnetExeName = `dotnet${CoreClrDebugUtil.getPlatformExeExtension()}`;
let dotnetExecutablePath: string | undefined;

for (const dotnetPath of dotNetCliPaths) {
let dotnetFullPath = join(dotnetPath, dotnetExeName);
if (CoreClrDebugUtil.existsSync(dotnetFullPath)) {
dotnetExecutablePath = dotnetFullPath;
break;
}
}
return dotnetExecutablePath;
}