Skip to content

Commit c84ec2c

Browse files
committed
TEMP DIAGNOSTIC: log resolver Included/Excluded sets and backing column resolution (to be reverted)
1 parent d7cc7d9 commit c84ec2c

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

src/Core/Authorization/AuthorizationResolver.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,15 @@ public bool AreColumnsAllowedForOperation(string entityName, string roleName, En
167167
if (roleMetadata.OperationToColumnMap.TryGetValue(operation, out OperationMetadata? operationToColumnMap) && operationToColumnMap is not null)
168168
{
169169
_runtimeConfigProvider.TryGetConfig(out RuntimeConfig? runtimeConfig);
170+
Console.WriteLine($"[DIAG resolver] entity={entityName} role={roleName} op={operation} Included=[{string.Join(",", operationToColumnMap.Included)}] Excluded=[{string.Join(",", operationToColumnMap.Excluded)}]");
170171
// Each column present in the request is an "exposedColumn".
171172
// Authorization permissions reference "backingColumns"
172173
// Resolve backingColumn name to check authorization.
173174
// Failure indicates that request contain invalid exposedColumn for entity.
174175
foreach (string exposedColumn in columns)
175176
{
177+
bool resolvedBacking = metadataProvider.TryGetBackingColumn(entityName, field: exposedColumn, out string? backingColumnDiag);
178+
Console.WriteLine($"[DIAG resolver] exposed={exposedColumn} resolvedBacking={resolvedBacking} backing={backingColumnDiag} inExcluded={(backingColumnDiag != null && operationToColumnMap.Excluded.Contains(backingColumnDiag))} inIncluded={(backingColumnDiag != null && operationToColumnMap.Included.Contains(backingColumnDiag))}");
176179
if (metadataProvider.TryGetBackingColumn(entityName, field: exposedColumn, out string? backingColumn))
177180
{
178181
// backingColumn will not be null when TryGetBackingColumn() is true.

src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,98 @@ public void WildcardColumnInclusionWithExplicitExclusion()
905905
excludedColumns));
906906
}
907907

908+
/// <summary>
909+
/// Reproduces the real-world Book entity scenario: a single role with MULTIPLE actions
910+
/// (read with no field restriction, create/update excluding a column, delete with no
911+
/// field restriction) all defined in one EntityPermission.Actions array - unlike
912+
/// AuthorizationHelpers.InitRuntimeConfig which only ever builds a single action per role.
913+
/// </summary>
914+
[TestMethod("Multiple actions per role - column exclusion on create/update only")]
915+
public void MultipleActionsPerRole_ColumnExclusionOnCreateAndUpdate()
916+
{
917+
EntityActionFields readFields = new(Exclude: new());
918+
EntityActionFields createUpdateFields = new(Exclude: new() { "col3" });
919+
EntityActionFields deleteFields = new(Exclude: new());
920+
921+
EntityAction readAction = new(Action: EntityActionOperation.Read, Fields: null, Policy: new(null, null));
922+
EntityAction createAction = new(Action: EntityActionOperation.Create, Fields: createUpdateFields, Policy: new(null, null));
923+
EntityAction updateAction = new(Action: EntityActionOperation.Update, Fields: createUpdateFields, Policy: new(null, null));
924+
EntityAction deleteAction = new(Action: EntityActionOperation.Delete, Fields: null, Policy: new(null, null));
925+
926+
Azure.DataApiBuilder.Config.ObjectModel.EntityPermission permissionForEntity = new(
927+
Role: AuthorizationHelpers.TEST_ROLE,
928+
Actions: new EntityAction[] { readAction, createAction, updateAction, deleteAction });
929+
930+
Azure.DataApiBuilder.Config.ObjectModel.Entity sampleEntity = new(
931+
Source: new Azure.DataApiBuilder.Config.ObjectModel.EntitySource(AuthorizationHelpers.TEST_ENTITY, Azure.DataApiBuilder.Config.ObjectModel.EntitySourceType.Table, null, null),
932+
Fields: null,
933+
Rest: new(Array.Empty<SupportedHttpVerb>()),
934+
GraphQL: new(AuthorizationHelpers.TEST_ENTITY, AuthorizationHelpers.TEST_ENTITY),
935+
Permissions: new Azure.DataApiBuilder.Config.ObjectModel.EntityPermission[] { permissionForEntity },
936+
Relationships: null,
937+
Mappings: null);
938+
939+
Azure.DataApiBuilder.Config.ObjectModel.RuntimeConfig runtimeConfig = new(
940+
Schema: "UnitTestSchema",
941+
DataSource: new Azure.DataApiBuilder.Config.ObjectModel.DataSource(Azure.DataApiBuilder.Config.ObjectModel.DatabaseType.MSSQL, "", new()),
942+
Runtime: new(
943+
Rest: new(),
944+
GraphQL: new(),
945+
Mcp: new(),
946+
Host: new(
947+
Cors: null,
948+
Authentication: new("AppService", null))),
949+
Entities: new(new Dictionary<string, Azure.DataApiBuilder.Config.ObjectModel.Entity> { { AuthorizationHelpers.TEST_ENTITY, sampleEntity } }));
950+
951+
AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig);
952+
953+
// Read should allow all columns (no exclusion for read).
954+
Assert.IsTrue(authZResolver.AreColumnsAllowedForOperation(
955+
AuthorizationHelpers.TEST_ENTITY,
956+
AuthorizationHelpers.TEST_ROLE,
957+
operation: EntityActionOperation.Read,
958+
new List<string> { "col1", "col3" }));
959+
960+
// Create should DENY col3 since it is excluded for create.
961+
Assert.IsFalse(authZResolver.AreColumnsAllowedForOperation(
962+
AuthorizationHelpers.TEST_ENTITY,
963+
AuthorizationHelpers.TEST_ROLE,
964+
operation: EntityActionOperation.Create,
965+
new List<string> { "col1", "col3" }));
966+
967+
// Update should DENY col3 since it is excluded for update.
968+
Assert.IsFalse(authZResolver.AreColumnsAllowedForOperation(
969+
AuthorizationHelpers.TEST_ENTITY,
970+
AuthorizationHelpers.TEST_ROLE,
971+
operation: EntityActionOperation.Update,
972+
new List<string> { "col1", "col3" }));
973+
974+
// Round-trip the config through JSON serialization/deserialization (as happens when DAB
975+
// loads a real config file from disk) to rule out any bug specific to the JSON converters
976+
// (e.g. shared/aliased Fields.Exclude HashSet instances across sibling actions).
977+
string json = runtimeConfig.ToJson();
978+
Assert.IsTrue(Azure.DataApiBuilder.Config.RuntimeConfigLoader.TryParseConfig(json, out Azure.DataApiBuilder.Config.ObjectModel.RuntimeConfig? roundTrippedConfig));
979+
AuthorizationResolver roundTrippedResolver = AuthorizationHelpers.InitAuthorizationResolver(roundTrippedConfig!);
980+
981+
Assert.IsTrue(roundTrippedResolver.AreColumnsAllowedForOperation(
982+
AuthorizationHelpers.TEST_ENTITY,
983+
AuthorizationHelpers.TEST_ROLE,
984+
operation: EntityActionOperation.Read,
985+
new List<string> { "col1", "col3" }));
986+
987+
Assert.IsFalse(roundTrippedResolver.AreColumnsAllowedForOperation(
988+
AuthorizationHelpers.TEST_ENTITY,
989+
AuthorizationHelpers.TEST_ROLE,
990+
operation: EntityActionOperation.Create,
991+
new List<string> { "col1", "col3" }));
992+
993+
Assert.IsFalse(roundTrippedResolver.AreColumnsAllowedForOperation(
994+
AuthorizationHelpers.TEST_ENTITY,
995+
AuthorizationHelpers.TEST_ROLE,
996+
operation: EntityActionOperation.Update,
997+
new List<string> { "col1", "col3" }));
998+
}
999+
9081000
/// <summary>
9091001
/// Test that all columns should be excluded if the exclusion contains wildcard character.
9101002
/// </summary>

0 commit comments

Comments
 (0)