diff --git a/src/Auth/IAuthorizationResolver.cs b/src/Auth/IAuthorizationResolver.cs
index c990611f9a..6499eb0632 100644
--- a/src/Auth/IAuthorizationResolver.cs
+++ b/src/Auth/IAuthorizationResolver.cs
@@ -82,6 +82,9 @@ public interface IAuthorizationResolver
/// The policy text and typed claim values to bind to it.
public ResolvedDatabasePolicy ResolveDBPolicy(string entityName, string roleName, EntityActionOperation operation, HttpContext httpContext);
+ /// Audits a configured policy that produced no predicate and rejects the request outside development mode.
+ public void AuditDatabasePolicyFailOpen(string entityName, string roleName, EntityActionOperation operation, string configuredPolicy);
+
///
/// Get list of roles defined for entity within runtime configuration.. This is applicable for GraphQL when creating authorization
/// directive on Object type.
diff --git a/src/Core/Authorization/AuthorizationResolver.cs b/src/Core/Authorization/AuthorizationResolver.cs
index 1b76401a0e..4c9fa1d3c6 100644
--- a/src/Core/Authorization/AuthorizationResolver.cs
+++ b/src/Core/Authorization/AuthorizationResolver.cs
@@ -4,6 +4,8 @@
using System.Globalization;
using System.Net;
using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Azure.DataApiBuilder.Auth;
@@ -16,6 +18,7 @@
using Azure.DataApiBuilder.Service.Exceptions;
using HotChocolate.Resolvers;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.JsonWebTokens;
@@ -29,6 +32,7 @@ public class AuthorizationResolver : IAuthorizationResolver
{
private readonly RuntimeConfigProvider _runtimeConfigProvider;
private readonly IMetadataProviderFactory _metadataProviderFactory;
+ private readonly ILogger? _logger;
public const string WILDCARD = "*";
public const string CLAIM_PREFIX = "@claims.";
public const string FIELD_PREFIX = "@item.";
@@ -49,10 +53,12 @@ public class AuthorizationResolver : IAuthorizationResolver
public AuthorizationResolver(
RuntimeConfigProvider runtimeConfigProvider,
IMetadataProviderFactory metadataProviderFactory,
- HotReloadEventHandler? handler = null)
+ HotReloadEventHandler? handler = null,
+ ILogger? logger = null)
{
_metadataProviderFactory = metadataProviderFactory;
_runtimeConfigProvider = runtimeConfigProvider;
+ _logger = logger;
handler?.Subscribe(DabConfigEvents.AUTHZ_RESOLVER_ON_CONFIG_CHANGED, OnConfigChanged);
if (runtimeConfigProvider.TryGetConfig(out RuntimeConfig? runtimeConfig))
@@ -216,7 +222,17 @@ public ResolvedDatabasePolicy ResolveDBPolicy(string entityName, string roleName
return ResolvedDatabasePolicy.Empty;
}
- return GetPolicyWithClaimValues(dBpolicyWithClaimTypes, GetAllAuthenticatedUserClaims(httpContext));
+ ResolvedDatabasePolicy resolvedPolicy = GetPolicyWithClaimValues(dBpolicyWithClaimTypes, GetAllAuthenticatedUserClaims(httpContext));
+
+ // A configured (non-empty) database policy that compiles to an empty predicate is a
+ // silent fail-open of row/column level authorization: the query would run unfiltered.
+ // Emit an audit signal and, outside development mode, fail the request.
+ if (string.IsNullOrWhiteSpace(resolvedPolicy.Policy))
+ {
+ AuditDatabasePolicyFailOpen(entityName, roleName, operation, dBpolicyWithClaimTypes);
+ }
+
+ return resolvedPolicy;
}
///
@@ -794,6 +810,77 @@ private static ResolvedDatabasePolicy GetPolicyWithClaimValues(string policy, Di
return new ResolvedDatabasePolicy(processedPolicy, claimValues);
}
+ ///
+ /// Emits a structured audit signal when a configured (non-empty) database authorization
+ /// policy resolves to an empty predicate, which silently removes the intended row/column
+ /// level filter (fail-open). In development mode this is logged as a warning so the request
+ /// can proceed for debugging; in all other host modes it is logged as an error and the
+ /// request is failed to prevent returning unfiltered data.
+ ///
+ /// Entity targeted by the request.
+ /// Role resolved from the client role header.
+ /// Operation the policy applies to.
+ /// The non-empty policy text configured in runtime config.
+ /// Thrown outside development mode to fail the request.
+ public void AuditDatabasePolicyFailOpen(
+ string entityName,
+ string roleName,
+ EntityActionOperation operation,
+ string configuredPolicy)
+ {
+ const string reason = "Configured database authorization policy compiled to an empty predicate; " +
+ "the request would run without the intended row/column level filter (fail-open).";
+ string predicateHash = ComputePredicateHash(configuredPolicy);
+ bool isDevelopmentMode = _runtimeConfigProvider.TryGetConfig(out RuntimeConfig? runtimeConfig)
+ && runtimeConfig.IsDevelopmentMode();
+
+ // Structured audit event: entity, role, operation, policy text, reason, predicateHash, dbPolicyApplied.
+ const string auditMessageTemplate = "Database authorization policy fail-open detected. " +
+ "Entity: {Entity}, Role: {Role}, Operation: {Operation}, PolicyText: {PolicyText}, " +
+ "Reason: {Reason}, PredicateHash: {PredicateHash}, DbPolicyApplied: {DbPolicyApplied}";
+
+ if (isDevelopmentMode)
+ {
+ _logger?.LogWarning(
+ auditMessageTemplate,
+ entityName,
+ roleName,
+ operation,
+ configuredPolicy,
+ reason,
+ predicateHash,
+ false);
+ return;
+ }
+
+ _logger?.LogError(
+ auditMessageTemplate,
+ entityName,
+ roleName,
+ operation,
+ configuredPolicy,
+ reason,
+ predicateHash,
+ false);
+
+ throw new DataApiBuilderException(
+ message: DataApiBuilderException.AUTHORIZATION_FAILURE,
+ statusCode: HttpStatusCode.Forbidden,
+ subStatusCode: DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure);
+ }
+
+ ///
+ /// Produces a stable, non-reversible fingerprint of a policy's text so audit signals can be
+ /// correlated across requests without logging claim values or other sensitive data.
+ ///
+ /// Configured policy text to hash.
+ /// Uppercase hexadecimal SHA-256 hash of the policy text.
+ private static string ComputePredicateHash(string policyText)
+ {
+ byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(policyText));
+ return Convert.ToHexString(hash);
+ }
+
///
/// Helper function used to retrieve the claim value for the given claim type from the user's claims.
///
diff --git a/src/Core/Resolvers/AuthorizationPolicyHelpers.cs b/src/Core/Resolvers/AuthorizationPolicyHelpers.cs
index 408b0f30d4..4428beb381 100644
--- a/src/Core/Resolvers/AuthorizationPolicyHelpers.cs
+++ b/src/Core/Resolvers/AuthorizationPolicyHelpers.cs
@@ -189,6 +189,15 @@ private static List ProcessFilter(
resourcePath: (entityDBObject is not null) ? $"{entityName}.{entityDBObject.FullName}" : entityName,
sqlMetadataProvider);
+ if (filterClause is null && !string.IsNullOrWhiteSpace(dbQueryPolicy.Policy))
+ {
+ string configuredPolicy = authorizationResolver.GetDBPolicyForRequest(entityName, clientRoleHeader, elementalOperation);
+ if (!string.IsNullOrWhiteSpace(configuredPolicy))
+ {
+ authorizationResolver.AuditDatabasePolicyFailOpen(entityName, clientRoleHeader, elementalOperation, configuredPolicy);
+ }
+ }
+
postProcessCallback(filterClause, elementalOperation);
}
diff --git a/src/Service.Tests/Authorization/AuthorizationHelpers.cs b/src/Service.Tests/Authorization/AuthorizationHelpers.cs
index bdd5630a50..8170c068bb 100644
--- a/src/Service.Tests/Authorization/AuthorizationHelpers.cs
+++ b/src/Service.Tests/Authorization/AuthorizationHelpers.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO.Abstractions.TestingHelpers;
+using Azure.DataApiBuilder.Auth;
using Azure.DataApiBuilder.Config;
using Azure.DataApiBuilder.Config.DatabasePrimitives;
using Azure.DataApiBuilder.Config.ObjectModel;
@@ -33,7 +34,7 @@ public static class AuthorizationHelpers
///
/// Configuration with Authorization metadata
/// AuthorizationResolver object
- public static AuthorizationResolver InitAuthorizationResolver(RuntimeConfig runtimeConfig)
+ public static AuthorizationResolver InitAuthorizationResolver(RuntimeConfig runtimeConfig, ILogger? logger = null)
{
MockFileSystem fileSystem = new();
fileSystem.AddFile(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME, new(runtimeConfig.ToJson()));
@@ -41,7 +42,6 @@ public static AuthorizationResolver InitAuthorizationResolver(RuntimeConfig runt
RuntimeConfigProvider runtimeConfigProvider = TestHelper.GetRuntimeConfigProvider(loader);
Mock metadataProvider = new();
- Mock> logger = new();
SourceDefinition sampleTable = CreateSampleTable();
metadataProvider.Setup(x => x.GetSourceDefinition(TEST_ENTITY)).Returns(sampleTable);
metadataProvider.Setup(x => x.GetDatabaseType()).Returns(DatabaseType.MSSQL);
@@ -58,7 +58,7 @@ public static AuthorizationResolver InitAuthorizationResolver(RuntimeConfig runt
string dataSourceName = runtimeConfigProvider.GetConfig().DefaultDataSourceName;
metadataProviderFactory.Setup(x => x.GetMetadataProvider(dataSourceName)).Returns(metadataProvider.Object);
- return new AuthorizationResolver(runtimeConfigProvider, metadataProviderFactory.Object);
+ return new AuthorizationResolver(runtimeConfigProvider, metadataProviderFactory.Object, logger: logger);
}
///
diff --git a/src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs b/src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs
index 81be4e2441..b01d095fa1 100644
--- a/src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs
+++ b/src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs
@@ -8,6 +8,7 @@
using System.Net;
using System.Security.Claims;
using System.Security.Cryptography;
+using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.DataApiBuilder.Auth;
@@ -17,6 +18,7 @@
using Azure.DataApiBuilder.Service.Exceptions;
using Azure.DataApiBuilder.Service.Tests.Authentication.Helpers;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
@@ -34,6 +36,91 @@ public class AuthorizationResolverUnitTests
private const string TEST_AUTHENTICATION_TYPE = "TestAuth";
private const string TEST_CLAIMTYPE_NAME = "TestName";
+ /// Verifies empty resolved policies emit an audit log and fail closed only in production.
+ [DataTestMethod]
+ [DataRow(HostMode.Development, EntityActionOperation.Read, "@item.", DisplayName = "Empty Predicate in Development read logs a warning")]
+ [DataRow(HostMode.Development, EntityActionOperation.Update, "@item.", DisplayName = "Empty Predicate in Development update logs a warning")]
+ [DataRow(HostMode.Development, EntityActionOperation.Delete, "@item.", DisplayName = "Empty Predicate in Development delete logs a warning")]
+ [DataRow(HostMode.Production, EntityActionOperation.Read, "@item.", DisplayName = "Empty Predicate in Production read logs an error and fails")]
+ [DataRow(HostMode.Production, EntityActionOperation.Update, "@item.", DisplayName = "Empty Predicate in Production update logs an error and fails")]
+ [DataRow(HostMode.Production, EntityActionOperation.Delete, "@item.", DisplayName = "Empty Predicate in Production delete logs an error and fails")]
+ [DataRow(HostMode.Development, EntityActionOperation.Read, "@item. @item.", DisplayName = "Empty Predicate in Development whitespace predicate logs a warning")]
+ [DataRow(HostMode.Production, EntityActionOperation.Read, "@item. @item.", DisplayName = "Empty Predicate in Production whitespace predicate logs an error and fails")]
+ public void ResolveDBPolicy_EmptyPredicate_AuditsAccordingToHostMode(
+ HostMode hostMode, EntityActionOperation operation, string configuredPolicy)
+ {
+ RuntimeConfig config = AuthorizationHelpers.InitRuntimeConfig(
+ roleName: TEST_ROLE, operation: operation, databasePolicy: configuredPolicy);
+ config = config with
+ {
+ Runtime = config.Runtime! with { Host = config.Runtime.Host! with { Mode = hostMode } }
+ };
+ Mock> logger = new();
+ AuthorizationResolver resolver = AuthorizationHelpers.InitAuthorizationResolver(config, logger.Object);
+ DefaultHttpContext context = new();
+ context.Request.Headers[AuthorizationResolver.CLIENT_ROLE_HEADER] = TEST_ROLE;
+
+ if (hostMode == HostMode.Development)
+ {
+ ResolvedDatabasePolicy resolved = resolver.ResolveDBPolicy(TEST_ENTITY, TEST_ROLE, operation, context);
+ Assert.IsTrue(string.IsNullOrWhiteSpace(resolved.Policy));
+ Assert.AreEqual(0, resolved.ClaimValues.Count);
+ }
+ else
+ {
+ DataApiBuilderException exception = Assert.ThrowsException(() =>
+ resolver.ResolveDBPolicy(TEST_ENTITY, TEST_ROLE, operation, context));
+ Assert.AreEqual(HttpStatusCode.Forbidden, exception.StatusCode);
+ Assert.AreEqual(DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure, exception.SubStatusCode);
+ Assert.AreEqual(DataApiBuilderException.AUTHORIZATION_FAILURE, exception.Message);
+ }
+
+ var log = logger.Invocations.Single(invocation => invocation.Method.Name == nameof(ILogger.Log));
+ Assert.AreEqual(hostMode == HostMode.Development ? LogLevel.Warning : LogLevel.Error, log.Arguments[0]);
+ Dictionary audit = ((IEnumerable>)log.Arguments[2])
+ .ToDictionary(property => property.Key, property => property.Value);
+ Assert.AreEqual(TEST_ENTITY, audit["Entity"]);
+ Assert.AreEqual(TEST_ROLE, audit["Role"]);
+ Assert.AreEqual(operation, audit["Operation"]);
+ Assert.AreEqual(configuredPolicy, audit["PolicyText"]);
+ Assert.AreEqual(false, audit["DbPolicyApplied"]);
+ Assert.AreEqual(
+ Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(configuredPolicy))),
+ audit["PredicateHash"]);
+ StringAssert.Contains((string)audit["Reason"]!, "compiled to an empty predicate");
+ }
+
+ /// Verifies valid or unconfigured policies do not produce false fail-open audit logs.
+ [DataTestMethod]
+ [DataRow(HostMode.Development, null, "", DisplayName = "ResolveDBPolicy Development missing policy does not log")]
+ [DataRow(HostMode.Production, null, "", DisplayName = "ResolveDBPolicy Production missing policy does not log")]
+ [DataRow(HostMode.Development, "", "", DisplayName = "ResolveDBPolicy Development empty policy does not log")]
+ [DataRow(HostMode.Production, "", "", DisplayName = "ResolveDBPolicy Production empty policy does not log")]
+ [DataRow(HostMode.Development, " ", "", DisplayName = "ResolveDBPolicy Development whitespace configuration does not log")]
+ [DataRow(HostMode.Production, " ", "", DisplayName = "ResolveDBPolicy Production whitespace configuration does not log")]
+ [DataRow(HostMode.Development, "@item.col1 eq 1", "col1 eq 1", DisplayName = "ResolveDBPolicy Development valid policy does not log")]
+ [DataRow(HostMode.Production, "@item.col1 eq 1", "col1 eq 1", DisplayName = "ResolveDBPolicy Production valid policy does not log")]
+ public void ResolveDBPolicy_ValidOrUnconfiguredPolicy_DoesNotAudit(
+ HostMode hostMode, string? configuredPolicy, string expectedPolicy)
+ {
+ RuntimeConfig config = AuthorizationHelpers.InitRuntimeConfig(
+ roleName: TEST_ROLE, operation: EntityActionOperation.Read, databasePolicy: configuredPolicy);
+ config = config with
+ {
+ Runtime = config.Runtime! with { Host = config.Runtime.Host! with { Mode = hostMode } }
+ };
+ Mock> logger = new();
+ AuthorizationResolver resolver = AuthorizationHelpers.InitAuthorizationResolver(config, logger.Object);
+ DefaultHttpContext context = new();
+ context.Request.Headers[AuthorizationResolver.CLIENT_ROLE_HEADER] = TEST_ROLE;
+
+ ResolvedDatabasePolicy resolved = resolver.ResolveDBPolicy(TEST_ENTITY, TEST_ROLE, EntityActionOperation.Read, context);
+
+ Assert.AreEqual(expectedPolicy, resolved.Policy);
+ Assert.AreEqual(0, resolved.ClaimValues.Count);
+ logger.VerifyNoOtherCalls();
+ }
+
[TestMethod]
public void GetRolesForOperation_NullEntityNameThrows()
{
diff --git a/src/Service.Tests/UnitTests/DatabasePolicyClaimBindingUnitTests.cs b/src/Service.Tests/UnitTests/DatabasePolicyClaimBindingUnitTests.cs
index fb423de2c7..a6374df986 100644
--- a/src/Service.Tests/UnitTests/DatabasePolicyClaimBindingUnitTests.cs
+++ b/src/Service.Tests/UnitTests/DatabasePolicyClaimBindingUnitTests.cs
@@ -5,7 +5,10 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
+using System.Net;
using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
using Azure.DataApiBuilder.Auth;
using Azure.DataApiBuilder.Config.DatabasePrimitives;
using Azure.DataApiBuilder.Config.ObjectModel;
@@ -16,6 +19,7 @@
using Azure.DataApiBuilder.Service.Exceptions;
using Azure.DataApiBuilder.Service.Tests.Authorization;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.OData.UriParser;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -34,6 +38,105 @@ public class DatabasePolicyClaimBindingUnitTests
private const string ROLE_NAME = AuthorizationHelpers.TEST_ROLE;
private const EntityActionOperation OPERATION = EntityActionOperation.Read;
+ /// Verifies a configured policy with no parsed filter is audited and rejected in production.
+ [DataTestMethod]
+ [DataRow(HostMode.Development, "@item.&$top=1", DisplayName = "Development empty filter with top logs a warning")]
+ [DataRow(HostMode.Production, "@item.&$top=1", DisplayName = "Production empty filter with top logs an error and fails")]
+ [DataRow(HostMode.Development, "@item.&$skip=1", DisplayName = "Development empty filter with skip logs a warning")]
+ [DataRow(HostMode.Production, "@item.&$skip=1", DisplayName = "Production empty filter with skip logs an error and fails")]
+ [DataRow(HostMode.Development, "@item.&$orderby=id", DisplayName = "Development empty filter with orderby logs a warning")]
+ [DataRow(HostMode.Production, "@item.&$orderby=id", DisplayName = "Production empty filter with orderby logs an error and fails")]
+ [DataRow(HostMode.Development, "@item.&$select=id", DisplayName = "Development empty filter with select logs a warning")]
+ [DataRow(HostMode.Production, "@item.&$select=id", DisplayName = "Production empty filter with select logs an error and fails")]
+ [DataRow(HostMode.Development, "@item.&$top=1&$skip=1", DisplayName = "Development empty filter with multiple options logs a warning")]
+ [DataRow(HostMode.Production, "@item.&$top=1&$skip=1", DisplayName = "Production empty filter with multiple options logs an error and fails")]
+ public void ProcessAuthorizationPolicies_NullParsedPolicy_AuditsAccordingToHostMode(HostMode hostMode, string policy)
+ {
+ RuntimeConfig config = AuthorizationHelpers.InitRuntimeConfig(
+ entityName: ENTITY_NAME, roleName: ROLE_NAME, operation: OPERATION, databasePolicy: policy);
+ config = config with
+ {
+ Runtime = config.Runtime! with { Host = config.Runtime.Host! with { Mode = hostMode } }
+ };
+ Mock> logger = new();
+ AuthorizationResolver resolver = AuthorizationHelpers.InitAuthorizationResolver(config, logger.Object);
+ DefaultHttpContext context = new();
+ context.Request.Headers[AuthorizationResolver.CLIENT_ROLE_HEADER] = ROLE_NAME;
+ Mock metadataProvider = CreateMetadataProvider();
+ TestSqlQueryStructure structure = new(metadataProvider.Object, resolver);
+
+ ResolvedDatabasePolicy resolvedPolicy = resolver.ResolveDBPolicy(ENTITY_NAME, ROLE_NAME, OPERATION, context);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(resolvedPolicy.Policy));
+ Assert.IsNull(AuthorizationPolicyHelpers.GetDBPolicyClauseForQueryStructure(
+ resolvedPolicy,
+ ENTITY_NAME,
+ $"{ENTITY_NAME}.{metadataProvider.Object.EntityToDatabaseObject[ENTITY_NAME].FullName}",
+ metadataProvider.Object));
+
+ if (hostMode == HostMode.Production)
+ {
+ DataApiBuilderException exception = Assert.ThrowsException(() =>
+ AuthorizationPolicyHelpers.ProcessAuthorizationPolicies(OPERATION, structure, context, resolver, metadataProvider.Object));
+ Assert.AreEqual(HttpStatusCode.Forbidden, exception.StatusCode);
+ Assert.AreEqual(DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure, exception.SubStatusCode);
+ }
+ else
+ {
+ AuthorizationPolicyHelpers.ProcessAuthorizationPolicies(OPERATION, structure, context, resolver, metadataProvider.Object);
+ }
+
+ var log = logger.Invocations.Single(invocation => invocation.Method.Name == nameof(ILogger.Log));
+ Assert.AreEqual(hostMode == HostMode.Development ? LogLevel.Warning : LogLevel.Error, log.Arguments[0]);
+ Dictionary audit = ((IEnumerable>)log.Arguments[2])
+ .ToDictionary(property => property.Key, property => property.Value);
+ Assert.AreEqual(ENTITY_NAME, audit["Entity"]);
+ Assert.AreEqual(ROLE_NAME, audit["Role"]);
+ Assert.AreEqual(OPERATION, audit["Operation"]);
+ Assert.AreEqual(policy, audit["PolicyText"]);
+ Assert.AreEqual(false, audit["DbPolicyApplied"]);
+ Assert.AreEqual(Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(policy))), audit["PredicateHash"]);
+ Assert.IsFalse(string.IsNullOrWhiteSpace((string)audit["Reason"]!));
+ AssertParameterValues(structure);
+ }
+
+ /// Verifies unconfigured and valid policies remain unaffected by the parsed-filter guard.
+ [DataTestMethod]
+ [DataRow(HostMode.Development, null, DisplayName = "Development missing policy is allowed")]
+ [DataRow(HostMode.Production, null, DisplayName = "Production missing policy is allowed")]
+ [DataRow(HostMode.Development, "", DisplayName = "Development empty policy is allowed")]
+ [DataRow(HostMode.Production, "", DisplayName = "Production empty policy is allowed")]
+ [DataRow(HostMode.Development, "@item.intCol eq 1", DisplayName = "Development valid policy is applied")]
+ [DataRow(HostMode.Production, "@item.intCol eq 1", DisplayName = "Production valid policy is applied")]
+ public void ProcessAuthorizationPolicies_UnconfiguredOrValidPolicy_DoesNotAudit(HostMode hostMode, string policy)
+ {
+ RuntimeConfig config = AuthorizationHelpers.InitRuntimeConfig(
+ entityName: ENTITY_NAME, roleName: ROLE_NAME, operation: OPERATION, databasePolicy: policy);
+ config = config with
+ {
+ Runtime = config.Runtime! with { Host = config.Runtime.Host! with { Mode = hostMode } }
+ };
+ Mock> logger = new();
+ AuthorizationResolver resolver = AuthorizationHelpers.InitAuthorizationResolver(config, logger.Object);
+ DefaultHttpContext context = new();
+ context.Request.Headers[AuthorizationResolver.CLIENT_ROLE_HEADER] = ROLE_NAME;
+ Mock metadataProvider = CreateMetadataProvider();
+ TestSqlQueryStructure structure = new(metadataProvider.Object, resolver);
+
+ AuthorizationPolicyHelpers.ProcessAuthorizationPolicies(OPERATION, structure, context, resolver, metadataProvider.Object);
+
+ logger.VerifyNoOtherCalls();
+ if (!string.IsNullOrEmpty(policy))
+ {
+ Assert.AreEqual("([intCol] = @param0)", structure.GetDbPolicyForOperation(OPERATION));
+ AssertParameterValues(structure, 1);
+ }
+ else
+ {
+ Assert.IsTrue(string.IsNullOrEmpty(structure.GetDbPolicyForOperation(OPERATION)));
+ AssertParameterValues(structure);
+ }
+ }
+
///
/// Verifies that encoded and literal syntax remains claim data throughout the complete
/// SQL and Cosmos policy pipelines.