Skip to content

Commit b21a924

Browse files
authored
Hot Patch for March Release (#1448)
## Why make this change? - Closes this issue referenced here #1423. Additional issue related to this change #597 - Cosmos DB currently doesn't support field level authorization, it's expected that we are not honoring ```operationToColumnMap.Excluded``` and ```operationToColumnMap.Included``` and by introducing field level auth check into the ```GQLFilterParser``` [recent update](#1407) results the issue that we are seeing. - Cosmos have tests to exercise the filter parser, the reason the tests didn't catch this issue is because the changes here [added lines](https://github.com/Azure/data-api-builder/blob/1431ffc8c11bcd80f62d51c0c0fd6f15383b147a/src/Service.Tests/CosmosTests/TestBase.cs#L77-L82) that's added in this PR [recent update](#1407), this mocks up the field auth check to always return true for all Cosmos filter tests. - ```CosmosMetadataProvider``` does not translate a field wild card (*) or list of hardcoded include columns to the list of AllowedFields in the engine's ```AuthorizationResolver``` currently, but we can add a pass through to the ```TryGetExposedColumnName``` as suggested so it doesn't throw a ```NotImplementedException``` when user accidently passed in the fields settings in the Cosmos configuration file. ## What is this change? - Address this by skipping the field auth check inside of the GQLFilterParser when the database type is Cosmos ## How was this tested? - [ x ] Integration Tests - [ x ] Unit Tests ## Sample Request(s) ```json query planets { planets (filter: {id: {eq: 1000}}) { items { id } } } ```
1 parent 32fc03f commit b21a924

File tree

4 files changed

+60
-4
lines changed

4 files changed

+60
-4
lines changed

src/Service.Tests/Configuration/ConfigurationTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,23 @@ public async Task TestInvalidConfigurationAtRuntime()
300300
Assert.AreEqual(HttpStatusCode.BadRequest, postResult.StatusCode);
301301
}
302302

303+
[TestMethod("Validates containing field permission in configuration returns a bad request."), TestCategory(TestCategory.COSMOSDBNOSQL)]
304+
public async Task TestInvalidConfigurationWithFieldPermission()
305+
{
306+
TestServer server = new(Program.CreateWebHostFromInMemoryUpdateableConfBuilder(Array.Empty<string>()));
307+
HttpClient httpClient = server.CreateClient();
308+
309+
ConfigurationPostParameters config = GetCosmosConfigurationParameters();
310+
config = config with
311+
{
312+
Configuration = "{\"$schema\":\"dab.draft.schema.json\",\"data-source\":{\"database-type\":\"cosmosdb_nosql\",\"options\":{\"database\":\"graphqldb\",\"schema\":\"schema.gql\"}},\"entities\":{\"Planet\":{\"source\":\"graphqldb.planet\",\"graphql\":{\"type\":{\"singular\":\"Planet\",\"plural\":\"Planets\"}},\"permissions\":[{\"role\":\"anonymous\",\"actions\":[{\"action\":\"read\",\"fields\":{\"include\":[\"*\"],\"exclude\":[]}}]}]}}}"
313+
};
314+
315+
HttpResponseMessage postResult =
316+
await httpClient.PostAsync("/configuration", JsonContent.Create(config));
317+
Assert.AreEqual(HttpStatusCode.BadRequest, postResult.StatusCode);
318+
}
319+
303320
[TestMethod("Validates a failure in one of the config updated handlers returns a bad request."), TestCategory(TestCategory.COSMOSDBNOSQL)]
304321
public async Task TestSettingFailureConfigurations()
305322
{

src/Service.Tests/CosmosTests/TestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static void Init(TestContext context)
7979
It.IsAny<string>(),
8080
It.IsAny<Config.Operation>(),
8181
It.IsAny<IEnumerable<string>>()
82-
)).Returns(true);
82+
)).Returns(false);
8383

8484
_application = new WebApplicationFactory<Startup>()
8585
.WithWebHostBuilder(builder =>

src/Service/Models/GraphQLFilterParsers.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,11 @@ public Predicate Parse(
141141
relationshipField = false;
142142
}
143143

144-
// Only perform field (column) authorization when the field is not a relationship field.
144+
// Only perform field (column) authorization when the field is not a relationship field and when the database type is not Cosmos DB.
145+
// Currently Cosmos DB doesn't support field level authorization.
145146
// Due to the recursive behavior of SqlExistsQueryStructure compilation, the column authorization
146147
// check only occurs when access to the column's owner entity is confirmed.
147-
if (!relationshipField)
148+
if (!relationshipField && _metadataProvider.GetDatabaseType() is not DatabaseType.cosmosdb_nosql)
148149
{
149150
string targetEntity = queryStructure.EntityName;
150151

src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using System.Collections.Generic;
66
using System.Diagnostics.CodeAnalysis;
77
using System.IO.Abstractions;
8+
using System.Text.Json;
89
using System.Threading.Tasks;
10+
using Azure.DataApiBuilder.Auth;
911
using Azure.DataApiBuilder.Config;
1012
using Azure.DataApiBuilder.Service.Configurations;
1113
using Azure.DataApiBuilder.Service.Exceptions;
@@ -53,9 +55,44 @@ public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IF
5355
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
5456
}
5557

58+
foreach (Entity entity in _runtimeConfig.Entities.Values)
59+
{
60+
CheckFieldPermissionsForEntity(entity);
61+
}
62+
5663
_cosmosDb = cosmosDb;
5764
}
5865

66+
public void CheckFieldPermissionsForEntity(Entity entity)
67+
{
68+
foreach (PermissionSetting permission in entity.Permissions)
69+
{
70+
string role = permission.Role;
71+
RoleMetadata roleToOperation = new();
72+
object[] Operations = permission.Operations;
73+
foreach (JsonElement operationElement in Operations)
74+
{
75+
if (operationElement.ValueKind is JsonValueKind.String)
76+
{
77+
continue;
78+
}
79+
else
80+
{
81+
// If not a string, the operationObj is expected to be an object that can be deserialized into PermissionOperation
82+
// object.
83+
if (RuntimeConfig.TryGetDeserializedJsonString(operationElement.ToString(), out PermissionOperation? operationObj, null!)
84+
&& operationObj is not null && operationObj.Fields is not null)
85+
{
86+
throw new DataApiBuilderException(
87+
message: "Invalid runtime configuration, CosmosDB_NoSql currently doesn't support field level authorization.",
88+
statusCode: System.Net.HttpStatusCode.BadRequest,
89+
subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
90+
}
91+
}
92+
}
93+
}
94+
}
95+
5996
/// <inheritdoc />
6097
public string GetDatabaseObjectName(string entityName)
6198
{
@@ -170,7 +207,8 @@ public bool VerifyForeignKeyExistsInDB(
170207

171208
public bool TryGetExposedColumnName(string entityName, string field, out string? name)
172209
{
173-
throw new NotImplementedException();
210+
name = field;
211+
return true;
174212
}
175213

176214
/// <summary>

0 commit comments

Comments
 (0)