Created
July 5, 2023 11:26
-
-
Save GeorgDangl/14cd0c2a2866c091e521593eaadc20c4 to your computer and use it in GitHub Desktop.
Accessing AVACloud with user- instead of service accounts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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