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 diff --git a/src/Service.Tests/Authorization/AuthorizationHelpers.cs b/src/Service.Tests/Authorization/AuthorizationHelpers.cs index ee9bae29e6..d6166f4ace 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..a04b4b1221 100644 --- a/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs +++ b/src/Service.Tests/Unittests/ConfigValidationUnitTests.cs @@ -593,6 +593,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(RuntimeConfigValidator.INVALID_CLAIMS_IN_POLICY_ERR_MSG, 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..06cf1cc85b 100644 --- a/src/Service/Configurations/RuntimeConfigValidator.cs +++ b/src/Service/Configurations/RuntimeConfigValidator.cs @@ -6,6 +6,7 @@ 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; @@ -41,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, @@ -676,10 +680,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 +712,18 @@ private static void ValidateClaimsInPolicy(string policy) subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError ); } + + if (isStaticWebAppsAuthConfigured && + !(typeOfClaim.Equals(StaticWebAppsAuthentication.USER_ID_CLAIM) || + typeOfClaim.Equals(StaticWebAppsAuthentication.USER_DETAILS_CLAIM))) + { + // Not a valid claimType containing allowed characters + throw new DataApiBuilderException( + message: INVALID_CLAIMS_IN_POLICY_ERR_MSG, + statusCode: System.Net.HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ConfigValidationError + ); + } } // MatchType claimType }