From cac6b8c31ee965936ddd5b8387e43182ab6b9485 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 6 Dec 2022 13:40:24 +0530 Subject: [PATCH 1/4] failing validation for invalid claims in SWA --- .../Authorization/AuthorizationHelpers.cs | 20 ++++++++-- .../Unittests/ConfigValidationUnitTests.cs | 39 +++++++++++++++++++ .../Configurations/RuntimeConfigValidator.cs | 20 +++++++++- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/Service.Tests/Authorization/AuthorizationHelpers.cs b/src/Service.Tests/Authorization/AuthorizationHelpers.cs index ee9bae29e6..c0674f0222 100644 --- a/src/Service.Tests/Authorization/AuthorizationHelpers.cs +++ b/src/Service.Tests/Authorization/AuthorizationHelpers.cs @@ -64,7 +64,8 @@ public static RuntimeConfig InitRuntimeConfig( HashSet? includedCols = null, HashSet? excludedCols = null, string? databasePolicy = null, - string? requestPolicy = null + string? requestPolicy = null, + string authProvider = "AppService" ) { Field? fieldsForRole = null; @@ -102,16 +103,27 @@ public static RuntimeConfig InitRuntimeConfig( Mappings: null ); - Dictionary entityMap = new(); - entityMap.Add(entityName, sampleEntity); + Dictionary entityMap = new() + { + { entityName, sampleEntity } + }; + + // Create runtime settings for the config. + Dictionary runtimeSettings = new(); + AuthenticationConfig authenticationConfig = new(Provider: authProvider); + HostGlobalSettings hostGlobal = new(Authentication: authenticationConfig); + JsonElement hostGlobalJson = JsonSerializer.SerializeToElement(hostGlobal); + runtimeSettings.Add(GlobalSettingsType.Host, hostGlobalJson); RuntimeConfig runtimeConfig = new( Schema: "UnitTestSchema", DataSource: new DataSource(DatabaseType: DatabaseType.mssql), - RuntimeSettings: new Dictionary(), + RuntimeSettings: runtimeSettings, Entities: entityMap ); + runtimeConfig.DetermineGlobalSettings(); + return runtimeConfig; } diff --git a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs index 8b11eab86e..d2bae35196 100644 --- a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs +++ b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Net; using System.Text.Json; @@ -593,6 +594,44 @@ public void ParseInvalidDbPolicyWithInvalidClaimTypeFormat(string policy) Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ConfigValidationError, ex.SubStatusCode); } + /// + /// Test to validate that only and only for SWA, if claims other than "userId" and + /// "userDetails" are referenced in the database policy, we fail the validation. + /// + /// Authentication provider like AppService, StaticWebApps. + /// Database policy defined for action. + /// The action for which database policy is defined. + /// Boolean value indicating whether an exception is expected or not. + [DataTestMethod] + [DataRow("StaticWebApps", "@claims.userId eq @item.col2", Operation.Read, false, DisplayName = "SWA- Database Policy defined for Read passes")] + [DataRow("staticwebapps", "@claims.userDetails eq @item.col3", Operation.Update, false, DisplayName = "SWA- Database Policy defined for Update passes")] + [DataRow("StaticWebAPPs", "@claims.email eq @item.col3", Operation.Delete, true, DisplayName = "SWA- Database Policy defined for Delete fails")] + [DataRow("appService", "@claims.email eq @item.col3", Operation.Delete, false, DisplayName = "AppService- Database Policy defined for Delete passes")] + public void TestInvalidClaimsForStaticWebApps(string authProvider, string dbPolicy, Operation action, bool errorExpected) + { + RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig( + entityName: AuthorizationHelpers.TEST_ENTITY, + roleName: AuthorizationHelpers.TEST_ROLE, + operation: action, + includedCols: new HashSet { "col1", "col2", "col3" }, + databasePolicy: dbPolicy, + authProvider: authProvider.ToString() + ); + RuntimeConfigValidator configValidator = AuthenticationConfigValidatorUnitTests.GetMockConfigValidator(ref runtimeConfig); + try + { + configValidator.ValidatePermissionsInConfig(runtimeConfig); + Assert.IsFalse(errorExpected); + } + catch (DataApiBuilderException ex) + { + Assert.IsTrue(errorExpected); + Assert.AreEqual(HttpStatusCode.ServiceUnavailable, ex.StatusCode); + Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ConfigValidationError, ex.SubStatusCode); + Assert.AreEqual("One or more claim types supplied in the database policy are not supported.", ex.Message); + } + } + /// /// Test to validate that wildcard action passes all stages of config validation. /// diff --git a/src/Service/Configurations/RuntimeConfigValidator.cs b/src/Service/Configurations/RuntimeConfigValidator.cs index 431659e2df..6d7ffe3be6 100644 --- a/src/Service/Configurations/RuntimeConfigValidator.cs +++ b/src/Service/Configurations/RuntimeConfigValidator.cs @@ -6,12 +6,15 @@ using System.Text.Json; using System.Text.RegularExpressions; using Azure.DataApiBuilder.Config; +using Azure.DataApiBuilder.Service.AuthenticationHelpers; using Azure.DataApiBuilder.Service.Authorization; using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder; using Azure.DataApiBuilder.Service.Models; using Azure.DataApiBuilder.Service.Services; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using static Azure.DataApiBuilder.Service.Exceptions.DataApiBuilderException; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using PermissionOperation = Azure.DataApiBuilder.Config.PermissionOperation; @@ -676,10 +679,13 @@ private static string ProcessFieldsInPolicy(string policy) /// The policy to be validated and processed. /// Processed policy /// Throws exception when one or the other validations fail. - private static void ValidateClaimsInPolicy(string policy) + private void ValidateClaimsInPolicy(string policy) { // Find all the claimTypes from the policy MatchCollection claimTypes = GetClaimTypesInPolicy(policy); + RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetRuntimeConfiguration(); + bool isStaticWebAppsAuthConfigured = Enum.TryParse(runtimeConfig.AuthNConfig!.Provider, ignoreCase: true, out EasyAuthType easyAuthMode) ? + easyAuthMode is EasyAuthType.StaticWebApps : false; foreach (Match claimType in claimTypes) { @@ -705,6 +711,18 @@ private static void ValidateClaimsInPolicy(string policy) subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError ); } + + if (isStaticWebAppsAuthConfigured && + !(typeOfClaim.Equals(StaticWebAppsAuthentication.USER_ID_CLAIM, StringComparison.OrdinalIgnoreCase) || + typeOfClaim.Equals(StaticWebAppsAuthentication.USER_DETAILS_CLAIM, StringComparison.OrdinalIgnoreCase))) + { + // Not a valid claimType containing allowed characters + throw new DataApiBuilderException( + message: $"One or more claim types supplied in the database policy are not supported.", + statusCode: System.Net.HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError + ); + } } // MatchType claimType } From b45af8bc7de345a06226f68cddd0efe57cf18ad5 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 6 Dec 2022 14:06:06 +0530 Subject: [PATCH 2/4] fixing tests --- src/Service.Tests/Authorization/AuthorizationHelpers.cs | 2 +- src/Service.Tests/Unittests/ConfigValidationUnitTests.cs | 1 - src/Service/Configurations/RuntimeConfigValidator.cs | 6 ++---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Service.Tests/Authorization/AuthorizationHelpers.cs b/src/Service.Tests/Authorization/AuthorizationHelpers.cs index c0674f0222..d6166f4ace 100644 --- a/src/Service.Tests/Authorization/AuthorizationHelpers.cs +++ b/src/Service.Tests/Authorization/AuthorizationHelpers.cs @@ -107,7 +107,7 @@ public static RuntimeConfig InitRuntimeConfig( { { entityName, sampleEntity } }; - + // Create runtime settings for the config. Dictionary runtimeSettings = new(); AuthenticationConfig authenticationConfig = new(Provider: authProvider); diff --git a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs index d2bae35196..940fee3929 100644 --- a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs +++ b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Net; using System.Text.Json; diff --git a/src/Service/Configurations/RuntimeConfigValidator.cs b/src/Service/Configurations/RuntimeConfigValidator.cs index 6d7ffe3be6..635aa53789 100644 --- a/src/Service/Configurations/RuntimeConfigValidator.cs +++ b/src/Service/Configurations/RuntimeConfigValidator.cs @@ -12,9 +12,7 @@ using Azure.DataApiBuilder.Service.GraphQLBuilder; using Azure.DataApiBuilder.Service.Models; using Azure.DataApiBuilder.Service.Services; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using static Azure.DataApiBuilder.Service.Exceptions.DataApiBuilderException; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using PermissionOperation = Azure.DataApiBuilder.Config.PermissionOperation; @@ -713,8 +711,8 @@ private void ValidateClaimsInPolicy(string policy) } if (isStaticWebAppsAuthConfigured && - !(typeOfClaim.Equals(StaticWebAppsAuthentication.USER_ID_CLAIM, StringComparison.OrdinalIgnoreCase) || - typeOfClaim.Equals(StaticWebAppsAuthentication.USER_DETAILS_CLAIM, StringComparison.OrdinalIgnoreCase))) + !(typeOfClaim.Equals(StaticWebAppsAuthentication.USER_ID_CLAIM) || + typeOfClaim.Equals(StaticWebAppsAuthentication.USER_DETAILS_CLAIM))) { // Not a valid claimType containing allowed characters throw new DataApiBuilderException( From 84833e361c60053b1dc14015eb385190a0035ae5 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 6 Dec 2022 14:14:47 +0530 Subject: [PATCH 3/4] updating commands files --- ConfigGenerators/MsSqlCommands.txt | 2 +- ConfigGenerators/MySqlCommands.txt | 2 +- ConfigGenerators/PostgreSqlCommands.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ConfigGenerators/MsSqlCommands.txt b/ConfigGenerators/MsSqlCommands.txt index c73ae78027..a7b87c5afa 100644 --- a/ConfigGenerators/MsSqlCommands.txt +++ b/ConfigGenerators/MsSqlCommands.txt @@ -83,7 +83,7 @@ update Book --config "dab-config.MsSql.json" --permissions "policy_tester_08:rea update Review --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" update Review --config "dab-config.MsSql.json" --relationship books --target.entity Book --cardinality one update BookWebsitePlacement --config "dab-config.MsSql.json" --permissions "authenticated:create,update" --rest true --graphql true -update BookWebsitePlacement --config "dab-config.MsSql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.id eq @item.id" +update BookWebsitePlacement --config "dab-config.MsSql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.userId eq @item.id" update Author --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true update WebsiteUser --config "dab-config.MsSql.json" --permissions "authenticated:create,read,delete,update" --rest false --graphql "websiteUser:websiteUsers" update stocks_price --config "dab-config.MsSql.json" --permissions "authenticated:create,read,delete,update" --rest false diff --git a/ConfigGenerators/MySqlCommands.txt b/ConfigGenerators/MySqlCommands.txt index c00076b5f3..52aa23a7a0 100644 --- a/ConfigGenerators/MySqlCommands.txt +++ b/ConfigGenerators/MySqlCommands.txt @@ -77,7 +77,7 @@ update Review --config "dab-config.MySql.json" --permissions "authenticated:crea update Review --config "dab-config.MySql.json" --relationship books --target.entity Book --cardinality one update Empty --config "dab-config.MySql.json" --permissions "anonymous:read" update BookWebsitePlacement --config "dab-config.MySql.json" --permissions "authenticated:create,update" --rest true --graphql true -update BookWebsitePlacement --config "dab-config.MySql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.id eq @item.id" +update BookWebsitePlacement --config "dab-config.MySql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.userId eq @item.id" update Author --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true update WebsiteUser --config "dab-config.MySql.json" --permissions "authenticated:create,read,delete,update" --rest false --graphql "websiteUser:websiteUsers" update stocks_price --config "dab-config.MySql.json" --permissions "authenticated:create,read,delete,update" --rest false diff --git a/ConfigGenerators/PostgreSqlCommands.txt b/ConfigGenerators/PostgreSqlCommands.txt index 7b9cf35326..a7cafb7a82 100644 --- a/ConfigGenerators/PostgreSqlCommands.txt +++ b/ConfigGenerators/PostgreSqlCommands.txt @@ -77,7 +77,7 @@ update Review --config "dab-config.PostgreSql.json" --permissions "authenticated update Review --config "dab-config.PostgreSql.json" --relationship books --target.entity Book --cardinality one update Empty --config "dab-config.PostgreSql.json" --permissions "anonymous:read" update BookWebsitePlacement --config "dab-config.PostgreSql.json" --permissions "authenticated:create,update" --rest true --graphql true -update BookWebsitePlacement --config "dab-config.PostgreSql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.id eq @item.id" +update BookWebsitePlacement --config "dab-config.PostgreSql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.userId eq @item.id" update Author --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true update WebsiteUser --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,delete,update" --rest false --graphql "websiteUser:websiteUsers" update stocks_price --config "dab-config.PostgreSql.json" --permissions "authenticated:create,read,delete,update" --rest false From d2ed3dfc7f62da62ebfeb2bb010cabb2130a9370 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 6 Dec 2022 14:55:29 +0530 Subject: [PATCH 4/4] using constant for error message --- src/Service.Tests/Unittests/ConfigValidationUnitTests.cs | 2 +- src/Service/Configurations/RuntimeConfigValidator.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs index 940fee3929..a04b4b1221 100644 --- a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs +++ b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs @@ -627,7 +627,7 @@ public void TestInvalidClaimsForStaticWebApps(string authProvider, string dbPoli Assert.IsTrue(errorExpected); Assert.AreEqual(HttpStatusCode.ServiceUnavailable, ex.StatusCode); Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ConfigValidationError, ex.SubStatusCode); - Assert.AreEqual("One or more claim types supplied in the database policy are not supported.", ex.Message); + Assert.AreEqual(RuntimeConfigValidator.INVALID_CLAIMS_IN_POLICY_ERR_MSG, ex.Message); } } diff --git a/src/Service/Configurations/RuntimeConfigValidator.cs b/src/Service/Configurations/RuntimeConfigValidator.cs index 635aa53789..06cf1cc85b 100644 --- a/src/Service/Configurations/RuntimeConfigValidator.cs +++ b/src/Service/Configurations/RuntimeConfigValidator.cs @@ -42,6 +42,9 @@ public class RuntimeConfigValidator : IConfigValidator // specify the action name. private static readonly string _actionKey = "action"; + // Error messages. + public const string INVALID_CLAIMS_IN_POLICY_ERR_MSG = "One or more claim types supplied in the database policy are not supported."; + public RuntimeConfigValidator( RuntimeConfigProvider runtimeConfigProvider, IFileSystem fileSystem, @@ -716,7 +719,7 @@ private void ValidateClaimsInPolicy(string policy) { // Not a valid claimType containing allowed characters throw new DataApiBuilderException( - message: $"One or more claim types supplied in the database policy are not supported.", + message: INVALID_CLAIMS_IN_POLICY_ERR_MSG, statusCode: System.Net.HttpStatusCode.ServiceUnavailable, subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError );