Skip to content

Instantly share code, notes, and snippets.

@GeorgDangl
Created July 5, 2023 11:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GeorgDangl/14cd0c2a2866c091e521593eaadc20c4 to your computer and use it in GitHub Desktop.
Save GeorgDangl/14cd0c2a2866c091e521593eaadc20c4 to your computer and use it in GitHub Desktop.
Accessing AVACloud with user- instead of service accounts
public class AvaCloudUserClientFactory
{
public AvaCloudUserClientFactory(string userIdentifier,
string userPassword,
string avacloudBaseUrl = "https://avacloud-api.dangl-it.com",
Func<ITokenStorage> tokenStorageFactory = null)
{
_serviceProvider = BuildAvaCloudServiceProvider(userIdentifier,
userPassword,
avacloudBaseUrl,
tokenStorageFactory);
}
public TokenAccessChecker TokenAccessChecker => _serviceProvider.GetRequiredService<TokenAccessChecker>();
public AvaConversionClient AvaConversionClient => _serviceProvider.GetRequiredService<AvaConversionClient>();
public GaebConversionClient GaebConversionClient => _serviceProvider.GetRequiredService<GaebConversionClient>();
public ExcelConversionClient ExcelConversionClient => _serviceProvider.GetRequiredService<ExcelConversionClient>();
private readonly IServiceProvider _serviceProvider;
private static IServiceProvider BuildAvaCloudServiceProvider(string userIdentifier,
string userPassword,
string avacloudBaseUrl,
Func<ITokenStorage> tokenStorageFactory)
{
tokenStorageFactory ??= () => new InMemoryTokenStorage();
var services = new ServiceCollection();
services.AddSingleton<ITokenStorage>(x => tokenStorageFactory());
services.AddTransient<ITokenHandler>(x =>
{
var httpClientFactory = x.GetRequiredService<IHttpClientFactory>();
var tokenStorage = x.GetRequiredService<ITokenStorage>();
var userAccountTokenHandler = new UserAccountTokenHandler(httpClientFactory,
userIdentifier,
userPassword,
avacloudBaseUrl,
tokenStorage);
return userAccountTokenHandler;
});
services.AddTransient<TokenAccessChecker>();
services.AddHttpClient();
var avaCloudHttpClientBuilder = services
.AddDanglHttpClient<AvaCloudHttpClientAccessor>(avacloudBaseUrl)
.AddHttpMessageHandler(() => new GzipHandler())
.ConfigurePrimaryHttpMessageHandler(c =>
{
var handler = new HttpClientHandler();
if (handler is HttpClientHandler httpClientHandler)
{
httpClientHandler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
}
return handler;
});
services.AddTransient<AvaConversionClient>(x =>
{
var httpClient = x.GetRequiredService<AvaCloudHttpClientAccessor>().HttpClient;
return new AvaConversionClient(avacloudBaseUrl, httpClient) { BaseUrl = avacloudBaseUrl };
});
services.AddTransient<GaebConversionClient>(x =>
{
var httpClient = x.GetRequiredService<AvaCloudHttpClientAccessor>().HttpClient;
return new GaebConversionClient(avacloudBaseUrl, httpClient) { BaseUrl = avacloudBaseUrl };
});
services.AddTransient<ExcelConversionClient>(x =>
{
var httpClient = x.GetRequiredService<AvaCloudHttpClientAccessor>().HttpClient;
return new ExcelConversionClient(avacloudBaseUrl, httpClient) { BaseUrl = avacloudBaseUrl };
});
return services.BuildServiceProvider();
}
}
public class TokenAccessChecker
{
private readonly ITokenHandler _tokenHandler;
public TokenAccessChecker(ITokenHandler tokenHandler)
{
_tokenHandler = tokenHandler;
}
public async Task<bool> CheckIfUserHasAccessAsync()
{
var token = await _tokenHandler.GetAndStoreTokenAsync();
var jwt = new JwtSecurityToken(token.AccessToken);
var hasUserAccess = jwt.Claims.Any(c => c.Type == "avacloud_user_access"
&& (c.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)
|| DateTimeOffset.TryParse(c.Value.Trim('"'), out var claimValidUntil) && claimValidUntil >= DateTimeOffset.UtcNow));
return hasUserAccess;
}
}
public class UserAccountAuthenticationTests
{
[Fact]
public async Task UserCredentialsTest()
{
// We're getting the client factory first
var userClientFactory = new AvaCloudUserClientFactory("<Username>", "<Password>");
// Now we'll try to get a token and check if it's valid
var tokenAccessChecker = userClientFactory.TokenAccessChecker;
var hasAccess = await tokenAccessChecker.CheckIfUserHasAccessAsync();
Assert.True(hasAccess);
// Now we'll convert Excel to AVA
var excelConversionClient = userClientFactory.ExcelConversionClient;
using var excelFile = System.IO.File.OpenRead(@"C:\my\path.xlsx");
var conversionResult = await excelConversionClient.ConvertToAvaAsync(new FileParameter(excelFile),
readNewElements: false,
rebuildItemNumberSchema: false,
removePlainTextLongTexts: false,
removeHtmlLongTexts: false);
Assert.NotNull(conversionResult);
}
}
public class UserAccountHttpClientAccessor : DanglHttpClientAccessor
{
/// <summary>
/// This is a wrapper that provides an <see cref="HttpClient"/>
/// to be used with the Microsoft HttpClientFactory
/// </summary>
public UserAccountHttpClientAccessor(HttpClient httpClient)
: base(httpClient)
{
}
}
public class UserAccountTokenHandler : ITokenHandler
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly string _userIdentifier;
private readonly string _userPassword;
private readonly string _avaCloudBaseUrl;
private readonly ITokenStorage _tokenStorage;
public UserAccountTokenHandler(IHttpClientFactory httpClientFactory,
string userIdentifier,
string userPassword,
string avaCloudBaseUrl,
ITokenStorage tokenStorage)
{
_httpClientFactory = httpClientFactory;
_userIdentifier = userIdentifier;
_userPassword = userPassword;
_avaCloudBaseUrl = avaCloudBaseUrl;
_tokenStorage = tokenStorage;
}
public bool CanGetToken => true;
public bool CanRefreshToken => true;
public event EventHandler<TokenUpdateEventArgs> TokenUpdate;
public Task<OAuthToken> GetAndStoreTokenAsync()
{
return GetAndStoreNewTokenAsync();
}
public Task<OAuthToken> RefreshAndStoreTokenAsync()
{
return GetAndStoreNewTokenAsync();
}
private async Task<OAuthToken> GetAndStoreNewTokenAsync()
{
var httpClient = _httpClientFactory.CreateClient();
var danglIdentityClient = new Dangl.AVACloud.Client.DanglIdentityClient(_avaCloudBaseUrl, httpClient)
{
BaseUrl = _avaCloudBaseUrl
};
var requestTime = DateTime.UtcNow;
var token = await danglIdentityClient.LoginAndReturnTokenAsync(new TokenLoginPost
{
Identifier = _userIdentifier,
Password = _userPassword
});
var oauthToken = new OAuthToken
{
AccessToken = token.AccessToken,
ValidUntilUtc = requestTime.AddSeconds(token.ExpiresIn)
};
await _tokenStorage.StoreTokenAsync(oauthToken);
return oauthToken;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment