Skip to content

Commit

Permalink
feat: OAuth2 option [IDE-461] [IDE-465] (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawkyZ authored Jul 31, 2024
1 parent 034fbec commit 5946622
Show file tree
Hide file tree
Showing 27 changed files with 374 additions and 212 deletions.
1 change: 0 additions & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ on:

jobs:
test-vs22:
continue-on-error: true
runs-on: windows-2022
defaults:
run:
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Snyk Security Changelog

## [1.1.62]

### Fixed
- Added OAuth2 authentication Option to settings window.

## [1.1.61]

### Fixed
Expand Down
28 changes: 10 additions & 18 deletions Snyk.Code.Library.Tests/Snyk.Code.Library.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,17 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Microsoft.VisualStudio.SDK.Analyzers" Version="16.10.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Microsoft.VisualStudio.Sdk.TestFramework" Version="17.6.16" />
<PackageReference Include="Microsoft.VisualStudio.Sdk.TestFramework.Xunit" Version="17.6.16" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand All @@ -86,7 +75,10 @@
<ItemGroup>
<Reference Include="System.Runtime.Caching" />
</ItemGroup>

<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
<Target Name="WriteSettingsFile" BeforeTargets="PreBuildEvent">
<WriteLinesToFile File="$(SettingsFile)" Overwrite="true" Lines="{" />
<WriteLinesToFile File="$(SettingsFile)" Lines="&quot;ApiToken&quot;:&quot;$(TEST_API_TOKEN)&quot;" />
Expand Down
41 changes: 17 additions & 24 deletions Snyk.Code.Library.Tests/SnykCode/Api/SnykCodeClientTest.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
namespace Snyk.Code.Library.Tests.SnykCode.Api
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Snyk.Code.Library.Api;
using Snyk.Code.Library.Api.Dto;
using Snyk.Code.Library.Api.Dto.Analysis;
using Snyk.Common;
using Snyk.Common.Authentication;
using Xunit;

namespace Snyk.Code.Library.Tests.SnykCode.Api
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Snyk.Code.Library.Api;
using Snyk.Code.Library.Api.Dto;
using Snyk.Code.Library.Api.Dto.Analysis;
using Snyk.Code.Library.Tests.Api;
using Snyk.Common;
using Snyk.Common.Authentication;
using Xunit;

/// <summary>
/// Tests for <see cref="SnykCodeClient"/>.
/// </summary>
public class SnykCodeClientTest
{
private const string ContextFlowName = "test-visual-studio-plugin-ide";
private const string ContextOrgName = "platform_hammerhead";

private SnykCodeClient snykCodeClient;
private const string ContextOrgName = "devex_ide";

public SnykCodeClientTest()
{
this.snykCodeClient = new SnykCodeClient(
TestSettings.SnykCodeApiUrl,
TestSettings.Instance.ApiToken,
ContextFlowName,
ContextOrgName);
}
private readonly SnykCodeClient snykCodeClient = new SnykCodeClient(
TestSettings.SnykCodeApiUrl,
TestSettings.Instance.ApiToken,
ContextFlowName,
ContextOrgName);

[Fact]
public async Task SnykCodeClient_TwoFilesWithIssuesProvided_GetAnalysisSuccessAsync()
Expand Down
9 changes: 4 additions & 5 deletions Snyk.Code.Library.Tests/SnykCode/TestSettings.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
namespace Snyk.Code.Library.Tests.Api
{
using System;
using Snyk.Common;
using Snyk.Common.Authentication;
using System;
using Snyk.Common.Authentication;

namespace Snyk.Code.Library.Tests.SnykCode
{
/// <summary>
/// Test settings.
/// </summary>
Expand Down
43 changes: 35 additions & 8 deletions Snyk.Common.Tests/Service/ApiEndpointResolverTest.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using Moq;
using Xunit;
using Snyk.Common.Authentication;
using Snyk.Common.Settings;
using Snyk.Common.Service;

namespace Snyk.Common.Tests.Service
{
using Moq;
using Xunit;
using Snyk.Common.Authentication;
using Snyk.Common.Settings;
using Snyk.Common.Service;

/// <summary>
/// Tests for <see cref="ApiEndpointResolver"/>.
/// </summary>
Expand Down Expand Up @@ -51,17 +52,40 @@ public void ApiEndpointResolver_GetSnykCodeApiUrl_SingleTenant()
}

[Fact]
public void ApiEndpointResolver_GetSnykCodeApiUrl_Snykgov()
public void ApiEndpointResolver_GetSnykCodeApiUrl_Snykgov_NoOrg()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.CustomEndpoint)
.Returns("https://app.random-uuid.polaris.snykgov.io/api");
optionsMock
.Setup(options => options.IsFedramp())
.Returns(true);

var apiEndpointResolver = new ApiEndpointResolver(optionsMock.Object);

Assert.Throws<InvalidOperationException>(() => apiEndpointResolver.GetSnykCodeApiUrl());
}

[Fact]
public void ApiEndpointResolver_GetSnykCodeApiUrl_Snykgov()
{
var optionsMock = new Mock<ISnykOptions>();
optionsMock
.Setup(options => options.CustomEndpoint)
.Returns("https://app.random-uuid.polaris.snykgov.io/api");
optionsMock
.Setup(options => options.IsFedramp())
.Returns(true);
optionsMock
.Setup(options => options.Organization)
.Returns("dummy-org-name");

var apiEndpointResolver = new ApiEndpointResolver(optionsMock.Object);

var snykCodeApiUrl = apiEndpointResolver.GetSnykCodeApiUrl();

Assert.Equal("https://deeproxy.random-uuid.polaris.snykgov.io/", snykCodeApiUrl);
Assert.Equal("https://api.random-uuid.polaris.snykgov.io/hidden/orgs/dummy-org-name/code/", snykCodeApiUrl);
}

[Fact]
Expand All @@ -77,6 +101,9 @@ public void AuthenticationMethod()
optionsMock
.Setup(options => options.CustomEndpoint)
.Returns("https://app.snykgov.io/api");
optionsMock
.Setup(option => option.AuthenticationMethod)
.Returns(AuthenticationType.OAuth);

// Assert
Assert.Equal(AuthenticationType.OAuth, apiEndpointResolver.AuthenticationMethod);
Expand Down
20 changes: 10 additions & 10 deletions Snyk.Common.Tests/Service/SnykApiServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace Snyk.VisualStudio.Extension.Tests
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Moq;
using Snyk.Common.Authentication;
using Snyk.Common.Service;
using Snyk.Common.Settings;
using Xunit;

namespace Snyk.Common.Tests.Service
{
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Moq;
using Snyk.Common.Authentication;
using Snyk.Common.Service;
using Snyk.Common.Settings;
using Xunit;

/// <summary>
/// Tests for <see cref="SnykApiService"/>.
/// </summary>
Expand Down
14 changes: 11 additions & 3 deletions Snyk.Common.Tests/Snyk.Common.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Microsoft.VisualStudio.Sdk.TestFramework" Version="17.6.16" />
<PackageReference Include="Microsoft.VisualStudio.Sdk.TestFramework.Xunit" Version="17.6.16" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand All @@ -20,6 +23,11 @@
</PackageReference>
</ItemGroup>

<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Snyk.Common\Snyk.Common.csproj" />
</ItemGroup>
Expand Down
19 changes: 12 additions & 7 deletions Snyk.Common/Authentication/AuthenticationToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public AuthenticationToken(AuthenticationType type, string value)

public AuthenticationType Type { get; }

public override string ToString()
public string Refresh()
{
// if possible and required, update the token before using it
if (this.TokenRefresher != null && Type == AuthenticationType.OAuth)
Expand All @@ -43,6 +43,17 @@ public override string ToString()
return this.value;
}

public override string ToString()
{
return this.value;
}

public bool IsValidAfterRefresh()
{
this.value = Refresh();
return IsValid();
}

public bool IsValid()
{
if (string.IsNullOrWhiteSpace(this.value))
Expand All @@ -57,12 +68,6 @@ public bool IsValid()
case AuthenticationType.OAuth:
{
var tokenState = GetTokenState(this.value);
if (tokenState == OAuthTokenState.Expired)
{
this.value = this.TokenRefresher();
tokenState = GetTokenState(this.value);
}

return tokenState == OAuthTokenState.Valid;
}
default:
Expand Down
6 changes: 5 additions & 1 deletion Snyk.Common/Authentication/AuthenticationType.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
namespace Snyk.Common.Authentication
using System.ComponentModel;

namespace Snyk.Common.Authentication
{
public enum AuthenticationType
{
[Description("Token authentication")]
Token,
[Description("OAuth2 authentication")]
OAuth,
}
}
2 changes: 1 addition & 1 deletion Snyk.Common/HttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static HttpClient NewHttpClient(AuthenticationToken token, string baseUrl
var authorizationString = "token " + token;
if (token.Type == AuthenticationType.OAuth)
{
var rawToken = token.ToString();
var rawToken = token.Refresh();
var oauthToken = OAuthToken.FromJson(rawToken);
var accessToken = oauthToken?.AccessToken;
authorizationString = "bearer " + accessToken;
Expand Down
29 changes: 13 additions & 16 deletions Snyk.Common/Service/ApiEndpointResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,7 @@ public ApiEndpointResolver(ISnykOptions options)
/// </summary>
public string UserMeEndpoint => SnykApiEndpoint + "/v1/user/me";

public AuthenticationType AuthenticationMethod
{
get
{
var endpoint = ResolveCustomEndpoint(this.options.CustomEndpoint);
var endpointUri = new Uri(endpoint);
if (endpointUri.Host.Contains("snykgov.io"))
{
return AuthenticationType.OAuth;
}

return AuthenticationType.Token;
}
}
public AuthenticationType AuthenticationMethod => this.options.AuthenticationMethod;

/// <summary>
/// Get SnykCode Settings url.
Expand All @@ -73,8 +60,18 @@ public string GetSnykCodeApiUrl()

var endpoint = ResolveCustomEndpoint(this.options.CustomEndpoint);

var result = GetCustomEndpointUrlFromSnykApi(endpoint, "deeproxy");

var isFedramp = this.options.IsFedramp();

if (isFedramp && string.IsNullOrEmpty(this.options.Organization))
throw new InvalidOperationException("Organization is required in a fedramp environment");

var subDomain = isFedramp ? "api" : "deeproxy";

var result = GetCustomEndpointUrlFromSnykApi(endpoint, subDomain);

if (isFedramp)
result += $"/hidden/orgs/{this.options.Organization}/code";

return result + "/";
}

Expand Down
Loading

0 comments on commit 5946622

Please sign in to comment.