private void SetEntityPermissionMap()

in src/Core/Authorization/AuthorizationResolver.cs [260:389]


    private void SetEntityPermissionMap(RuntimeConfig runtimeConfig)
    {
        foreach ((string entityName, Entity entity) in runtimeConfig.Entities)
        {
            EntityMetadata entityToRoleMap = new();

            bool isStoredProcedureEntity = entity.Source.Type is EntitySourceType.StoredProcedure;
            if (isStoredProcedureEntity)
            {
                SupportedHttpVerb[] methods;
                if (entity.Rest.Methods is not null)
                {
                    methods = entity.Rest.Methods;
                }
                else
                {
                    methods = (entity.Rest.Enabled) ? new SupportedHttpVerb[] { SupportedHttpVerb.Post } : Array.Empty<SupportedHttpVerb>();
                }

                entityToRoleMap.StoredProcedureHttpVerbs = new(methods);
            }

            // Store the allowedColumns for anonymous role.
            // In case the authenticated role is not defined on the entity,
            // this will help in copying over permissions from anonymous role to authenticated role.
            HashSet<string> allowedColumnsForAnonymousRole = new();
            string dataSourceName = runtimeConfig.GetDataSourceNameFromEntityName(entityName);
            ISqlMetadataProvider metadataProvider = _metadataProviderFactory.GetMetadataProvider(dataSourceName);
            foreach (EntityPermission permission in entity.Permissions)
            {
                string role = permission.Role;
                RoleMetadata roleToOperation = new();
                EntityAction[] entityActions = permission.Actions;
                foreach (EntityAction entityAction in entityActions)
                {
                    EntityActionOperation operation = entityAction.Action;
                    OperationMetadata operationToColumn = new();

                    // Use a HashSet to store all the backing field names
                    // that are accessible to the user.
                    HashSet<string> allowedColumns = new();
                    IEnumerable<string> allTableColumns = ResolveEntityDefinitionColumns(entityName, metadataProvider);

                    if (entityAction.Fields is null)
                    {
                        operationToColumn.Included.UnionWith(ResolveEntityDefinitionColumns(entityName, metadataProvider));
                    }
                    else
                    {
                        // When a wildcard (*) is defined for Included columns, all of the table's
                        // columns must be resolved and placed in the operationToColumn Key/Value store.
                        // This is especially relevant for find requests, where actual column names must be
                        // resolved when no columns were included in a request.
                        if (entityAction.Fields.Include is null ||
                            (entityAction.Fields.Include.Count == 1 && entityAction.Fields.Include.Contains(WILDCARD)))
                        {
                            operationToColumn.Included.UnionWith(ResolveEntityDefinitionColumns(entityName, metadataProvider));
                        }
                        else
                        {
                            operationToColumn.Included = entityAction.Fields.Include;
                        }

                        // When a wildcard (*) is defined for Excluded columns, all of the table's
                        // columns must be resolved and placed in the operationToColumn Key/Value store.
                        if (entityAction.Fields.Exclude is null ||
                            (entityAction.Fields.Exclude.Count == 1 && entityAction.Fields.Exclude.Contains(WILDCARD)))
                        {
                            operationToColumn.Excluded.UnionWith(ResolveEntityDefinitionColumns(entityName, metadataProvider));
                        }
                        else
                        {
                            operationToColumn.Excluded = entityAction.Fields.Exclude;
                        }
                    }

                    if (entityAction.Policy is not null && entityAction.Policy.Database is not null)
                    {
                        operationToColumn.DatabasePolicy = entityAction.Policy.Database;
                    }

                    // Calculate the set of allowed backing column names.
                    allowedColumns.UnionWith(operationToColumn.Included.Except(operationToColumn.Excluded));

                    // Populate allowed exposed columns for each entity/role/operation combination during startup,
                    // so that it doesn't need to be evaluated per request.
                    PopulateAllowedExposedColumns(operationToColumn.AllowedExposedColumns, entityName, allowedColumns, metadataProvider);

                    IEnumerable<EntityActionOperation> operations = GetAllOperationsForObjectType(operation, entity.Source.Type);
                    foreach (EntityActionOperation crudOperation in operations)
                    {
                        // Try to add the opElement to the map if not present.
                        // Builds up mapping: i.e. Operation.Create permitted in {Role1, Role2, ..., RoleN}
                        if (!entityToRoleMap.OperationToRolesMap.TryAdd(crudOperation, new List<string>(new string[] { role })))
                        {
                            entityToRoleMap.OperationToRolesMap[crudOperation].Add(role);
                        }

                        foreach (string allowedColumn in allowedColumns)
                        {
                            entityToRoleMap.FieldToRolesMap.TryAdd(key: allowedColumn, CreateOperationToRoleMap(entity.Source.Type));
                            entityToRoleMap.FieldToRolesMap[allowedColumn][crudOperation].Add(role);
                        }

                        roleToOperation.OperationToColumnMap[crudOperation] = operationToColumn;
                    }

                    if (ROLE_ANONYMOUS.Equals(role, StringComparison.OrdinalIgnoreCase))
                    {
                        // Saving the allowed columns for anonymous role in case we need to copy the
                        // allowed columns for authenticated role. This reduces the time complexity
                        // for copying over permissions to authenticated role from anonymous role.
                        allowedColumnsForAnonymousRole = allowedColumns;
                    }
                }

                entityToRoleMap.RoleToOperationMap[role] = roleToOperation;
            }

            // Check if anonymous role is defined but authenticated is not. If that is the case,
            // then the authenticated role derives permissions that are atleast equal to anonymous role.
            if (entityToRoleMap.RoleToOperationMap.ContainsKey(ROLE_ANONYMOUS) &&
                !entityToRoleMap.RoleToOperationMap.ContainsKey(ROLE_AUTHENTICATED))
            {
                CopyOverPermissionsFromAnonymousToAuthenticatedRole(entityToRoleMap, allowedColumnsForAnonymousRole);
            }

            EntityPermissionsMap[entityName] = entityToRoleMap;
        }
    }