From e16364bf24f4f58f0cb81aba0215f4d05cb4276f Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 16:34:46 -0700 Subject: [PATCH 1/7] update test' --- .../REST/RestAuthorizationHandlerUnitTests.cs | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs index 501e2855e2..f4694a1703 100644 --- a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; using System.Security.Claims; +using System.Text.Json; using System.Threading.Tasks; using Azure.DataGateway.Auth; using Azure.DataGateway.Config; @@ -350,10 +351,34 @@ IEnumerable columnsRequested /// private static AuthorizationResolver SetupAuthResolverWithWildcardActions() { - RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig( - entityName: AuthorizationHelpers.TEST_ENTITY, - roleName: "admin", - actionName: "*" + string roleName = "admin"; + string entityName = AuthorizationHelpers.TEST_ENTITY; + + PermissionSetting permissionForEntity = new( + role: roleName, + actions: new object[] { JsonSerializer.SerializeToElement("*") }); + + Entity sampleEntity = new( + Source: AuthorizationHelpers.TEST_ENTITY, + Rest: null, + GraphQL: null, + Permissions: new PermissionSetting[] { permissionForEntity }, + Relationships: null, + Mappings: null + ); + + Dictionary entityMap = new(); + entityMap.Add(entityName, sampleEntity); + + RuntimeConfig runtimeConfig = new( + Schema: "UnitTestSchema", + MsSql: null, + CosmosDb: null, + PostgreSql: null, + MySql: null, + DataSource: new DataSource(DatabaseType: DatabaseType.mssql), + RuntimeSettings: new Dictionary(), + Entities: entityMap ); return AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); From 59519d0eb1a0a6f0eba49714886c6eefdd296b1f Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 16:41:39 -0700 Subject: [PATCH 2/7] Update * string to constant variable. --- .../Authorization/AuthorizationResolverUnitTests.cs | 12 ++++++------ .../REST/RestAuthorizationHandlerUnitTests.cs | 13 ++++++------- .../Authorization/AuthorizationResolver.cs | 2 +- .../Configurations/RuntimeConfigValidator.cs | 12 ++++++------ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs index ba5408d039..09d42e2acc 100644 --- a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs @@ -165,7 +165,7 @@ public void WildcardIncludeColDefinedForAction_ColsForActionTest() AuthorizationHelpers.TEST_ENTITY, AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, - includedCols: new HashSet { "*" } + includedCols: new HashSet { AuthorizationResolver.WILDCARD } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); @@ -183,7 +183,7 @@ public void WildcardIncludeColsSomeExcludeDefinedForActionSuccess_ColsForActionT AuthorizationHelpers.TEST_ENTITY, AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, - includedCols: new HashSet { "*" }, + includedCols: new HashSet { AuthorizationResolver.WILDCARD }, excludedCols: new HashSet { "col1", "col2" } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); @@ -278,7 +278,7 @@ public void WildcardExcludeColsDefinedForAction_ColsForActionTest() AuthorizationHelpers.TEST_ENTITY, AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, - excludedCols: new HashSet { "*" } + excludedCols: new HashSet { AuthorizationResolver.WILDCARD } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); @@ -296,7 +296,7 @@ public void WildcardIncludeColsSomeExcludeDefinedForAction_ColsForActionTest() AuthorizationHelpers.TEST_ENTITY, AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, - includedCols: new HashSet { "*" }, + includedCols: new HashSet { AuthorizationResolver.WILDCARD }, excludedCols: new HashSet { "col1", "col2" } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); @@ -316,7 +316,7 @@ public void WildcardExcludeColsSomeIncludeDefinedForAction_ColsForActionTest() AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, includedCols: new HashSet { "col1", "col2" }, - excludedCols: new HashSet { "*" } + excludedCols: new HashSet { AuthorizationResolver.WILDCARD } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); @@ -335,7 +335,7 @@ public void WildcardExcludeColsSomeIncludeDefinedForActionSuccess_ColsForActionT AuthorizationHelpers.TEST_ROLE, ActionType.CREATE, includedCols: new HashSet { "col1", "col2" }, - excludedCols: new HashSet { "*" } + excludedCols: new HashSet { AuthorizationResolver.WILDCARD } ); AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); diff --git a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs index f4694a1703..84cd22d506 100644 --- a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs @@ -347,16 +347,15 @@ IEnumerable columnsRequested } /// - /// Sets up an authorization resolver with a config that specifies the wildcard ("*") as the test entity's actions + /// Sets up an authorization resolver with a config that specifies the wildcard ("*") as the test entity's actions. + /// Explicitly use this instead of AuthorizationHelpers.InitRuntimeConfig() because we want to create actions as + /// array of string instead of array of object. /// private static AuthorizationResolver SetupAuthResolverWithWildcardActions() { - string roleName = "admin"; - string entityName = AuthorizationHelpers.TEST_ENTITY; - PermissionSetting permissionForEntity = new( - role: roleName, - actions: new object[] { JsonSerializer.SerializeToElement("*") }); + role: "admin", + actions: new object[] { JsonSerializer.SerializeToElement(AuthorizationResolver.WILDCARD) }); Entity sampleEntity = new( Source: AuthorizationHelpers.TEST_ENTITY, @@ -368,7 +367,7 @@ private static AuthorizationResolver SetupAuthResolverWithWildcardActions() ); Dictionary entityMap = new(); - entityMap.Add(entityName, sampleEntity); + entityMap.Add(AuthorizationHelpers.TEST_ENTITY, sampleEntity); RuntimeConfig runtimeConfig = new( Schema: "UnitTestSchema", diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs index 67699d383b..763d84051e 100644 --- a/DataGateway.Service/Authorization/AuthorizationResolver.cs +++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs @@ -25,7 +25,7 @@ namespace Azure.DataGateway.Service.Authorization public class AuthorizationResolver : IAuthorizationResolver { private ISqlMetadataProvider _metadataProvider; - private const string WILDCARD = "*"; + public const string WILDCARD = "*"; public const string CLAIM_PREFIX = "@claims."; public const string FIELD_PREFIX = "@item."; public const string CLIENT_ROLE_HEADER = "X-MS-API-ROLE"; diff --git a/DataGateway.Service/Configurations/RuntimeConfigValidator.cs b/DataGateway.Service/Configurations/RuntimeConfigValidator.cs index 97a6c791a9..220dd3c6dd 100644 --- a/DataGateway.Service/Configurations/RuntimeConfigValidator.cs +++ b/DataGateway.Service/Configurations/RuntimeConfigValidator.cs @@ -162,10 +162,10 @@ public void ValidatePermissionsInConfig(RuntimeConfig runtimeConfig) // Check if the IncludeSet/ExcludeSet contain wildcard. If they contain wildcard, we make sure that they // don't contain any other field. If they do, we throw an appropriate exception. - if (configAction.Fields!.Include.Contains("*") && configAction.Fields.Include.Count > 1 || - configAction.Fields.Exclude.Contains("*") && configAction.Fields.Exclude.Count > 1) + if (configAction.Fields!.Include.Contains(AuthorizationResolver.WILDCARD) && configAction.Fields.Include.Count > 1 || + configAction.Fields.Exclude.Contains(AuthorizationResolver.WILDCARD) && configAction.Fields.Exclude.Count > 1) { - string incExc = configAction.Fields.Include.Contains("*") && configAction.Fields.Include.Count > 1 ? "included" : "excluded"; + string incExc = configAction.Fields.Include.Contains(AuthorizationResolver.WILDCARD) && configAction.Fields.Include.Count > 1 ? "included" : "excluded"; throw new DataGatewayException( message: $"No other field can be present with wildcard in the {incExc} set for: entity:{entityName}," + $" role:{permissionSetting.Role}, action:{actionName}", @@ -389,8 +389,8 @@ private static void AreFieldsAccessible(string policy, HashSet includedF private static bool IsFieldAccessible(Match columnNameMatch, HashSet includedFields, HashSet excludedFields) { string columnName = columnNameMatch.Value.Substring(AuthorizationResolver.FIELD_PREFIX.Length); - if (excludedFields.Contains(columnName!) || excludedFields.Contains("*") || - !(includedFields.Contains("*") || includedFields.Contains(columnName))) + if (excludedFields.Contains(columnName!) || excludedFields.Contains(AuthorizationResolver.WILDCARD) || + !(includedFields.Contains(AuthorizationResolver.WILDCARD) || includedFields.Contains(columnName))) { // If column is present in excluded OR excluded='*' // If column is absent from included and included!=* @@ -472,7 +472,7 @@ private static void ValidateActionName(string actionName, string entityName, str /// Boolean value indicating whether the actionName is valid or not. public static bool IsValidActionName(string actionName) { - return actionName.Equals("*") || _validActions.Contains(actionName); + return actionName.Equals(AuthorizationResolver.WILDCARD) || _validActions.Contains(actionName); } } } From a37b8fa6cd2ce8833b627a70ab2eed75e627772f Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 17:47:16 -0700 Subject: [PATCH 3/7] Update code formatting --- DataGateway.Auth/IAuthorizationResolver.cs | 9 +++++++-- .../Authorization/AuthorizationResolver.cs | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/DataGateway.Auth/IAuthorizationResolver.cs b/DataGateway.Auth/IAuthorizationResolver.cs index 934a84de7e..f418a2ea10 100644 --- a/DataGateway.Auth/IAuthorizationResolver.cs +++ b/DataGateway.Auth/IAuthorizationResolver.cs @@ -91,14 +91,19 @@ public interface IAuthorizationResolver /// Entity to lookup permissions /// Action to lookup applicable roles /// Collection of roles. Empty list if entityPermissionsMap is null. - public static IEnumerable GetRolesForAction(string entityName, string actionName, Dictionary? entityPermissionsMap) + public static IEnumerable GetRolesForAction( + string entityName, + string actionName, + Dictionary? entityPermissionsMap) { if (entityName is null) { throw new ArgumentNullException(paramName: "entityName"); } - if (entityPermissionsMap is not null && entityPermissionsMap[entityName].ActionToRolesMap.TryGetValue(actionName, out List? roleList) && roleList is not null) + if (entityPermissionsMap is not null && + entityPermissionsMap[entityName].ActionToRolesMap.TryGetValue(actionName, out List? roleList) && + roleList is not null) { return roleList; } diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs index 763d84051e..31a508103c 100644 --- a/DataGateway.Service/Authorization/AuthorizationResolver.cs +++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs @@ -141,7 +141,7 @@ public bool AreColumnsAllowedForAction(string entityName, string roleName, strin { // backingColumn will not be null when TryGetBackingColumn() is true. if (actionToColumnMap.Excluded.Contains(backingColumn!) || actionToColumnMap.Excluded.Contains(WILDCARD) || - !(actionToColumnMap.Included.Contains(WILDCARD) || actionToColumnMap.Included.Contains(backingColumn!))) + !(actionToColumnMap.Included.Contains(WILDCARD) || actionToColumnMap.Included.Contains(backingColumn!))) { // If column is present in excluded OR excluded='*' // If column is absent from included and included!=* @@ -289,7 +289,8 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) // Try to add the actionName to the map if not present. // Builds up mapping: i.e. ActionType.CREATE permitted in {Role1, Role2, ..., RoleN} - if (!string.IsNullOrWhiteSpace(actionName) && !entityToRoleMap.ActionToRolesMap.TryAdd(actionName, new List(new string[] { role }))) + if (!string.IsNullOrWhiteSpace(actionName) && + !entityToRoleMap.ActionToRolesMap.TryAdd(actionName, new List(new string[] { role }))) { entityToRoleMap.ActionToRolesMap[actionName].Add(role); } From 32982ea95b5956e16984c5f0b40800ddc5294e05 Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 18:14:12 -0700 Subject: [PATCH 4/7] Expand wildcard actions --- .../Authorization/AuthorizationResolver.cs | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs index 31a508103c..c2603eedd0 100644 --- a/DataGateway.Service/Authorization/AuthorizationResolver.cs +++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs @@ -289,18 +289,24 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) // Try to add the actionName to the map if not present. // Builds up mapping: i.e. ActionType.CREATE permitted in {Role1, Role2, ..., RoleN} - if (!string.IsNullOrWhiteSpace(actionName) && - !entityToRoleMap.ActionToRolesMap.TryAdd(actionName, new List(new string[] { role }))) + // Expand wildcard action to explicit action type. + // + if (actionName.Equals(AuthorizationResolver.WILDCARD)) { - entityToRoleMap.ActionToRolesMap[actionName].Add(role); + AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.READ, role); + AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.CREATE, role); + AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.DELETE, role); + AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.UPDATE, role); } - - foreach (string allowedColumn in actionToColumn.Allowed) + // Otherwise, we know explicit action name. Just add that. + // + else if (!string.IsNullOrWhiteSpace(actionName)) { - entityToRoleMap.FieldToRolesMap.TryAdd(key: allowedColumn, CreateActionToRoleMap()); - entityToRoleMap.FieldToRolesMap[allowedColumn][actionName].Add(role); + AddRoleToAction(entityToRoleMap.ActionToRolesMap, actionName, role); } + PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, actionName, actionToColumn); + roleToAction.ActionToColumnMap[actionName] = actionToColumn; } @@ -311,6 +317,40 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) } } + private static void PopuldateFieldToRoleMap( + Dictionary>> fieldToRolesMap, + string role, + string actionName, + ActionMetadata actionToColumn) + { + foreach (string allowedColumn in actionToColumn.Allowed) + { + fieldToRolesMap.TryAdd(key: allowedColumn, CreateActionToRoleMap()); + + // Expand wildcard action to explicit action type. + // + if (actionName.Equals(AuthorizationResolver.WILDCARD)) + { + fieldToRolesMap[allowedColumn][ActionType.READ].Add(role); + fieldToRolesMap[allowedColumn][ActionType.CREATE].Add(role); + fieldToRolesMap[allowedColumn][ActionType.DELETE].Add(role); + fieldToRolesMap[allowedColumn][ActionType.UPDATE].Add(role); + } + else + { + fieldToRolesMap[allowedColumn][actionName].Add(role); + } + } + } + + private static void AddRoleToAction(Dictionary> actionToRolesMap, string actionName, string role) + { + if (!actionToRolesMap.TryAdd(actionName, new List(new string[] { role }))) + { + actionToRolesMap[actionName].Add(role); + } + } + /// public IEnumerable GetAllowedColumns(string entityName, string roleName, string action) { From c024972060c79765a5a37f899b63306ea2db0e03 Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 21:13:57 -0700 Subject: [PATCH 5/7] Remove wildcard special case from authorization checks. --- .../Authorization/AuthorizationResolver.cs | 67 ++++++------------- DataGateway.Service/Services/RestService.cs | 8 +-- 2 files changed, 24 insertions(+), 51 deletions(-) diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs index c2603eedd0..a2c7922f2f 100644 --- a/DataGateway.Service/Authorization/AuthorizationResolver.cs +++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs @@ -102,8 +102,7 @@ public bool AreRoleAndActionDefinedForEntity(string entityName, string roleName, { if (valueOfEntityToRole.RoleToActionMap.TryGetValue(roleName, out RoleMetadata? valueOfRoleToAction)) { - if (valueOfRoleToAction!.ActionToColumnMap.ContainsKey(WILDCARD) || - valueOfRoleToAction!.ActionToColumnMap.ContainsKey(action)) + if (valueOfRoleToAction!.ActionToColumnMap.ContainsKey(action)) { return true; } @@ -122,14 +121,7 @@ public bool AreColumnsAllowedForAction(string entityName, string roleName, strin ActionMetadata actionToColumnMap; RoleMetadata roleInEntity = EntityPermissionsMap[entityName].RoleToActionMap[roleName]; - try - { - actionToColumnMap = roleInEntity.ActionToColumnMap[actionName]; - } - catch (KeyNotFoundException) - { - actionToColumnMap = roleInEntity.ActionToColumnMap[WILDCARD]; - } + actionToColumnMap = roleInEntity.ActionToColumnMap[actionName]; // Each column present in the request is an "exposedColumn". // Authorization permissions reference "backingColumns" @@ -140,8 +132,8 @@ public bool AreColumnsAllowedForAction(string entityName, string roleName, strin if (_metadataProvider.TryGetBackingColumn(entityName, field: exposedColumn, out string? backingColumn)) { // backingColumn will not be null when TryGetBackingColumn() is true. - if (actionToColumnMap.Excluded.Contains(backingColumn!) || actionToColumnMap.Excluded.Contains(WILDCARD) || - !(actionToColumnMap.Included.Contains(WILDCARD) || actionToColumnMap.Included.Contains(backingColumn!))) + if (actionToColumnMap.Excluded.Contains(backingColumn!) || + !actionToColumnMap.Included.Contains(backingColumn!)) { // If column is present in excluded OR excluded='*' // If column is absent from included and included!=* @@ -189,20 +181,7 @@ private string GetDBPolicyForRequest(string entityName, string roleName, string RoleMetadata roleMetadata = EntityPermissionsMap[entityName].RoleToActionMap[roleName]; roleMetadata.ActionToColumnMap.TryGetValue(action, out ActionMetadata? actionMetadata); - // If action exists in map (explicitly specified in config), use its policy - // action should only be absent in roleMetadata if WILDCARD is in the map instead of specific actions, - // as authorization happens before policy parsing (would have already returned forbidden) - string? dbPolicy; - if (actionMetadata is not null) - { - dbPolicy = actionMetadata.DatabasePolicy; - - } // else check if wildcard exists in action map, if so use its policy, else null - else - { - roleMetadata.ActionToColumnMap.TryGetValue(WILDCARD, out ActionMetadata? wildcardMetadata); - dbPolicy = wildcardMetadata is not null ? wildcardMetadata.DatabasePolicy : null; - } + string? dbPolicy = actionMetadata!.DatabasePolicy; return dbPolicy is not null ? dbPolicy : string.Empty; } @@ -291,23 +270,29 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) // Builds up mapping: i.e. ActionType.CREATE permitted in {Role1, Role2, ..., RoleN} // Expand wildcard action to explicit action type. // - if (actionName.Equals(AuthorizationResolver.WILDCARD)) + if (actionName.Equals(WILDCARD)) { - AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.READ, role); - AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.CREATE, role); - AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.DELETE, role); - AddRoleToAction(entityToRoleMap.ActionToRolesMap, ActionType.UPDATE, role); + string[] allAvailableActions = { ActionType.READ, ActionType.CREATE, ActionType.DELETE, ActionType.UPDATE }; + + foreach (string action in allAvailableActions) + { + AddRoleToAction(entityToRoleMap.ActionToRolesMap, action, role); + + PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, action, actionToColumn); + + roleToAction.ActionToColumnMap[action] = actionToColumn; + } } // Otherwise, we know explicit action name. Just add that. // else if (!string.IsNullOrWhiteSpace(actionName)) { AddRoleToAction(entityToRoleMap.ActionToRolesMap, actionName, role); - } - PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, actionName, actionToColumn); + PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, actionName, actionToColumn); - roleToAction.ActionToColumnMap[actionName] = actionToColumn; + roleToAction.ActionToColumnMap[actionName] = actionToColumn; + } } entityToRoleMap.RoleToActionMap[role] = roleToAction; @@ -327,19 +312,7 @@ private static void PopuldateFieldToRoleMap( { fieldToRolesMap.TryAdd(key: allowedColumn, CreateActionToRoleMap()); - // Expand wildcard action to explicit action type. - // - if (actionName.Equals(AuthorizationResolver.WILDCARD)) - { - fieldToRolesMap[allowedColumn][ActionType.READ].Add(role); - fieldToRolesMap[allowedColumn][ActionType.CREATE].Add(role); - fieldToRolesMap[allowedColumn][ActionType.DELETE].Add(role); - fieldToRolesMap[allowedColumn][ActionType.UPDATE].Add(role); - } - else - { - fieldToRolesMap[allowedColumn][actionName].Add(role); - } + fieldToRolesMap[allowedColumn][actionName].Add(role); } } diff --git a/DataGateway.Service/Services/RestService.cs b/DataGateway.Service/Services/RestService.cs index d9490039e8..3757356843 100644 --- a/DataGateway.Service/Services/RestService.cs +++ b/DataGateway.Service/Services/RestService.cs @@ -263,16 +263,16 @@ public static string HttpVerbToActions(string httpVerbName) switch (httpVerbName) { case "POST": - return "create"; + return ActionType.CREATE; case "PUT": case "PATCH": // Please refer to the use of this method, which is to look out for policy based on crud operation type. // Since create doesn't have filter predicates, PUT/PATCH would resolve to update operation. - return "update"; + return ActionType.UPDATE;; case "DELETE": - return "delete"; + return ActionType.DELETE; case "GET": - return "read"; + return ActionType.READ; default: throw new DataGatewayException( message: "Unsupported operation type.", From 882fbf5af9ceb6c6114a5c7136fbe6631969aae9 Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Tue, 19 Jul 2022 23:54:48 -0700 Subject: [PATCH 6/7] Add unittests for wildcard handling --- .../AuthorizationResolverUnitTests.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs index 09d42e2acc..d40c782101 100644 --- a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs @@ -110,7 +110,31 @@ public void AreRoleAndActionDefinedForEntityTest( AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); // Mock Request Values - Assert.AreEqual(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, actionName), expected); + Assert.AreEqual(expected, authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, actionName)); + } + + /// + /// Test that wildcard actions are expanded to explicit actions. + /// + [TestMethod] + public void TestWildcardAction() + { + string roleName = "myRole"; + RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig(AuthorizationHelpers.TEST_ENTITY, roleName, "*"); + runtimeConfig.Entities[AuthorizationHelpers.TEST_ENTITY].Permissions[0].Actions = new object[] { JsonSerializer.SerializeToElement(AuthorizationResolver.WILDCARD) }; + AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); + + // There should not be a wildcard action in AuthorizationResolver.EntityPermissionsMap + // + Assert.IsFalse(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, "*")); + + // All the wildcard action should be expand to explicit actions. + // + string[] allAvailableActions = { ActionType.READ, ActionType.CREATE, ActionType.DELETE, ActionType.UPDATE }; + foreach (string action in allAvailableActions) + { + Assert.IsTrue(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, action)); + } } #endregion From cfdcf040e6fc7962836d72b1ac00d05a54d75efb Mon Sep 17 00:00:00 2001 From: Jarupat Jisarojito Date: Wed, 20 Jul 2022 00:33:44 -0700 Subject: [PATCH 7/7] Add tests that verify internal data structure when wildcard is used. --- .../AuthorizationMetadataHelpers.cs | 11 ++++--- .../AuthorizationResolverUnitTests.cs | 21 ++++++++++-- .../REST/RestAuthorizationHandlerUnitTests.cs | 32 ++++--------------- .../Authorization/AuthorizationResolver.cs | 12 +++---- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/DataGateway.Auth/AuthorizationMetadataHelpers.cs b/DataGateway.Auth/AuthorizationMetadataHelpers.cs index b7873bd70d..ef44cd77d0 100644 --- a/DataGateway.Auth/AuthorizationMetadataHelpers.cs +++ b/DataGateway.Auth/AuthorizationMetadataHelpers.cs @@ -14,10 +14,13 @@ public class EntityMetadata public Dictionary RoleToActionMap { get; set; } = new(); /// - /// Given the key (actionName) returns a key/value collection of fieldName to Roles - /// i.e. READ action - /// Key(field): id -> Value(collection): permitted in {Role1, Role2, ..., RoleN} - /// Key(field): title -> Value(collection): permitted in {Role1} + /// Field to action to role mapping. + /// Given the key (Field aka. column name) returns a key/value collection of action to Roles + /// i.e. ID column + /// Key(field): id -> Dictionary(actions) + /// each entry in the dictionary contains action to role map. + /// create: permitted in {Role1, Role2, ..., RoleN} + /// delete: permitted in {Role1, RoleN} /// public Dictionary>> FieldToRolesMap { get; set; } = new(); diff --git a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs index d40c782101..933e2eab2e 100644 --- a/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/AuthorizationResolverUnitTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using System.Linq; using System.Net; using System.Security.Claims; using System.Text.Json; @@ -87,7 +88,9 @@ public void NoRoleHeader_RoleContextTest() Assert.AreEqual(authZResolver.IsValidRoleContext(context.Object), expected); } #endregion + #region Role and Action on Entity Tests + /// /// Tests the AreRoleAndActionDefinedForEntity stage of authorization. /// Request Action is defined for role -> VALID @@ -115,18 +118,24 @@ public void AreRoleAndActionDefinedForEntityTest( /// /// Test that wildcard actions are expanded to explicit actions. + /// Verifies that internal data structure are created correctly. /// [TestMethod] public void TestWildcardAction() { string roleName = "myRole"; - RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig(AuthorizationHelpers.TEST_ENTITY, roleName, "*"); + List expectedRoles = new() { roleName }; + + RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig(AuthorizationHelpers.TEST_ENTITY, roleName, AuthorizationResolver.WILDCARD); + + // Override the action to be a list of string for wildcard instead of a list of object created by InitRuntimeConfig() + // runtimeConfig.Entities[AuthorizationHelpers.TEST_ENTITY].Permissions[0].Actions = new object[] { JsonSerializer.SerializeToElement(AuthorizationResolver.WILDCARD) }; AuthorizationResolver authZResolver = AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); // There should not be a wildcard action in AuthorizationResolver.EntityPermissionsMap // - Assert.IsFalse(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, "*")); + Assert.IsFalse(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, AuthorizationResolver.WILDCARD)); // All the wildcard action should be expand to explicit actions. // @@ -134,8 +143,16 @@ public void TestWildcardAction() foreach (string action in allAvailableActions) { Assert.IsTrue(authZResolver.AreRoleAndActionDefinedForEntity(AuthorizationHelpers.TEST_ENTITY, roleName, action)); + + IEnumerable actualRolesForCol1 = authZResolver.GetRolesForField(AuthorizationHelpers.TEST_ENTITY, "col1", action); + + CollectionAssert.AreEquivalent(expectedRoles, actualRolesForCol1.ToList()); } + + IEnumerable actualRolesForAction = authZResolver.GetRolesForAction(AuthorizationHelpers.TEST_ENTITY, ActionType.CREATE); + CollectionAssert.AreEquivalent(expectedRoles, actualRolesForAction.ToList()); } + #endregion /// diff --git a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs index 84cd22d506..d4943d9da1 100644 --- a/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs +++ b/DataGateway.Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs @@ -353,32 +353,14 @@ IEnumerable columnsRequested /// private static AuthorizationResolver SetupAuthResolverWithWildcardActions() { - PermissionSetting permissionForEntity = new( - role: "admin", - actions: new object[] { JsonSerializer.SerializeToElement(AuthorizationResolver.WILDCARD) }); - - Entity sampleEntity = new( - Source: AuthorizationHelpers.TEST_ENTITY, - Rest: null, - GraphQL: null, - Permissions: new PermissionSetting[] { permissionForEntity }, - Relationships: null, - Mappings: null - ); + RuntimeConfig runtimeConfig = AuthorizationHelpers.InitRuntimeConfig( + entityName: AuthorizationHelpers.TEST_ENTITY, + roleName: "admin", + actionName: "*"); - Dictionary entityMap = new(); - entityMap.Add(AuthorizationHelpers.TEST_ENTITY, sampleEntity); - - RuntimeConfig runtimeConfig = new( - Schema: "UnitTestSchema", - MsSql: null, - CosmosDb: null, - PostgreSql: null, - MySql: null, - DataSource: new DataSource(DatabaseType: DatabaseType.mssql), - RuntimeSettings: new Dictionary(), - Entities: entityMap - ); + // Override the action to be a list of string for wildcard instead of a list of object created by InitRuntimeConfig() + // + runtimeConfig.Entities[AuthorizationHelpers.TEST_ENTITY].Permissions[0].Actions = new object[] { JsonSerializer.SerializeToElement(AuthorizationResolver.WILDCARD) }; return AuthorizationHelpers.InitAuthorizationResolver(runtimeConfig); } diff --git a/DataGateway.Service/Authorization/AuthorizationResolver.cs b/DataGateway.Service/Authorization/AuthorizationResolver.cs index a2c7922f2f..29e17f576f 100644 --- a/DataGateway.Service/Authorization/AuthorizationResolver.cs +++ b/DataGateway.Service/Authorization/AuthorizationResolver.cs @@ -276,9 +276,9 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) foreach (string action in allAvailableActions) { - AddRoleToAction(entityToRoleMap.ActionToRolesMap, action, role); + PopulateActionToRoleMap(entityToRoleMap.ActionToRolesMap, action, role); - PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, action, actionToColumn); + PopulateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, action, actionToColumn); roleToAction.ActionToColumnMap[action] = actionToColumn; } @@ -287,9 +287,9 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) // else if (!string.IsNullOrWhiteSpace(actionName)) { - AddRoleToAction(entityToRoleMap.ActionToRolesMap, actionName, role); + PopulateActionToRoleMap(entityToRoleMap.ActionToRolesMap, actionName, role); - PopuldateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, actionName, actionToColumn); + PopulateFieldToRoleMap(entityToRoleMap.FieldToRolesMap, role, actionName, actionToColumn); roleToAction.ActionToColumnMap[actionName] = actionToColumn; } @@ -302,7 +302,7 @@ public void SetEntityPermissionMap(RuntimeConfig? runtimeConfig) } } - private static void PopuldateFieldToRoleMap( + private static void PopulateFieldToRoleMap( Dictionary>> fieldToRolesMap, string role, string actionName, @@ -316,7 +316,7 @@ private static void PopuldateFieldToRoleMap( } } - private static void AddRoleToAction(Dictionary> actionToRolesMap, string actionName, string role) + private static void PopulateActionToRoleMap(Dictionary> actionToRolesMap, string actionName, string role) { if (!actionToRolesMap.TryAdd(actionName, new List(new string[] { role }))) {