Skip to content

Commit

Permalink
Merge branch 'main' into rust/add-resource-owner-password-cred-grant-…
Browse files Browse the repository at this point in the history
…auth
  • Loading branch information
secana authored Nov 3, 2023
2 parents ad041c8 + ccada44 commit 8734d0d
Show file tree
Hide file tree
Showing 38 changed files with 1,040 additions and 218 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci-dotnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ jobs:
run: dotnet build --configuration Release --no-restore
working-directory: dotnet/Vaas

- name: Test
run: dotnet test --no-restore --verbosity normal
working-directory: dotnet/Vaas
# - name: Test
# run: dotnet test --no-restore --verbosity normal
# working-directory: dotnet/Vaas

- name: Run example FileScan
env:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci-ruby.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ jobs:
run: ruby example_with_reconnect.rb
working-directory: ruby/examples

- name: Run authentication example
env:
URL: "https://github.com/GDATASoftwareAG/vaas"
run: ruby authentication.rb
working-directory: ruby/examples

- name: Push to rubygems.org
if: startsWith(github.ref, 'refs/tags/rb')
env:
Expand Down
19 changes: 9 additions & 10 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ Create a Discord bot that scans and deletes malicious files uploaded on your Dis

## I'm interested in VaaS

You need credentials to use the service in your application. If you are interested in using VaaS, please [contact us](mailto:[email protected]).
Interested in trying out VaaS? Sign up on our website to create a free trial account. Visit our [registration page](https://vaas.gdata.de/login) and follow the instructions to get started.

If you have a business case or specific requirements, please contact us at [[email protected]](mailto:[email protected]) to discuss your needs and explore how VaaS can best fit your organization.

## SDKs

Expand All @@ -49,14 +51,11 @@ At the moment SDKs for [Rust](./rust/), [Java](./java/), [Typescript](./typescri
Documentation for the SDKs is available in the corresponding SDK folder.

* [Rust SDK](./rust/), [Examples](./rust/examples)
* [Java SDK](./java/)
* [Java SDK](./java/) [Examples](./java/examples)
* [PHP SDK](./php/), [Examples](./php/examples)
* [TypeScript SDK](./typescript/)
* [Python SDK](./python/)
* [.NET SDK](./dotnet/)
* [Ruby SDK](./ruby/)
* [Golang SDK](./golang/vaas/)

### Planned SDKs
* [TypeScript SDK](./typescript/), [Examples](./typescript/examples)
* [Python SDK](./python/), [Examples](./python/examples)
* [.NET SDK](./dotnet/), [Examples](./dotnet/examples)
* [Ruby SDK](./ruby/), [Examples](./ruby/examples)
* [Golang SDK](./golang/vaas/), [Examples](./golang/examples)

The following SDKs are planned but not yet available: *Swift*. If you need SDKs for other languages, please create an issue or contribute an SDK with a pull request.
62 changes: 62 additions & 0 deletions dotnet/Vaas/src/Vaas/Authentication/Authenticator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Vaas.Messages;

namespace Vaas.Authentication;

public class Authenticator : IAuthenticator
{
private readonly HttpClient _httpClient;
private readonly VaasOptions _options;

public Authenticator(HttpClient httpClient, VaasOptions options)
{
_httpClient = httpClient;
_options = options;
}

public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
var form = TokenRequestToForm();
var response = await _httpClient.PostAsync(_options.TokenUrl, form, cancellationToken);
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync(cancellationToken);
var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(stringResponse);
if (tokenResponse == null)
throw new JsonException("Access token is null");
return tokenResponse.AccessToken;
}

private FormUrlEncodedContent TokenRequestToForm()
{
if (_options.Credentials.GrantType == GrantType.ClientCredentials)
{
return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", _options.Credentials.ClientId),
new("client_secret", _options.Credentials.ClientSecret ?? throw new InvalidOperationException()),
new("grant_type", "client_credentials")
}
);
}

return new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new("client_id", _options.Credentials.ClientId),
new("username", _options.Credentials.UserName ?? throw new InvalidOperationException()),
new("password", _options.Credentials.Password ?? throw new InvalidOperationException()),
new("grant_type", "password")
});
}

public Task<string> RefreshTokenAsync(CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
}
}
21 changes: 21 additions & 0 deletions dotnet/Vaas/src/Vaas/Authentication/BearerTokenHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;

namespace Vaas.Authentication;

public class BearerTokenHandler : DelegatingHandler
{
private readonly IAuthenticator _authenticator;

public BearerTokenHandler(IAuthenticator authenticator) => _authenticator = authenticator;

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", await _authenticator.GetTokenAsync(cancellationToken));
return await base.SendAsync(request, cancellationToken);
}
}
11 changes: 11 additions & 0 deletions dotnet/Vaas/src/Vaas/Authentication/IAuthenticator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;

namespace Vaas.Authentication;

public interface IAuthenticator
{
Task<string> GetTokenAsync(CancellationToken cancellationToken);

Task<string> RefreshTokenAsync(CancellationToken cancellationToken);
}
54 changes: 54 additions & 0 deletions dotnet/Vaas/src/Vaas/Authentication/TokenRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.ComponentModel.DataAnnotations;
using CommunityToolkit.Diagnostics;

namespace Vaas.Authentication;

public enum GrantType
{
ClientCredentials,
Password
}

public class TokenRequest
{
[Required] public GrantType GrantType { get; set; }

[Required] public string ClientId { get; set; } = string.Empty;
public string? ClientSecret { get; set; }

public string? UserName { get; set; }
public string? Password { get; set; }

public static ValidationResult IsValid(TokenRequest? request, ValidationContext context)
{
Guard.IsNotNull(request);
var memberNames = new[] { context.MemberName ?? "" };
if (request.GrantType == GrantType.ClientCredentials)
{
if (string.IsNullOrWhiteSpace(request.ClientId) || string.IsNullOrWhiteSpace(request.ClientSecret))
{
return new ValidationResult(
"The fields ClientId and ClientSecret are required for the GrantType ClientCredentials.",
memberNames);
}

return ValidationResult.Success!;
}

if (request.GrantType == GrantType.Password)
{
if (string.IsNullOrWhiteSpace(request.ClientId) || string.IsNullOrWhiteSpace(request.UserName) ||
string.IsNullOrWhiteSpace(request.Password))
{
return new ValidationResult(
"The fields ClientId, UserName and Password are required for the GrantType Password.",
memberNames);
}

return ValidationResult.Success!;
}

throw new ArgumentOutOfRangeException();
}
}
16 changes: 16 additions & 0 deletions dotnet/Vaas/src/Vaas/Authentication/TokenResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
using CommunityToolkit.Diagnostics;

namespace Vaas.Messages;

public class TokenResponse
{
[JsonPropertyName("access_token")]
public string AccessToken { get; init; }

public TokenResponse(string accessToken)
{
Guard.IsNotNullOrEmpty(accessToken);
AccessToken = accessToken;
}
}
69 changes: 69 additions & 0 deletions dotnet/Vaas/src/Vaas/ChecksumSha256.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using CommunityToolkit.Diagnostics;

namespace Vaas;

[JsonConverter(typeof(ChecksumSha256Converter))]
public class ChecksumSha256
{
private const string EmptyFileSha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

public string Sha256 { get; }
private static readonly Regex Pattern = new("^[a-fA-F0-9]{64}$", RegexOptions.Compiled);

public ChecksumSha256(string sha256)
{
if (!Pattern.IsMatch(sha256))
{
throw new ArgumentException("Invalid Sha256", nameof(sha256));
}
Sha256 = sha256.ToLower();
}

public ChecksumSha256(byte[] sha256)
{
Guard.HasSizeEqualTo(sha256, 32);
Sha256 = Convert.ToHexString(sha256).ToLower();
}

public bool IsEmptyFile()
{
return Sha256 == EmptyFileSha256;
}

public static bool TryParse(string value, out ChecksumSha256? result)
{
try
{
result = new ChecksumSha256(value);
return true;
}
catch (ArgumentException)
{
result = default;
return false;
}
}

public static implicit operator ChecksumSha256(string sha256) => new (sha256);

public static implicit operator string(ChecksumSha256 s) => s.Sha256;

public override string ToString() => Sha256;
}

public class ChecksumSha256Converter : JsonConverter<ChecksumSha256>
{
public override ChecksumSha256? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new ChecksumSha256(reader.GetString() ?? throw new JsonException("Expected SHA256 string"));
}

public override void Write(Utf8JsonWriter writer, ChecksumSha256 value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
39 changes: 0 additions & 39 deletions dotnet/Vaas/src/Vaas/ClientCredentialsGrantAuthenticator.cs

This file was deleted.

25 changes: 25 additions & 0 deletions dotnet/Vaas/src/Vaas/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,41 @@ public VaasConnectionClosedException() : base("Connection closed")
}
}

/// <summary>The request is malformed or cannot be completed.</summary>
/// <remarks>
/// Recommended actions:
/// <ul>
/// <li>Don't repeat the request.</li>
/// <li>Log.</li>
/// <li>Analyze the error</li>
/// </ul>
/// </remarks>
public class VaasClientException : Exception
{
public VaasClientException(string? message) : base(message)
{
}

public VaasClientException(string? message, Exception? innerException) : base(message, innerException)
{
}
}

/// <summary>The server encountered an internal error.</summary>
/// <remarks>
/// Recommended actions:
/// <ul>
/// <li>You may retry the request after a certain delay.</li>
/// <li>If the problem persists contact G DATA.</li>
/// </ul>
/// </remarks>
public class VaasServerException : Exception
{
public VaasServerException(string? message) : base(message)
{
}

public VaasServerException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
9 changes: 0 additions & 9 deletions dotnet/Vaas/src/Vaas/Messages/TokenResponse.cs

This file was deleted.

Loading

0 comments on commit 8734d0d

Please sign in to comment.