Skip to content

Commit

Permalink
Update authenticators
Browse files Browse the repository at this point in the history
  • Loading branch information
lennartdohmann committed Feb 12, 2025
1 parent e5f1e46 commit a47f36b
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 176 deletions.
Original file line number Diff line number Diff line change
@@ -1,62 +1,29 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Vaas.Authentication;

public class ClientCredentialsGrantAuthenticator : IAuthenticator
public class ClientCredentialsGrantAuthenticator(
string clientId,
string clientSecret,
Uri? tokenEndpoint = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
) : TokenReceiver(tokenEndpoint, httpClient, systemClock), IAuthenticator
{
private readonly TokenReceiver _tokenReceiver;
private string ClientId { get; } = clientId;
private string ClientSecret { get; } = clientSecret;

private string ClientId { get; }
private string ClientSecret { get; }

public ClientCredentialsGrantAuthenticator(
string clientId,
string clientSecret,
Uri? tokenUrl = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
)
protected override FormUrlEncodedContent TokenRequestToForm()
{
_tokenReceiver = new ClientCredentialsTokenReceiver(
this,
tokenUrl,
httpClient,
systemClock
return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", ClientId),
new("client_secret", ClientSecret ?? throw new InvalidOperationException()),
new("grant_type", "client_credentials"),
}
);
ClientId = clientId;
ClientSecret = clientSecret;
}

public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
return await _tokenReceiver.GetTokenAsync(cancellationToken);
}

private class ClientCredentialsTokenReceiver(
IAuthenticator authenticator,
Uri? tokenUrl = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
) : TokenReceiver(authenticator, tokenUrl, httpClient, systemClock)
{
protected override FormUrlEncodedContent TokenRequestToForm()
{
var authenticator = (ClientCredentialsGrantAuthenticator)Authenticator;
return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", authenticator.ClientId),
new(
"client_secret",
authenticator.ClientSecret ?? throw new InvalidOperationException()
),
new("grant_type", "client_credentials"),
}
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,69 +1,32 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Vaas.Authentication;

public class ResourceOwnerPasswordGrantAuthenticator : IAuthenticator
public class ResourceOwnerPasswordGrantAuthenticator(
string clientId,
string userName,
string password,
Uri? tokenEndpoint = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
) : TokenReceiver(tokenEndpoint, httpClient, systemClock), IAuthenticator
{
private readonly TokenReceiver _tokenReceiver;
private string ClientId { get; } = clientId;
private string UserName { get; } = userName;
private string Password { get; } = password;

private string ClientId { get; }
private string UserName { get; }
private string Password { get; }

public ResourceOwnerPasswordGrantAuthenticator(
string clientId,
string userName,
string password,
Uri? tokenUrl = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
)
protected override FormUrlEncodedContent TokenRequestToForm()
{
_tokenReceiver = new ResourceOwnerPasswordTokenReceiver(
this,
tokenUrl,
httpClient,
systemClock
return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", ClientId),
new("username", UserName ?? throw new InvalidOperationException()),
new("password", Password ?? throw new InvalidOperationException()),
new("grant_type", "password"),
}
);
ClientId = clientId;
UserName = userName;
Password = password;
}

public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
return await _tokenReceiver.GetTokenAsync(cancellationToken);
}

private class ResourceOwnerPasswordTokenReceiver(
IAuthenticator authenticator,
Uri? tokenUrl = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
) : TokenReceiver(authenticator, tokenUrl, httpClient, systemClock)
{
protected override FormUrlEncodedContent TokenRequestToForm()
{
var authenticator = (ResourceOwnerPasswordGrantAuthenticator)Authenticator;
return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", authenticator.ClientId),
new(
"username",
authenticator.UserName ?? throw new InvalidOperationException()
),
new(
"password",
authenticator.Password ?? throw new InvalidOperationException()
),
new("grant_type", "password"),
}
);
}
}
}
6 changes: 2 additions & 4 deletions dotnet/Vaas/src/Vaas/Authentication/TokenReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@

namespace Vaas.Authentication;

internal abstract class TokenReceiver(
IAuthenticator authenticator,
public abstract class TokenReceiver(
Uri? tokenUrl = null,
HttpClient? httpClient = null,
ISystemClock? systemClock = null
) : IAuthenticator, IDisposable
) : IDisposable
{
protected readonly IAuthenticator Authenticator = authenticator;
private readonly Uri _tokenUrl =
tokenUrl
?? new Uri("https://account.gdata.de/realms/vaas-production/protocol/openid-connect/token");
Expand Down
53 changes: 32 additions & 21 deletions dotnet/Vaas/src/Vaas/Vaas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,30 +143,41 @@ public async Task<VaasVerdict> ForFileAsync(
ForFileOptions? options = null
)
{
options ??= ForFileOptions.From(_options);
if (!File.Exists(path))
throw new VaasClientException("File does not exist: " + path);

var sha256 = ChecksumSha256.Sha256CheckSum(path);
options ??= ForFileOptions.From(_options);

var forSha256Options = new ForSha256Options
if (options.UseCache || options.UseHashLookup)
{
VaasRequestId = options.VaasRequestId,
UseHashLookup = options.UseHashLookup,
UseCache = options.UseCache,
};
var forSha256Options = new ForSha256Options
{
VaasRequestId = options.VaasRequestId,
UseHashLookup = options.UseHashLookup,
UseCache = options.UseCache,
};

var response = await ForSha256Async(sha256, cancellationToken, forSha256Options);

var verdictWithoutDetection =
response.Verdict is Verdict.Malicious or Verdict.Pup
&& string.IsNullOrEmpty(response.Detection);
if (
response.Verdict != Verdict.Unknown
&& !verdictWithoutDetection
&& !string.IsNullOrWhiteSpace(response.FileType)
&& !string.IsNullOrEmpty(response.MimeType)
)
{
return response;
try
{
var sha256 = ChecksumSha256.Sha256CheckSum(path);
var response = await ForSha256Async(sha256, cancellationToken, forSha256Options);
var verdictWithoutDetection =
response.Verdict is Verdict.Malicious or Verdict.Pup
&& string.IsNullOrEmpty(response.Detection);
if (
response.Verdict != Verdict.Unknown
&& !verdictWithoutDetection
&& !string.IsNullOrWhiteSpace(response.FileType)
&& !string.IsNullOrEmpty(response.MimeType)
)
{
return response;
}
}
catch (Exception)
{
// ignore
}
}

await using var stream = File.OpenRead(path);
Expand All @@ -188,7 +199,7 @@ public async Task<VaasVerdict> ForStreamAsync(

var url = new Uri(
_options.VaasUrl,
$"/files?useHashLookup={JsonSerializer.Serialize(options.UseHashLookup)}"
$"/files?useHashLookup={JsonSerializer.Serialize(options.UseHashLookup)}&useCache={JsonSerializer.Serialize(true)}"
);

var request = new HttpRequestMessage
Expand Down
1 change: 0 additions & 1 deletion dotnet/Vaas/src/Vaas/Vaas.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.4.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.4.0" />
<PackageReference Include="Websocket.Client" Version="5.1.2" />
</ItemGroup>

Expand Down
65 changes: 22 additions & 43 deletions dotnet/Vaas/test/Vaas.Test/VaasTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private static ServiceCollection GetServices()
return GetServices(
new Dictionary<string, string>
{
{ "VerdictAsAService:Options:UseHashLookup", "false" },
{ "VerdictAsAService:Options:UseHashLookup", "true" },
{ "VerdictAsAService:Options:UseCache", "false" },
{ "VerdictAsAService:Options:VaasUrl", VaasUrl.ToString() },
{ "VerdictAsAService:Options:Timeout", "10" },
Expand Down Expand Up @@ -360,29 +360,32 @@ public async Task ForFileOptions_SendsOptions(bool useCache, bool useHashLookup)
const string content =
"X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
const string sha256 = "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f";
var fileName = Guid.NewGuid().ToString() + ".txt";
var fileName = Guid.NewGuid() + ".txt";

await File.WriteAllBytesAsync(fileName, Encoding.UTF8.GetBytes(content));

var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

handlerMock
.SetupRequest(request =>
request.RequestUri != null
&& request.Method == HttpMethod.Get
&& request.RequestUri.ToString().Contains(sha256)
&& request
.RequestUri.ToString()
.Contains("useCache=" + JsonSerializer.Serialize(useCache))
&& request
.RequestUri.ToString()
.Contains("useHashLookup=" + JsonSerializer.Serialize(useHashLookup))
)
.ReturnsResponse(
JsonSerializer.Serialize(
new FileReport { Sha256 = EicarSha256, Verdict = Verdict.Unknown }
if (useCache || useHashLookup)
{
handlerMock
.SetupRequest(request =>
request.RequestUri != null
&& request.Method == HttpMethod.Get
&& request.RequestUri.ToString().Contains(sha256)
&& request
.RequestUri.ToString()
.Contains("useCache=" + JsonSerializer.Serialize(useCache))
&& request
.RequestUri.ToString()
.Contains("useHashLookup=" + JsonSerializer.Serialize(useHashLookup))
)
);
.ReturnsResponse(
JsonSerializer.Serialize(
new FileReport { Sha256 = EicarSha256, Verdict = Verdict.Unknown }
)
);
}

if (!useCache)
{
Expand Down Expand Up @@ -422,7 +425,7 @@ public async Task ForFileOptions_SendsOptions(bool useCache, bool useHashLookup)
var vaas = provider.GetRequiredService<IVaas>();

await vaas.ForFileAsync(
"file.txt",
fileName,
CancellationToken.None,
new ForFileOptions { UseCache = useCache, UseHashLookup = useHashLookup }
);
Expand Down Expand Up @@ -632,30 +635,6 @@ await _vaas
.ThrowAsync<OperationCanceledException>();
}

[Fact]
public async Task ForFileAsync_IfForSha256DoesNotReturnDetectionEtc_UploadsFile()
{
var buffer = new byte[1024];
Random.Shared.NextBytes(buffer);
await File.WriteAllBytesAsync("file.txt", buffer);
var sha256 = new ChecksumSha256(SHA256.HashData(buffer));
// TODO: Mock response

var actual = await _vaas.ForFileAsync("file.txt", CancellationToken.None);

actual
.Should()
.BeEquivalentTo(
new VaasVerdict
{
Sha256 = sha256,
Detection = null,
FileType = "data",
MimeType = "application/octet-stream",
}
);
}

[Fact]
public async Task ForStreamAsync_ReturnsVerdict()
{
Expand Down

0 comments on commit a47f36b

Please sign in to comment.