From 2a506e9c86681cf45294a7140dda1a60c8fa352b Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Tue, 5 Jul 2022 10:15:22 -0400
Subject: [PATCH 01/10] Parsing StaticWebApps ClientPrincipal
---
DataGateway.Config/Authentication.cs | 18 +++--
.../EasyAuthAuthenticationUnitTests.cs | 78 ++++++++++++++-----
.../AuthenticationConfigValidatorUnitTests.cs | 2 +-
...ication.cs => AppServiceAuthentication.cs} | 16 ++--
....cs => AppServiceAuthenticationHandler.cs} | 8 +-
...EasyAuthAuthenticationBuilderExtensions.cs | 23 ++++--
.../StaticWebAppsAuthentication.cs | 63 +++++++++++++++
.../StaticWebAppsAuthenticationHandler.cs | 74 ++++++++++++++++++
.../CosmosSqlMetadataProvider.cs | 70 ++++++++++++++++-
DataGateway.Service/Startup.cs | 2 +-
DataGateway.Service/hawaii-config.Cosmos.json | 29 +++++--
...awaii-config.Cosmos.overrides.example.json | 6 +-
DataGateway.Service/hawaii-config.MsSql.json | 2 +-
...hawaii-config.MsSql.overrides.example.json | 2 +-
DataGateway.Service/hawaii-config.MySql.json | 2 +-
...hawaii-config.MySql.overrides.example.json | 2 +-
.../hawaii-config.PostgreSql.json | 2 +-
...i-config.PostgreSql.overrides.example.json | 4 +-
DataGateway.Service/hawaii-config.json | 2 +-
DataGateway.Service/schema.gql | 10 ++-
20 files changed, 352 insertions(+), 63 deletions(-)
rename DataGateway.Service/AuthenticationHelpers/{EasyAuthAuthentication.cs => AppServiceAuthentication.cs} (83%)
rename DataGateway.Service/AuthenticationHelpers/{EasyAuthAuthenticationHandler.cs => AppServiceAuthenticationHandler.cs} (91%)
create mode 100644 DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
create mode 100644 DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
diff --git a/DataGateway.Config/Authentication.cs b/DataGateway.Config/Authentication.cs
index 9a8c835e2a..d886cb3f69 100644
--- a/DataGateway.Config/Authentication.cs
+++ b/DataGateway.Config/Authentication.cs
@@ -3,20 +3,19 @@ namespace Azure.DataGateway.Config
///
/// Authentication configuration.
///
- /// Identity Provider. Default is EasyAuth.
+ /// Identity Provider.
/// With EasyAuth, no Audience or Issuer are expected.
///
/// Settings enabling validation of the received JWT token.
/// Required only when Provider is other than EasyAuth.
public record AuthenticationConfig(
- string Provider = AuthenticationConfig.EASYAUTH_PROVIDER_NAME,
+ string Provider,
Jwt? Jwt = null)
{
- public const string EASYAUTH_PROVIDER_NAME = "EasyAuth";
-
public bool IsEasyAuthAuthenticationProvider()
{
- return Provider.Equals(EASYAUTH_PROVIDER_NAME);
+ return Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(Provider, StringComparison.OrdinalIgnoreCase)
+ || Enum.GetName(EasyAuthType.AppService)!.Equals(Provider, StringComparison.OrdinalIgnoreCase);
}
}
@@ -26,4 +25,13 @@ public bool IsEasyAuthAuthenticationProvider()
///
///
public record Jwt(string Audience, string Issuer);
+
+ ///
+ /// Different modes in which the runtime can run.
+ ///
+ public enum EasyAuthType
+ {
+ StaticWebApps,
+ AppService
+ }
}
diff --git a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
index 07b4b88682..4af7d899ae 100644
--- a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
+++ b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
@@ -7,6 +7,7 @@
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
+using Azure.DataGateway.Config;
using Azure.DataGateway.Service.AuthenticationHelpers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -17,7 +18,8 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using static Azure.DataGateway.Service.AuthenticationHelpers.EasyAuthAuthentication;
+using static Azure.DataGateway.Service.AuthenticationHelpers.AppServiceAuthentication;
+using static Azure.DataGateway.Service.AuthenticationHelpers.StaticWebAppsAuthentication;
namespace Azure.DataGateway.Service.Tests.Authentication
{
@@ -30,19 +32,38 @@ public class EasyAuthAuthenticationUnitTests
{
#region Positive Tests
///
- /// Ensures a valid EasyAuth header/value does NOT result in HTTP 401 Unauthorized response.
+ /// Ensures a valid AppService EasyAuth header/value does NOT result in HTTP 401 Unauthorized response.
/// 403 is okay, as it indicates authorization level failure, not authentication.
/// When an authorization header is sent, it contains an invalid value, if the runtime returns an error
/// then there is improper JWT validation occurring.
///
[DataTestMethod]
- [DataRow(false, DisplayName = "Valid EasyAuth header only")]
- [DataRow(true, DisplayName = "Valid EasyAuth header and authorization header")]
+ [DataRow(false, DisplayName = "Valid AppService EasyAuth header only")]
+ [DataRow(true, DisplayName = "Valid AppService EasyAuth header and authorization header")]
[TestMethod]
- public async Task TestValidEasyAuthToken(bool sendAuthorizationHeader)
+ public async Task TestValidAppServiceEasyAuthToken(bool sendAuthorizationHeader)
{
- string generatedToken = CreateEasyAuthToken();
- HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(generatedToken);
+ string generatedToken = CreateAppServiceEasyAuthToken();
+ HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(generatedToken, EasyAuthType.AppService);
+ Assert.IsNotNull(postMiddlewareContext.User.Identity);
+ Assert.IsTrue(postMiddlewareContext.User.Identity.IsAuthenticated);
+ Assert.AreEqual(expected: (int)HttpStatusCode.OK, actual: postMiddlewareContext.Response.StatusCode);
+ }
+
+ ///
+ /// Ensures a valid StaticWebApps EasyAuth header/value does NOT result in HTTP 401 Unauthorized response.
+ /// 403 is okay, as it indicates authorization level failure, not authentication.
+ /// When an authorization header is sent, it contains an invalid value, if the runtime returns an error
+ /// then there is improper JWT validation occurring.
+ ///
+ [DataTestMethod]
+ [DataRow(false, DisplayName = "Valid StaticWebApps EasyAuth header only")]
+ [DataRow(true, DisplayName = "Valid StaticWebApps EasyAuth header and authorization header")]
+ [TestMethod]
+ public async Task TestValidStaticWebAppsEasyAuthToken(bool sendAuthorizationHeader)
+ {
+ string generatedToken = CreateStaticWebAppsEasyAuthToken();
+ HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(generatedToken, EasyAuthType.StaticWebApps);
Assert.IsNotNull(postMiddlewareContext.User.Identity);
Assert.IsTrue(postMiddlewareContext.User.Identity.IsAuthenticated);
Assert.AreEqual(expected: (int)HttpStatusCode.OK, actual: postMiddlewareContext.Response.StatusCode);
@@ -68,7 +89,7 @@ public async Task TestValidEasyAuthToken(bool sendAuthorizationHeader)
[TestMethod]
public async Task TestInvalidEasyAuthToken(string token, bool sendAuthorizationHeader = false)
{
- HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(token, sendAuthorizationHeader);
+ HttpContext postMiddlewareContext = await SendRequestAndGetHttpContextState(token, EasyAuthType.StaticWebApps, sendAuthorizationHeader);
Assert.IsNotNull(postMiddlewareContext.User.Identity);
Assert.IsFalse(postMiddlewareContext.User.Identity.IsAuthenticated);
Assert.AreEqual(expected: (int)HttpStatusCode.Unauthorized, actual: postMiddlewareContext.Response.StatusCode);
@@ -80,7 +101,7 @@ public async Task TestInvalidEasyAuthToken(string token, bool sendAuthorizationH
/// Configures test server with bare minimum middleware
///
/// IHost
- private static async Task CreateWebHostEasyAuth()
+ private static async Task CreateWebHostEasyAuth(EasyAuthType easyAuthType)
{
return await new HostBuilder()
.ConfigureWebHost(webBuilder =>
@@ -90,7 +111,8 @@ private static async Task CreateWebHostEasyAuth()
.ConfigureServices(services =>
{
services.AddAuthentication(defaultScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication();
+ .AddEasyAuthAuthentication(easyAuthType.ToString());
+
services.AddAuthorization();
})
.ConfigureLogging(o =>
@@ -125,9 +147,9 @@ private static async Task CreateWebHostEasyAuth()
/// The EasyAuth header value(base64 encoded token) to test against the TestServer
/// Whether to add authorization header to header dictionary
///
- private static async Task SendRequestAndGetHttpContextState(string? token, bool sendAuthorizationHeader = false)
+ private static async Task SendRequestAndGetHttpContextState(string? token, EasyAuthType easyAuthType, bool sendAuthorizationHeader = false)
{
- using IHost host = await CreateWebHostEasyAuth();
+ using IHost host = await CreateWebHostEasyAuth(easyAuthType);
TestServer server = host.GetTestServer();
return await server.SendAsync(context =>
@@ -135,7 +157,7 @@ private static async Task SendRequestAndGetHttpContextState(string?
if (token is not null)
{
StringValues headerValue = new(new string[] { $"{token}" });
- KeyValuePair easyAuthHeader = new(EasyAuthAuthentication.EASYAUTHHEADER, headerValue);
+ KeyValuePair easyAuthHeader = new(AppServiceAuthentication.EASYAUTHHEADER, headerValue);
context.Request.Headers.Add(easyAuthHeader);
}
@@ -153,25 +175,25 @@ private static async Task SendRequestAndGetHttpContextState(string?
/// Creates a mocked EasyAuth token, namely, the value of the header injected by EasyAuth.
///
/// A Base64 encoded string of a serialized EasyAuthClientPrincipal object
- private static string CreateEasyAuthToken()
+ private static string CreateAppServiceEasyAuthToken()
{
- EasyAuthClaim emailClaim = new()
+ AppServiceClaim emailClaim = new()
{
Val = "apple@contoso.com",
Typ = ClaimTypes.Upn
};
- EasyAuthClaim roleClaim = new()
+ AppServiceClaim roleClaim = new()
{
Val = "Anonymous",
Typ = ClaimTypes.Role
};
- List claims = new();
+ List claims = new();
claims.Add(emailClaim);
claims.Add(roleClaim);
- EasyAuthClientPrincipal token = new()
+ AppServiceClientPrincipal token = new()
{
Auth_typ = "aad",
Name_typ = "Apple Banana",
@@ -182,6 +204,26 @@ private static string CreateEasyAuthToken()
string serializedToken = JsonSerializer.Serialize(value: token);
return Convert.ToBase64String(Encoding.UTF8.GetBytes(serializedToken));
}
+
+ ///
+ /// Creates a mocked EasyAuth token, namely, the value of the header injected by EasyAuth.
+ ///
+ /// A Base64 encoded string of a serialized EasyAuthClientPrincipal object
+ private static string CreateStaticWebAppsEasyAuthToken()
+ {
+ List roles = new();
+ roles.Add("anonymous");
+ roles.Add("authenticated");
+
+ StaticWebAppsClientPrincipal token = new()
+ {
+ IdentityProvider = "github",
+ UserRoles = roles
+ };
+
+ string serializedToken = JsonSerializer.Serialize(value: token);
+ return Convert.ToBase64String(Encoding.UTF8.GetBytes(serializedToken));
+ }
#endregion
}
}
diff --git a/DataGateway.Service.Tests/Configuration/AuthenticationConfigValidatorUnitTests.cs b/DataGateway.Service.Tests/Configuration/AuthenticationConfigValidatorUnitTests.cs
index 4b82806b47..42e4bcdad2 100644
--- a/DataGateway.Service.Tests/Configuration/AuthenticationConfigValidatorUnitTests.cs
+++ b/DataGateway.Service.Tests/Configuration/AuthenticationConfigValidatorUnitTests.cs
@@ -21,7 +21,7 @@ public class AuthenticationConfigValidatorUnitTests
public void ValidateEasyAuthConfig()
{
RuntimeConfig config =
- CreateRuntimeConfigWithAuthN(new AuthenticationConfig());
+ CreateRuntimeConfigWithAuthN(new AuthenticationConfig(EasyAuthType.StaticWebApps.ToString()));
RuntimeConfigValidator configValidator = GetMockConfigValidator(ref config);
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
similarity index 83%
rename from DataGateway.Service/AuthenticationHelpers/EasyAuthAuthentication.cs
rename to DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
index 8855f727fa..34dae53408 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
@@ -12,26 +12,26 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
/// Helper class which parses EasyAuth's injected headers into a ClaimsIdentity object.
/// This class provides helper methods for StaticWebApp's Authentication feature: EasyAuth.
///
- public static class EasyAuthAuthentication
+ public static class AppServiceAuthentication
{
public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
///
/// Representation of authenticated user principal Http header
/// injected by EasyAuth
///
- public struct EasyAuthClientPrincipal
+ public struct AppServiceClientPrincipal
{
public string Auth_typ { get; set; }
public string Name_typ { get; set; }
public string Role_typ { get; set; }
- public IEnumerable Claims { get; set; }
+ public IEnumerable Claims { get; set; }
}
///
/// Representation of authenticated user principal claims
/// injected by EasyAuth
///
- public struct EasyAuthClaim
+ public struct AppServiceClaim
{
public string Typ { get; set; }
public string Val { get; set; }
@@ -53,20 +53,20 @@ public struct EasyAuthClaim
{
ClaimsIdentity? identity = null;
- if (context.Request.Headers.TryGetValue(EasyAuthAuthentication.EASYAUTHHEADER, out StringValues header))
+ if (context.Request.Headers.TryGetValue(AppServiceAuthentication.EASYAUTHHEADER, out StringValues header))
{
try
{
string encodedPrincipalData = header[0];
byte[] decodedPrincpalData = Convert.FromBase64String(encodedPrincipalData);
string json = Encoding.UTF8.GetString(decodedPrincpalData);
- EasyAuthClientPrincipal principal = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ AppServiceClientPrincipal principal = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
identity = new(principal.Auth_typ, principal.Name_typ, principal.Role_typ);
if (principal.Claims != null)
{
- foreach (EasyAuthClaim claim in principal.Claims)
+ foreach (AppServiceClaim claim in principal.Claims)
{
identity.AddClaim(new Claim(type: claim.Typ, value: claim.Val));
}
@@ -77,7 +77,7 @@ public struct EasyAuthClaim
// Logging the parsing failure exception to the console, but not rethrowing
// nor creating a DataGateway exception because the authentication handler
// will create and send a 401 unauthorized response to the client.
- Console.Error.WriteLine("Failure processing the EasyAuth header.");
+ Console.Error.WriteLine("Failure processing the AppServie EasyAuth header.");
Console.Error.WriteLine(error.Message);
Console.Error.WriteLine(error.StackTrace);
}
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
similarity index 91%
rename from DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
rename to DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
index 942f1b90bb..60c4d58d63 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
@@ -17,7 +17,7 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
/// Usage modelled from Microsoft.Identity.Web.
/// Ref: https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationHandler.cs
///
- public class EasyAuthAuthenticationHandler : AuthenticationHandler
+ public class AppServiceAuthenticationHandler : AuthenticationHandler
{
private const string EASY_AUTH_HEADER = "X-MS-CLIENT-PRINCIPAL";
@@ -29,7 +29,7 @@ public class EasyAuthAuthenticationHandler : AuthenticationHandlerLogger factory.
/// URL encoder.
/// System clock.
- public EasyAuthAuthenticationHandler(
+ public AppServiceAuthenticationHandler(
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder,
@@ -49,11 +49,11 @@ protected override Task HandleAuthenticateAsync()
{
if (Context.Request.Headers[EASY_AUTH_HEADER].Count > 0)
{
- ClaimsIdentity? identity = EasyAuthAuthentication.Parse(Context);
+ ClaimsIdentity? identity = AppServiceAuthentication.Parse(Context);
if (identity is null)
{
- return Task.FromResult(AuthenticateResult.Fail(failureMessage: "Invalid EasyAuth token."));
+ return Task.FromResult(AuthenticateResult.Fail(failureMessage: "Invalid AppService EasyAuth token."));
}
ClaimsPrincipal? claimsPrincipal = new(identity);
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
index 173ff3c880..1017c7af39 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
@@ -1,3 +1,5 @@
+using System;
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Authentication;
namespace Azure.DataGateway.Service.AuthenticationHelpers
@@ -15,17 +17,28 @@ public static class EasyAuthAuthenticationBuilderExtensions
/// Authentication builder.
/// The builder, to chain commands.
public static AuthenticationBuilder AddEasyAuthAuthentication(
- this AuthenticationBuilder builder)
+ this AuthenticationBuilder builder, string easyAuthAuthenticationProvider)
{
if (builder is null)
{
throw new System.ArgumentNullException(nameof(builder));
}
- builder.AddScheme(
- authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- options => { });
+ if (Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
+ {
+ builder.AddScheme(
+ authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ options => { });
+ }
+
+ if (Enum.GetName(EasyAuthType.AppService)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
+ {
+ builder.AddScheme(
+ authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ options => { });
+ }
return builder;
}
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
new file mode 100644
index 0000000000..3876ec8cab
--- /dev/null
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using System.Text.Json;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace Azure.DataGateway.Service.AuthenticationHelpers
+{
+ public class StaticWebAppsAuthentication
+ {
+ public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
+
+ public class StaticWebAppsClientPrincipal
+ {
+ public string? IdentityProvider { get; set; }
+ public string? UserId { get; set; }
+ public string? UserDetails { get; set; }
+ public IEnumerable? UserRoles { get; set; }
+ }
+
+ public static ClaimsIdentity? Parse(HttpRequest req)
+ {
+ ClaimsIdentity? identity = null;
+ StaticWebAppsClientPrincipal principal = new();
+ try
+ {
+ if (req.Headers.TryGetValue(EASYAUTHHEADER, out StringValues header))
+ {
+ string data = header[0];
+ byte[] decoded = Convert.FromBase64String(data);
+ string json = Encoding.UTF8.GetString(decoded);
+ principal = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new();
+ }
+
+ if (!principal?.UserRoles?.Any() ?? true)
+ {
+ return identity;
+ }
+
+ identity = new(principal!.IdentityProvider);
+ identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, principal.UserId ?? string.Empty));
+ identity.AddClaim(new Claim(ClaimTypes.Name, principal.UserDetails ?? string.Empty));
+ identity.AddClaims(principal.UserRoles!.Select(r => new Claim(ClaimTypes.Role, r)));
+
+ return identity;
+ }
+ catch (Exception error)
+ {
+ // Logging the parsing failure exception to the console, but not rethrowing
+ // nor creating a DataGateway exception because the authentication handler
+ // will create and send a 401 unauthorized response to the client.
+ Console.Error.WriteLine("Failure processing the StaticWebApps EasyAuth header.");
+ Console.Error.WriteLine(error.Message);
+ Console.Error.WriteLine(error.StackTrace);
+ }
+
+ return identity;
+ }
+ }
+}
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
new file mode 100644
index 0000000000..c3da9a1198
--- /dev/null
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
@@ -0,0 +1,74 @@
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Azure.DataGateway.Service.AuthenticationHelpers
+{
+ ///
+ /// This class is used to best integrate with ASP.NET Core AuthenticationHandler base class.
+ /// Ref: https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationHandler.cs
+ /// When "EasyAuth" is configured, this handler authenticates the user once per request,
+ /// and utilizes the base class default handler for
+ /// - AuthenticateAsync: Authenticates the current request.
+ /// - Forbid Async: Creates 403 HTTP Response.
+ /// Usage modelled from Microsoft.Identity.Web.
+ /// Ref: https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationHandler.cs
+ ///
+ public class StaticWebAppsAuthenticationHandler : AuthenticationHandler
+ {
+ private const string EASY_AUTH_HEADER = "X-MS-CLIENT-PRINCIPAL";
+
+ ///
+ /// Constructor for the EasyAuthAuthenticationHandler.
+ /// Note the parameters are required by the base class.
+ ///
+ /// App service authentication options.
+ /// Logger factory.
+ /// URL encoder.
+ /// System clock.
+ public StaticWebAppsAuthenticationHandler(
+ IOptionsMonitor options,
+ ILoggerFactory logger,
+ UrlEncoder encoder,
+ ISystemClock clock
+ ) : base(options, logger, encoder, clock)
+ {
+ }
+
+ ///
+ /// Gets any authentication data for a request. When an EasyAuth header is present,
+ /// parses the header and authenticates the user within a ClaimsPrincipal object.
+ /// The ClaimsPrincipal is a security principal usable by middleware to identify the
+ /// authenticated user.
+ ///
+ /// An authentication result to ASP.NET Core library authentication mechanisms
+ protected override Task HandleAuthenticateAsync()
+ {
+ if (Context.Request.Headers[EASY_AUTH_HEADER].Count > 0)
+ {
+ ClaimsIdentity? identity = StaticWebAppsAuthentication.Parse(Context.Request);
+
+ if (identity is null)
+ {
+ return Task.FromResult(AuthenticateResult.Fail(failureMessage: "Invalid StaticWebApps EasyAuth token."));
+ }
+
+ ClaimsPrincipal? claimsPrincipal = new(identity);
+
+ if (claimsPrincipal is not null)
+ {
+ // AuthenticationTicket is Asp.Net Core Abstraction of Authentication information
+ // Ref: aspnetcore/src/Http/Authentication.Abstractions/src/AuthenticationTicket.cs
+ AuthenticationTicket ticket = new(claimsPrincipal, EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME);
+ AuthenticateResult success = AuthenticateResult.Success(ticket);
+ return Task.FromResult(success);
+ }
+ }
+ // Try another handler
+ return Task.FromResult(AuthenticateResult.NoResult());
+ }
+ }
+}
diff --git a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
index 956df5a5b1..f403db05fd 100644
--- a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
+++ b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
@@ -97,14 +97,82 @@ string s when string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(_cosmosDb.Contain
public TableDefinition GetTableDefinition(string entityName)
{
- throw new NotSupportedException("Cosmos backends don't support direct table definitions. Definitions are provided via the GraphQL schema");
+ if (!EntityToDatabaseObject.TryGetValue(entityName, out DatabaseObject? databaseObject))
+ {
+ throw new InvalidCastException($"Table Definition for {entityName} has not been inferred.");
+ }
+
+ return databaseObject!.TableDefinition;
}
public Task InitializeAsync()
{
+ GenerateDatabaseObjectForEntities();
return Task.CompletedTask;
}
+ ///
+ /// Create a DatabaseObject for all the exposed entities.
+ ///
+ private void GenerateDatabaseObjectForEntities()
+ {
+ string schemaName, dbObjectName;
+ Dictionary sourceObjects = new();
+ foreach ((string entityName, Entity entity)
+ in _entities)
+ {
+ if (!EntityToDatabaseObject.ContainsKey(entityName))
+ {
+ // Reuse the same Database object for multiple entities if they share the same source.
+ if (!sourceObjects.TryGetValue(entity.GetSourceName(), out DatabaseObject? sourceObject))
+ {
+ // parse source name into a tuple of (schemaName, databaseObjectName)
+ (schemaName, dbObjectName) = ParseSchemaAndDbObjectName(entity.GetSourceName())!;
+ sourceObject = new()
+ {
+ SchemaName = schemaName,
+ Name = dbObjectName,
+ TableDefinition = new()
+ };
+
+ sourceObjects.Add(entity.GetSourceName(), sourceObject);
+ }
+
+ EntityToDatabaseObject.Add(entityName, sourceObject);
+ }
+ }
+ }
+
+ ///
+ /// Helper function will parse the schema and database object name
+ /// from the provided and string and sort out if a default schema
+ /// should be used. It then returns the appropriate schema and
+ /// db object name as a tuple of strings.
+ ///
+ /// source string to parse
+ ///
+ ///
+ public (string, string) ParseSchemaAndDbObjectName(string source)
+ {
+ (string? schemaName, string dbObjectName) = EntitySourceNamesParser.ParseSchemaAndTable(source)!;
+
+ if (string.IsNullOrEmpty(schemaName))
+ {
+ throw new DataGatewayException(message: $"Missing database name for entity name: {source} in Config file for Cosmos",
+ statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
+ subStatusCode: DataGatewayException.SubStatusCodes.ErrorInInitialization);
+ }
+
+ if (string.IsNullOrEmpty(dbObjectName))
+ {
+ throw new DataGatewayException(message: $"Missing container name for entity name: {source} in Config file for Cosmos",
+ statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
+ subStatusCode: DataGatewayException.SubStatusCodes.ErrorInInitialization);
+ }
+
+ return (schemaName, dbObjectName);
+ }
+
public string GraphQLSchema()
{
if (_cosmosDb.GraphQLSchema is null && _fileSystem.File.Exists(_cosmosDb.GraphQLSchemaPath))
diff --git a/DataGateway.Service/Startup.cs b/DataGateway.Service/Startup.cs
index 73ec7241f1..036cc7721c 100644
--- a/DataGateway.Service/Startup.cs
+++ b/DataGateway.Service/Startup.cs
@@ -322,7 +322,7 @@ private void ConfigureAuthentication(IServiceCollection services)
runtimeConfig.IsEasyAuthAuthenticationProvider())
{
services.AddAuthentication(EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication();
+ .AddEasyAuthAuthentication(runtimeConfig.AuthNConfig.Provider);
}
}
diff --git a/DataGateway.Service/hawaii-config.Cosmos.json b/DataGateway.Service/hawaii-config.Cosmos.json
index 38eae70365..40be5b95f3 100644
--- a/DataGateway.Service/hawaii-config.Cosmos.json
+++ b/DataGateway.Service/hawaii-config.Cosmos.json
@@ -25,34 +25,49 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth"
+ "provider": "StaticWebApps"
}
}
},
"entities": {
"Planet": {
- "source": "planet",
+ "source": "graphqldb.planet",
+ "rest": false,
+ "graphql": true,
"permissions": [
{
"role": "anonymous",
- "actions": [ "*" ]
+ "actions": [ "create", "read", "update", "delete" ]
},
{
"role": "authenticated",
- "actions": [ "*" ]
+ "actions": [ "create", "read", "update", "delete" ]
}
]
},
"Character": {
- "source": "planet",
+ "source": "graphqldb.character",
+ "rest": false,
+ "graphql": true,
+ "permissions": [
+ {
+ "role": "authenticated",
+ "actions": [ "create", "read", "update", "delete" ]
+ }
+ ]
+ },
+ "Star": {
+ "source": "graphqldb.star",
+ "rest": false,
+ "graphql": true,
"permissions": [
{
"role": "anonymous",
- "actions": [ "*" ]
+ "actions": [ "create", "read", "update", "delete" ]
},
{
"role": "authenticated",
- "actions": [ "*" ]
+ "actions": [ "create", "read", "update", "delete" ]
}
]
}
diff --git a/DataGateway.Service/hawaii-config.Cosmos.overrides.example.json b/DataGateway.Service/hawaii-config.Cosmos.overrides.example.json
index 8e5c3df511..6da127b02a 100644
--- a/DataGateway.Service/hawaii-config.Cosmos.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.Cosmos.overrides.example.json
@@ -2,7 +2,7 @@
"$schema": "../schemas/hawaii.draft-01.schema.json",
"data-source": {
"database-type": "cosmos",
- "connection-string": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
+ "connection-string": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
},
"cosmos": {
"database": "graphqldb",
@@ -25,7 +25,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth"
+ "provider": "StaticWebApps"
}
}
},
@@ -35,7 +35,7 @@
"permissions": [
{
"role": "anonymous",
- "actions": ["*"]
+ "actions": [ "*" ]
}
]
},
diff --git a/DataGateway.Service/hawaii-config.MsSql.json b/DataGateway.Service/hawaii-config.MsSql.json
index 1afc2a17e0..e24135468f 100644
--- a/DataGateway.Service/hawaii-config.MsSql.json
+++ b/DataGateway.Service/hawaii-config.MsSql.json
@@ -24,7 +24,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/hawaii-config.MsSql.overrides.example.json b/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
index 3f8b7f7b90..3cbe7eb7c2 100644
--- a/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
@@ -24,7 +24,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/hawaii-config.MySql.json b/DataGateway.Service/hawaii-config.MySql.json
index eaf6533447..37378d8ecc 100644
--- a/DataGateway.Service/hawaii-config.MySql.json
+++ b/DataGateway.Service/hawaii-config.MySql.json
@@ -21,7 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/hawaii-config.MySql.overrides.example.json b/DataGateway.Service/hawaii-config.MySql.overrides.example.json
index 1e0eb89e93..33c340b2c9 100644
--- a/DataGateway.Service/hawaii-config.MySql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.MySql.overrides.example.json
@@ -21,7 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/hawaii-config.PostgreSql.json b/DataGateway.Service/hawaii-config.PostgreSql.json
index d08608c2eb..04f68d8b5f 100644
--- a/DataGateway.Service/hawaii-config.PostgreSql.json
+++ b/DataGateway.Service/hawaii-config.PostgreSql.json
@@ -21,7 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json b/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
index 03efb7b17d..b1287f34a8 100644
--- a/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
@@ -21,13 +21,13 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
- }
}
}
+ }
}
},
"entities": {
diff --git a/DataGateway.Service/hawaii-config.json b/DataGateway.Service/hawaii-config.json
index 139a3da016..dbfc0054c0 100644
--- a/DataGateway.Service/hawaii-config.json
+++ b/DataGateway.Service/hawaii-config.json
@@ -21,7 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "EasyAuth",
+ "provider": "StaticWebApps",
"jwt": {
"audience": "",
"issuer": ""
diff --git a/DataGateway.Service/schema.gql b/DataGateway.Service/schema.gql
index 6a28900a23..cd1f8a7607 100644
--- a/DataGateway.Service/schema.gql
+++ b/DataGateway.Service/schema.gql
@@ -11,5 +11,11 @@ type Planet @model {
name : String,
character: Character,
age : Int,
- dimension : String
-}
+ dimension : String,
+ stars: [Star]
+}
+
+type Star @model {
+ id : ID,
+ name : String
+}
From 46f69bd5a739c90675ec416881665d0f0362a4f2 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Tue, 5 Jul 2022 17:37:21 -0400
Subject: [PATCH 02/10] Address pr comments with more comments.
---
DataGateway.Config/Authentication.cs | 1 +
.../EasyAuthAuthenticationUnitTests.cs | 2 +-
.../AppServiceAuthentication.cs | 6 +++---
.../EasyAuthAuthenticationBuilderExtensions.cs | 1 +
.../StaticWebAppsAuthentication.cs | 13 ++++++++++---
.../MetadataProviders/CosmosSqlMetadataProvider.cs | 3 ++-
6 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/DataGateway.Config/Authentication.cs b/DataGateway.Config/Authentication.cs
index d886cb3f69..ee6d035e5b 100644
--- a/DataGateway.Config/Authentication.cs
+++ b/DataGateway.Config/Authentication.cs
@@ -12,6 +12,7 @@ public record AuthenticationConfig(
string Provider,
Jwt? Jwt = null)
{
+ public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
public bool IsEasyAuthAuthenticationProvider()
{
return Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(Provider, StringComparison.OrdinalIgnoreCase)
diff --git a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
index 4af7d899ae..02d9d4af11 100644
--- a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
+++ b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
@@ -157,7 +157,7 @@ private static async Task SendRequestAndGetHttpContextState(string?
if (token is not null)
{
StringValues headerValue = new(new string[] { $"{token}" });
- KeyValuePair easyAuthHeader = new(AppServiceAuthentication.EASYAUTHHEADER, headerValue);
+ KeyValuePair easyAuthHeader = new(AuthenticationConfig.EASYAUTHHEADER, headerValue);
context.Request.Headers.Add(easyAuthHeader);
}
diff --git a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
index 34dae53408..ac0c4bf840 100644
--- a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
@@ -3,6 +3,7 @@
using System.Security.Claims;
using System.Text;
using System.Text.Json;
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
@@ -10,11 +11,10 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
{
///
/// Helper class which parses EasyAuth's injected headers into a ClaimsIdentity object.
- /// This class provides helper methods for StaticWebApp's Authentication feature: EasyAuth.
+ /// This class provides helper methods for AppService's Authentication feature: EasyAuth.
///
public static class AppServiceAuthentication
{
- public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
///
/// Representation of authenticated user principal Http header
/// injected by EasyAuth
@@ -53,7 +53,7 @@ public struct AppServiceClaim
{
ClaimsIdentity? identity = null;
- if (context.Request.Headers.TryGetValue(AppServiceAuthentication.EASYAUTHHEADER, out StringValues header))
+ if (context.Request.Headers.TryGetValue(AuthenticationConfig.EASYAUTHHEADER, out StringValues header))
{
try
{
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
index 1017c7af39..276dbffd51 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
@@ -15,6 +15,7 @@ public static class EasyAuthAuthenticationBuilderExtensions
/// Add authentication with Static Web Apps.
///
/// Authentication builder.
+ /// EasyAuth provider type. StaticWebApps or AppService
/// The builder, to chain commands.
public static AuthenticationBuilder AddEasyAuthAuthentication(
this AuthenticationBuilder builder, string easyAuthAuthenticationProvider)
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
index 3876ec8cab..ec78047d65 100644
--- a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
@@ -4,15 +4,22 @@
using System.Security.Claims;
using System.Text;
using System.Text.Json;
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
namespace Azure.DataGateway.Service.AuthenticationHelpers
{
+ ///
+ /// Helper class which parses EasyAuth's injected headers into a ClaimsIdentity object.
+ /// This class provides helper methods for StaticWebApps' Authentication feature: EasyAuth.
+ ///
public class StaticWebAppsAuthentication
{
- public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
-
+ ///
+ /// Link for reference of how StaticWebAppsClientPrincipal is defined
+ /// https://docs.microsoft.com/azure/static-web-apps/user-information?tabs=csharp#client-principal-data
+ ///
public class StaticWebAppsClientPrincipal
{
public string? IdentityProvider { get; set; }
@@ -27,7 +34,7 @@ public class StaticWebAppsClientPrincipal
StaticWebAppsClientPrincipal principal = new();
try
{
- if (req.Headers.TryGetValue(EASYAUTHHEADER, out StringValues header))
+ if (req.Headers.TryGetValue(AuthenticationConfig.EASYAUTHHEADER, out StringValues header))
{
string data = header[0];
byte[] decoded = Convert.FromBase64String(data);
diff --git a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
index f403db05fd..f3b4424cbc 100644
--- a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
+++ b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
@@ -145,9 +145,10 @@ private void GenerateDatabaseObjectForEntities()
///
/// Helper function will parse the schema and database object name
- /// from the provided and string and sort out if a default schema
+ /// from the provided source string and sort out if a default schema
/// should be used. It then returns the appropriate schema and
/// db object name as a tuple of strings.
+ /// i.e. source = 'graphqldb.planet' -> databaseName ='graphqldb'; containerName ='planet'
///
/// source string to parse
///
From 0a952fce5fdfd4f3bdb77f63bf96d9b2dc8499d8 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Wed, 6 Jul 2022 11:25:11 -0400
Subject: [PATCH 03/10] Clean up config files.
---
DataGateway.Config/Authentication.cs | 2 +-
DataGateway.Service/hawaii-config.MsSql.json | 6 +-----
.../hawaii-config.MsSql.overrides.example.json | 6 +-----
DataGateway.Service/hawaii-config.MySql.json | 6 +-----
.../hawaii-config.MySql.overrides.example.json | 6 +-----
DataGateway.Service/hawaii-config.PostgreSql.json | 6 +-----
.../hawaii-config.PostgreSql.overrides.example.json | 7 +------
DataGateway.Service/hawaii-config.json | 6 +-----
8 files changed, 8 insertions(+), 37 deletions(-)
diff --git a/DataGateway.Config/Authentication.cs b/DataGateway.Config/Authentication.cs
index ee6d035e5b..00c4590659 100644
--- a/DataGateway.Config/Authentication.cs
+++ b/DataGateway.Config/Authentication.cs
@@ -3,7 +3,7 @@ namespace Azure.DataGateway.Config
///
/// Authentication configuration.
///
- /// Identity Provider.
+ /// Identity Provider. Default is StaticWebApps.
/// With EasyAuth, no Audience or Issuer are expected.
///
/// Settings enabling validation of the received JWT token.
diff --git a/DataGateway.Service/hawaii-config.MsSql.json b/DataGateway.Service/hawaii-config.MsSql.json
index e24135468f..5dbfad2c4b 100644
--- a/DataGateway.Service/hawaii-config.MsSql.json
+++ b/DataGateway.Service/hawaii-config.MsSql.json
@@ -24,11 +24,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.MsSql.overrides.example.json b/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
index 3cbe7eb7c2..9f3c44f6d4 100644
--- a/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.MsSql.overrides.example.json
@@ -24,11 +24,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.MySql.json b/DataGateway.Service/hawaii-config.MySql.json
index 37378d8ecc..a41f9c28bd 100644
--- a/DataGateway.Service/hawaii-config.MySql.json
+++ b/DataGateway.Service/hawaii-config.MySql.json
@@ -21,11 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.MySql.overrides.example.json b/DataGateway.Service/hawaii-config.MySql.overrides.example.json
index 33c340b2c9..be4d25dd4a 100644
--- a/DataGateway.Service/hawaii-config.MySql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.MySql.overrides.example.json
@@ -21,11 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.PostgreSql.json b/DataGateway.Service/hawaii-config.PostgreSql.json
index 04f68d8b5f..417c28d46e 100644
--- a/DataGateway.Service/hawaii-config.PostgreSql.json
+++ b/DataGateway.Service/hawaii-config.PostgreSql.json
@@ -21,11 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json b/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
index b1287f34a8..03226d9433 100644
--- a/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
+++ b/DataGateway.Service/hawaii-config.PostgreSql.overrides.example.json
@@ -21,12 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
- }
+ "provider": "StaticWebApps"
}
}
},
diff --git a/DataGateway.Service/hawaii-config.json b/DataGateway.Service/hawaii-config.json
index dbfc0054c0..460ed37178 100644
--- a/DataGateway.Service/hawaii-config.json
+++ b/DataGateway.Service/hawaii-config.json
@@ -21,11 +21,7 @@
"allow-credentials": false
},
"authentication": {
- "provider": "StaticWebApps",
- "jwt": {
- "audience": "",
- "issuer": ""
- }
+ "provider": "StaticWebApps"
}
}
},
From ab347a48253b18bba41adc7dc12d3082bbddec8c Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Mon, 11 Jul 2022 10:35:28 -0400
Subject: [PATCH 04/10] Address PR comments
---
DataGateway.Config/Authentication.cs | 2 +-
.../Authentication/EasyAuthAuthenticationUnitTests.cs | 2 +-
.../AuthenticationHelpers/AppServiceAuthentication.cs | 4 ++--
.../AppServiceAuthenticationHandler.cs | 5 ++---
.../AuthenticationHelpers/StaticWebAppsAuthentication.cs | 2 +-
.../StaticWebAppsAuthenticationHandler.cs | 9 ++++-----
6 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/DataGateway.Config/Authentication.cs b/DataGateway.Config/Authentication.cs
index 00c4590659..c4cf1d3b32 100644
--- a/DataGateway.Config/Authentication.cs
+++ b/DataGateway.Config/Authentication.cs
@@ -12,7 +12,7 @@ public record AuthenticationConfig(
string Provider,
Jwt? Jwt = null)
{
- public const string EASYAUTHHEADER = "X-MS-CLIENT-PRINCIPAL";
+ public const string CLIENT_PRINCIPAL_HEADER = "X-MS-CLIENT-PRINCIPAL";
public bool IsEasyAuthAuthenticationProvider()
{
return Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(Provider, StringComparison.OrdinalIgnoreCase)
diff --git a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
index 02d9d4af11..eb28bfbd54 100644
--- a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
+++ b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
@@ -157,7 +157,7 @@ private static async Task SendRequestAndGetHttpContextState(string?
if (token is not null)
{
StringValues headerValue = new(new string[] { $"{token}" });
- KeyValuePair easyAuthHeader = new(AuthenticationConfig.EASYAUTHHEADER, headerValue);
+ KeyValuePair easyAuthHeader = new(AuthenticationConfig.CLIENT_PRINCIPAL_HEADER, headerValue);
context.Request.Headers.Add(easyAuthHeader);
}
diff --git a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
index ac0c4bf840..05b8b63cab 100644
--- a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthentication.cs
@@ -53,7 +53,7 @@ public struct AppServiceClaim
{
ClaimsIdentity? identity = null;
- if (context.Request.Headers.TryGetValue(AuthenticationConfig.EASYAUTHHEADER, out StringValues header))
+ if (context.Request.Headers.TryGetValue(AuthenticationConfig.CLIENT_PRINCIPAL_HEADER, out StringValues header))
{
try
{
@@ -77,7 +77,7 @@ public struct AppServiceClaim
// Logging the parsing failure exception to the console, but not rethrowing
// nor creating a DataGateway exception because the authentication handler
// will create and send a 401 unauthorized response to the client.
- Console.Error.WriteLine("Failure processing the AppServie EasyAuth header.");
+ Console.Error.WriteLine("Failure processing the AppService EasyAuth header.");
Console.Error.WriteLine(error.Message);
Console.Error.WriteLine(error.StackTrace);
}
diff --git a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
index 60c4d58d63..318f5e2ec1 100644
--- a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
@@ -1,6 +1,7 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -19,8 +20,6 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
///
public class AppServiceAuthenticationHandler : AuthenticationHandler
{
- private const string EASY_AUTH_HEADER = "X-MS-CLIENT-PRINCIPAL";
-
///
/// Constructor for the EasyAuthAuthenticationHandler.
/// Note the parameters are required by the base class.
@@ -47,7 +46,7 @@ ISystemClock clock
/// An authentication result to ASP.NET Core library authentication mechanisms
protected override Task HandleAuthenticateAsync()
{
- if (Context.Request.Headers[EASY_AUTH_HEADER].Count > 0)
+ if (Context.Request.Headers[AuthenticationConfig.CLIENT_PRINCIPAL_HEADER].Count > 0)
{
ClaimsIdentity? identity = AppServiceAuthentication.Parse(Context);
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
index ec78047d65..9c35599e88 100644
--- a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
@@ -34,7 +34,7 @@ public class StaticWebAppsClientPrincipal
StaticWebAppsClientPrincipal principal = new();
try
{
- if (req.Headers.TryGetValue(AuthenticationConfig.EASYAUTHHEADER, out StringValues header))
+ if (req.Headers.TryGetValue(AuthenticationConfig.CLIENT_PRINCIPAL_HEADER, out StringValues header))
{
string data = header[0];
byte[] decoded = Convert.FromBase64String(data);
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
index c3da9a1198..0f22cb2edc 100644
--- a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
@@ -1,6 +1,7 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -15,17 +16,15 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
/// - AuthenticateAsync: Authenticates the current request.
/// - Forbid Async: Creates 403 HTTP Response.
/// Usage modelled from Microsoft.Identity.Web.
- /// Ref: https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationHandler.cs
+ /// Ref: https://docs.microsoft.com/en-us/azure/static-web-apps/user-information?tabs=javascript
///
public class StaticWebAppsAuthenticationHandler : AuthenticationHandler
{
- private const string EASY_AUTH_HEADER = "X-MS-CLIENT-PRINCIPAL";
-
///
/// Constructor for the EasyAuthAuthenticationHandler.
/// Note the parameters are required by the base class.
///
- /// App service authentication options.
+ /// Static Web Apps authentication options.
/// Logger factory.
/// URL encoder.
/// System clock.
@@ -47,7 +46,7 @@ ISystemClock clock
/// An authentication result to ASP.NET Core library authentication mechanisms
protected override Task HandleAuthenticateAsync()
{
- if (Context.Request.Headers[EASY_AUTH_HEADER].Count > 0)
+ if (Context.Request.Headers[AuthenticationConfig.CLIENT_PRINCIPAL_HEADER].Count > 0)
{
ClaimsIdentity? identity = StaticWebAppsAuthentication.Parse(Context.Request);
From fa979e524c6303c8ec81fd103047f68ec9a1e3c4 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Mon, 11 Jul 2022 15:49:12 -0400
Subject: [PATCH 05/10] Refactor authorization handler
---
.../AppServiceAuthenticationHandler.cs | 72 -------------------
...EasyAuthAuthenticationBuilderExtensions.cs | 29 ++++----
...er.cs => EasyAuthAuthenticationHandler.cs} | 15 ++--
.../EasyAuthAuthenticationOptions.cs | 2 +
.../StaticWebAppsAuthentication.cs | 4 +-
.../CosmosSqlMetadataProvider.cs | 4 +-
6 files changed, 28 insertions(+), 98 deletions(-)
delete mode 100644 DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
rename DataGateway.Service/AuthenticationHelpers/{StaticWebAppsAuthenticationHandler.cs => EasyAuthAuthenticationHandler.cs} (84%)
diff --git a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
deleted file mode 100644
index 318f5e2ec1..0000000000
--- a/DataGateway.Service/AuthenticationHelpers/AppServiceAuthenticationHandler.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Azure.DataGateway.Config;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace Azure.DataGateway.Service.AuthenticationHelpers
-{
- ///
- /// This class is used to best integrate with ASP.NET Core AuthenticationHandler base class.
- /// Ref: https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationHandler.cs
- /// When "EasyAuth" is configured, this handler authenticates the user once per request,
- /// and utilizes the base class default handler for
- /// - AuthenticateAsync: Authenticates the current request.
- /// - Forbid Async: Creates 403 HTTP Response.
- /// Usage modelled from Microsoft.Identity.Web.
- /// Ref: https://github.com/AzureAD/microsoft-identity-web/blob/master/src/Microsoft.Identity.Web/AppServicesAuth/AppServicesAuthenticationHandler.cs
- ///
- public class AppServiceAuthenticationHandler : AuthenticationHandler
- {
- ///
- /// Constructor for the EasyAuthAuthenticationHandler.
- /// Note the parameters are required by the base class.
- ///
- /// App service authentication options.
- /// Logger factory.
- /// URL encoder.
- /// System clock.
- public AppServiceAuthenticationHandler(
- IOptionsMonitor options,
- ILoggerFactory logger,
- UrlEncoder encoder,
- ISystemClock clock
- ) : base(options, logger, encoder, clock)
- {
- }
-
- ///
- /// Gets any authentication data for a request. When an EasyAuth header is present,
- /// parses the header and authenticates the user within a ClaimsPrincipal object.
- /// The ClaimsPrincipal is a security principal usable by middleware to identify the
- /// authenticated user.
- ///
- /// An authentication result to ASP.NET Core library authentication mechanisms
- protected override Task HandleAuthenticateAsync()
- {
- if (Context.Request.Headers[AuthenticationConfig.CLIENT_PRINCIPAL_HEADER].Count > 0)
- {
- ClaimsIdentity? identity = AppServiceAuthentication.Parse(Context);
-
- if (identity is null)
- {
- return Task.FromResult(AuthenticateResult.Fail(failureMessage: "Invalid AppService EasyAuth token."));
- }
-
- ClaimsPrincipal? claimsPrincipal = new(identity);
- if (claimsPrincipal is not null)
- {
- // AuthenticationTicket is Asp.Net Core Abstraction of Authentication information
- // Ref: aspnetcore/src/Http/Authentication.Abstractions/src/AuthenticationTicket.cs
- AuthenticationTicket ticket = new(claimsPrincipal, EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME);
- AuthenticateResult success = AuthenticateResult.Success(ticket);
- return Task.FromResult(success);
- }
- }
- // Try another handler
- return Task.FromResult(AuthenticateResult.NoResult());
- }
- }
-}
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
index 276dbffd51..6061b49a74 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
@@ -25,22 +25,19 @@ public static AuthenticationBuilder AddEasyAuthAuthentication(
throw new System.ArgumentNullException(nameof(builder));
}
- if (Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
- {
- builder.AddScheme(
- authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- options => { });
- }
-
- if (Enum.GetName(EasyAuthType.AppService)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
- {
- builder.AddScheme(
- authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- options => { });
- }
-
+ builder.AddScheme(
+ authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
+ options => {
+ if (Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
+ {
+ options.EasyAuthProvider = EasyAuthType.StaticWebApps;
+ }
+ else if (Enum.GetName(EasyAuthType.AppService)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
+ {
+ options.EasyAuthProvider = EasyAuthType.AppService;
+ }
+ });
return builder;
}
}
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
similarity index 84%
rename from DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
rename to DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
index 0f22cb2edc..7330b7b3d9 100644
--- a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
@@ -15,10 +15,8 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
/// and utilizes the base class default handler for
/// - AuthenticateAsync: Authenticates the current request.
/// - Forbid Async: Creates 403 HTTP Response.
- /// Usage modelled from Microsoft.Identity.Web.
- /// Ref: https://docs.microsoft.com/en-us/azure/static-web-apps/user-information?tabs=javascript
///
- public class StaticWebAppsAuthenticationHandler : AuthenticationHandler
+ public class EasyAuthAuthenticationHandler : AuthenticationHandler
{
///
/// Constructor for the EasyAuthAuthenticationHandler.
@@ -28,7 +26,7 @@ public class StaticWebAppsAuthenticationHandler : AuthenticationHandlerLogger factory.
/// URL encoder.
/// System clock.
- public StaticWebAppsAuthenticationHandler(
+ public EasyAuthAuthenticationHandler(
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder,
@@ -48,11 +46,16 @@ protected override Task HandleAuthenticateAsync()
{
if (Context.Request.Headers[AuthenticationConfig.CLIENT_PRINCIPAL_HEADER].Count > 0)
{
- ClaimsIdentity? identity = StaticWebAppsAuthentication.Parse(Context.Request);
+ ClaimsIdentity? identity = Options.EasyAuthProvider switch
+ {
+ EasyAuthType.StaticWebApps => StaticWebAppsAuthentication.Parse(Context),
+ EasyAuthType.AppService => AppServiceAuthentication.Parse(Context),
+ _ => null
+ };
if (identity is null)
{
- return Task.FromResult(AuthenticateResult.Fail(failureMessage: "Invalid StaticWebApps EasyAuth token."));
+ return Task.FromResult(AuthenticateResult.Fail(failureMessage: $"Invalid {Options.EasyAuthProvider.ToString()} EasyAuth token."));
}
ClaimsPrincipal? claimsPrincipal = new(identity);
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationOptions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationOptions.cs
index 59a06e0381..62b21a26f9 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationOptions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationOptions.cs
@@ -1,3 +1,4 @@
+using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Authentication;
namespace Azure.DataGateway.Service.AuthenticationHelpers
@@ -12,5 +13,6 @@ namespace Azure.DataGateway.Service.AuthenticationHelpers
///
public class EasyAuthAuthenticationOptions : AuthenticationSchemeOptions
{
+ public EasyAuthType EasyAuthProvider { get; set; }
}
}
diff --git a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
index 9c35599e88..595ddac5cb 100644
--- a/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
+++ b/DataGateway.Service/AuthenticationHelpers/StaticWebAppsAuthentication.cs
@@ -28,13 +28,13 @@ public class StaticWebAppsClientPrincipal
public IEnumerable? UserRoles { get; set; }
}
- public static ClaimsIdentity? Parse(HttpRequest req)
+ public static ClaimsIdentity? Parse(HttpContext context)
{
ClaimsIdentity? identity = null;
StaticWebAppsClientPrincipal principal = new();
try
{
- if (req.Headers.TryGetValue(AuthenticationConfig.CLIENT_PRINCIPAL_HEADER, out StringValues header))
+ if (context.Request.Headers.TryGetValue(AuthenticationConfig.CLIENT_PRINCIPAL_HEADER, out StringValues header))
{
string data = header[0];
byte[] decoded = Convert.FromBase64String(data);
diff --git a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
index f3b4424cbc..9d853354e9 100644
--- a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
+++ b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
@@ -127,7 +127,7 @@ private void GenerateDatabaseObjectForEntities()
if (!sourceObjects.TryGetValue(entity.GetSourceName(), out DatabaseObject? sourceObject))
{
// parse source name into a tuple of (schemaName, databaseObjectName)
- (schemaName, dbObjectName) = ParseSchemaAndDbObjectName(entity.GetSourceName())!;
+ (schemaName, dbObjectName) = ParseDatabaseAndContainerName(entity.GetSourceName())!;
sourceObject = new()
{
SchemaName = schemaName,
@@ -153,7 +153,7 @@ private void GenerateDatabaseObjectForEntities()
/// source string to parse
///
///
- public (string, string) ParseSchemaAndDbObjectName(string source)
+ public (string, string) ParseDatabaseAndContainerName(string source)
{
(string? schemaName, string dbObjectName) = EntitySourceNamesParser.ParseSchemaAndTable(source)!;
From c0f353eb237fae75938b129668ca1989757a0241 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Mon, 11 Jul 2022 16:00:54 -0400
Subject: [PATCH 06/10] Refactor code
---
DataGateway.Config/Authentication.cs | 3 +--
.../EasyAuthAuthenticationBuilderExtensions.cs | 8 ++++----
DataGateway.Service/Startup.cs | 2 +-
3 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/DataGateway.Config/Authentication.cs b/DataGateway.Config/Authentication.cs
index c4cf1d3b32..46b7cd8af0 100644
--- a/DataGateway.Config/Authentication.cs
+++ b/DataGateway.Config/Authentication.cs
@@ -15,8 +15,7 @@ public record AuthenticationConfig(
public const string CLIENT_PRINCIPAL_HEADER = "X-MS-CLIENT-PRINCIPAL";
public bool IsEasyAuthAuthenticationProvider()
{
- return Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(Provider, StringComparison.OrdinalIgnoreCase)
- || Enum.GetName(EasyAuthType.AppService)!.Equals(Provider, StringComparison.OrdinalIgnoreCase);
+ return Enum.GetNames(typeof(EasyAuthType)).Any(x => x.Equals(Provider, StringComparison.OrdinalIgnoreCase));
}
}
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
index 6061b49a74..1c66e0e720 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
@@ -18,7 +18,7 @@ public static class EasyAuthAuthenticationBuilderExtensions
/// EasyAuth provider type. StaticWebApps or AppService
/// The builder, to chain commands.
public static AuthenticationBuilder AddEasyAuthAuthentication(
- this AuthenticationBuilder builder, string easyAuthAuthenticationProvider)
+ this AuthenticationBuilder builder, EasyAuthType easyAuthAuthenticationProvider)
{
if (builder is null)
{
@@ -29,11 +29,11 @@ public static AuthenticationBuilder AddEasyAuthAuthentication(
authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
options => {
- if (Enum.GetName(EasyAuthType.StaticWebApps)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
- {
+ if (easyAuthAuthenticationProvider is EasyAuthType.StaticWebApps)
+ {
options.EasyAuthProvider = EasyAuthType.StaticWebApps;
}
- else if (Enum.GetName(EasyAuthType.AppService)!.Equals(easyAuthAuthenticationProvider, StringComparison.OrdinalIgnoreCase))
+ else if (easyAuthAuthenticationProvider is EasyAuthType.AppService)
{
options.EasyAuthProvider = EasyAuthType.AppService;
}
diff --git a/DataGateway.Service/Startup.cs b/DataGateway.Service/Startup.cs
index 1f82e11958..21712b0819 100644
--- a/DataGateway.Service/Startup.cs
+++ b/DataGateway.Service/Startup.cs
@@ -321,7 +321,7 @@ private void ConfigureAuthentication(IServiceCollection services)
runtimeConfig.IsEasyAuthAuthenticationProvider())
{
services.AddAuthentication(EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication(runtimeConfig.AuthNConfig.Provider);
+ .AddEasyAuthAuthentication((EasyAuthType)Enum.Parse(typeof(EasyAuthType), runtimeConfig.AuthNConfig.Provider, true));
}
}
From 26cea5b5cf2bd621a4686100b4609538d30d1a71 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Mon, 11 Jul 2022 17:05:59 -0400
Subject: [PATCH 07/10] EntityToDatabaseObject is not needed for cosmos, clean
it up
---
.../EasyAuthAuthenticationUnitTests.cs | 2 +-
.../Authorization/AuthorizationHelpers.cs | 1 +
.../Authorization/AuthorizationResolver.cs | 5 ++
.../CosmosSqlMetadataProvider.cs | 71 +------------------
4 files changed, 8 insertions(+), 71 deletions(-)
diff --git a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
index eb28bfbd54..857432dc2a 100644
--- a/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
+++ b/DataGateway.Service.Tests/Authentication/EasyAuthAuthenticationUnitTests.cs
@@ -111,7 +111,7 @@ private static async Task CreateWebHostEasyAuth(EasyAuthType easyAuthType
.ConfigureServices(services =>
{
services.AddAuthentication(defaultScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication(easyAuthType.ToString());
+ .AddEasyAuthAuthentication(easyAuthType);
services.AddAuthorization();
})
diff --git a/DataGateway.Service.Tests/Authorization/AuthorizationHelpers.cs b/DataGateway.Service.Tests/Authorization/AuthorizationHelpers.cs
index 457b725df6..252a3421b0 100644
--- a/DataGateway.Service.Tests/Authorization/AuthorizationHelpers.cs
+++ b/DataGateway.Service.Tests/Authorization/AuthorizationHelpers.cs
@@ -33,6 +33,7 @@ public static AuthorizationResolver InitAuthorizationResolver(RuntimeConfig runt
Mock metadataProvider = new();
TableDefinition sampleTable = CreateSampleTable();
metadataProvider.Setup(x => x.GetTableDefinition(TEST_ENTITY)).Returns(sampleTable);
+ metadataProvider.Setup(x => x.GetDatabaseType()).Returns(DatabaseType.mssql);
string outParam;
Dictionary> _exposedNameToBackingColumnMapping = CreateColumnMappingTable();
diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs
index 416ff9e988..66aa55eea2 100644
--- a/DataGateway.Service/Authorization/AuthorizationResolver.cs
+++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs
@@ -521,6 +521,11 @@ public IEnumerable GetRolesForField(string entityName, string actionName
/// Collection of columns in table definition.
private IEnumerable ResolveTableDefinitionColumns(string entityName)
{
+ if (_metadataProvider.GetDatabaseType() is DatabaseType.cosmos)
+ {
+ return new List();
+ }
+
return _metadataProvider.GetTableDefinition(entityName).Columns.Keys;
}
#endregion
diff --git a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
index 9d853354e9..956df5a5b1 100644
--- a/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
+++ b/DataGateway.Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs
@@ -97,83 +97,14 @@ string s when string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(_cosmosDb.Contain
public TableDefinition GetTableDefinition(string entityName)
{
- if (!EntityToDatabaseObject.TryGetValue(entityName, out DatabaseObject? databaseObject))
- {
- throw new InvalidCastException($"Table Definition for {entityName} has not been inferred.");
- }
-
- return databaseObject!.TableDefinition;
+ throw new NotSupportedException("Cosmos backends don't support direct table definitions. Definitions are provided via the GraphQL schema");
}
public Task InitializeAsync()
{
- GenerateDatabaseObjectForEntities();
return Task.CompletedTask;
}
- ///
- /// Create a DatabaseObject for all the exposed entities.
- ///
- private void GenerateDatabaseObjectForEntities()
- {
- string schemaName, dbObjectName;
- Dictionary sourceObjects = new();
- foreach ((string entityName, Entity entity)
- in _entities)
- {
- if (!EntityToDatabaseObject.ContainsKey(entityName))
- {
- // Reuse the same Database object for multiple entities if they share the same source.
- if (!sourceObjects.TryGetValue(entity.GetSourceName(), out DatabaseObject? sourceObject))
- {
- // parse source name into a tuple of (schemaName, databaseObjectName)
- (schemaName, dbObjectName) = ParseDatabaseAndContainerName(entity.GetSourceName())!;
- sourceObject = new()
- {
- SchemaName = schemaName,
- Name = dbObjectName,
- TableDefinition = new()
- };
-
- sourceObjects.Add(entity.GetSourceName(), sourceObject);
- }
-
- EntityToDatabaseObject.Add(entityName, sourceObject);
- }
- }
- }
-
- ///
- /// Helper function will parse the schema and database object name
- /// from the provided source string and sort out if a default schema
- /// should be used. It then returns the appropriate schema and
- /// db object name as a tuple of strings.
- /// i.e. source = 'graphqldb.planet' -> databaseName ='graphqldb'; containerName ='planet'
- ///
- /// source string to parse
- ///
- ///
- public (string, string) ParseDatabaseAndContainerName(string source)
- {
- (string? schemaName, string dbObjectName) = EntitySourceNamesParser.ParseSchemaAndTable(source)!;
-
- if (string.IsNullOrEmpty(schemaName))
- {
- throw new DataGatewayException(message: $"Missing database name for entity name: {source} in Config file for Cosmos",
- statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
- subStatusCode: DataGatewayException.SubStatusCodes.ErrorInInitialization);
- }
-
- if (string.IsNullOrEmpty(dbObjectName))
- {
- throw new DataGatewayException(message: $"Missing container name for entity name: {source} in Config file for Cosmos",
- statusCode: System.Net.HttpStatusCode.ServiceUnavailable,
- subStatusCode: DataGatewayException.SubStatusCodes.ErrorInInitialization);
- }
-
- return (schemaName, dbObjectName);
- }
-
public string GraphQLSchema()
{
if (_cosmosDb.GraphQLSchema is null && _fileSystem.File.Exists(_cosmosDb.GraphQLSchemaPath))
From 28aec601323c235f5d41b13bffe6eafa90bae1a3 Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Mon, 11 Jul 2022 22:09:40 -0400
Subject: [PATCH 08/10] Fix format
---
.../EasyAuthAuthenticationBuilderExtensions.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
index 1c66e0e720..2b9cec9230 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationBuilderExtensions.cs
@@ -1,4 +1,3 @@
-using System;
using Azure.DataGateway.Config;
using Microsoft.AspNetCore.Authentication;
@@ -28,9 +27,10 @@ public static AuthenticationBuilder AddEasyAuthAuthentication(
builder.AddScheme(
authenticationScheme: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
displayName: EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME,
- options => {
+ options =>
+ {
if (easyAuthAuthenticationProvider is EasyAuthType.StaticWebApps)
- {
+ {
options.EasyAuthProvider = EasyAuthType.StaticWebApps;
}
else if (easyAuthAuthenticationProvider is EasyAuthType.AppService)
From f386733754f6b4eab99166913ea5f719c7924aaf Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Tue, 12 Jul 2022 09:25:13 -0400
Subject: [PATCH 09/10] address pr comment
---
.../AuthenticationHelpers/EasyAuthAuthenticationHandler.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
index 7330b7b3d9..5a51cef584 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
@@ -55,7 +55,7 @@ protected override Task HandleAuthenticateAsync()
if (identity is null)
{
- return Task.FromResult(AuthenticateResult.Fail(failureMessage: $"Invalid {Options.EasyAuthProvider.ToString()} EasyAuth token."));
+ return Task.FromResult(AuthenticateResult.Fail(failureMessage: $"Invalid {Options.EasyAuthProvider} EasyAuth token."));
}
ClaimsPrincipal? claimsPrincipal = new(identity);
From 25fb6493ded6dced24319279aa7f07578387e24a Mon Sep 17 00:00:00 2001
From: tarazou9 <40870773+tarazou9@users.noreply.github.com>
Date: Tue, 12 Jul 2022 10:20:34 -0400
Subject: [PATCH 10/10] pr comments
---
.../AuthenticationHelpers/EasyAuthAuthenticationHandler.cs | 2 +-
DataGateway.Service/Startup.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
index 5a51cef584..8ff5ee2eab 100644
--- a/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
+++ b/DataGateway.Service/AuthenticationHelpers/EasyAuthAuthenticationHandler.cs
@@ -22,7 +22,7 @@ public class EasyAuthAuthenticationHandler : AuthenticationHandler
- /// Static Web Apps authentication options.
+ /// Easy Auth authentication options.
/// Logger factory.
/// URL encoder.
/// System clock.
diff --git a/DataGateway.Service/Startup.cs b/DataGateway.Service/Startup.cs
index c1d00fc2af..67a90fbdff 100644
--- a/DataGateway.Service/Startup.cs
+++ b/DataGateway.Service/Startup.cs
@@ -333,7 +333,7 @@ private void ConfigureAuthentication(IServiceCollection services)
runtimeConfig.IsEasyAuthAuthenticationProvider())
{
services.AddAuthentication(EasyAuthAuthenticationDefaults.AUTHENTICATIONSCHEME)
- .AddEasyAuthAuthentication((EasyAuthType)Enum.Parse(typeof(EasyAuthType), runtimeConfig.AuthNConfig.Provider, true));
+ .AddEasyAuthAuthentication((EasyAuthType)Enum.Parse(typeof(EasyAuthType), runtimeConfig.AuthNConfig.Provider, ignoreCase: true));
}
}