diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/Manifest.cs index 7f0df0bcc3d..ae830026da6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/Manifest.cs @@ -21,8 +21,8 @@ [assembly: Feature( Id = "OrchardCore.Recipes.Core", - Name = "Recipes", - Description = "Provides recipe services.", + Name = "Recipes Core Services", + Description = "Provides recipe core services.", Category = "Infrastructure", EnabledByDependencyOnly = true )] diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs index 3d28a912373..70d3e128d9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs @@ -10,10 +10,8 @@ using Microsoft.Extensions.Localization; using OrchardCore.Data.Documents; using OrchardCore.DisplayManagement.Notify; -using OrchardCore.Documents; using OrchardCore.Environment.Extensions; using OrchardCore.Environment.Extensions.Features; -using OrchardCore.Roles.Models; using OrchardCore.Roles.ViewModels; using OrchardCore.Security; using OrchardCore.Security.Permissions; @@ -24,39 +22,35 @@ namespace OrchardCore.Roles.Controllers public class AdminController : Controller { private readonly IDocumentStore _documentStore; - private readonly IAuthorizationService _authorizationService; - private readonly IStringLocalizer S; private readonly RoleManager _roleManager; - private readonly IDocumentManager _rolesDocumentManager; + private readonly IAuthorizationService _authorizationService; private readonly IEnumerable _permissionProviders; private readonly ITypeFeatureProvider _typeFeatureProvider; private readonly IRoleService _roleService; private readonly INotifier _notifier; + private readonly IStringLocalizer S; private readonly IHtmlLocalizer H; public AdminController( - IAuthorizationService authorizationService, - ITypeFeatureProvider typeFeatureProvider, IDocumentStore documentStore, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, RoleManager roleManager, - IDocumentManager rolesDocumentManager, + IAuthorizationService authorizationService, + IEnumerable permissionProviders, + ITypeFeatureProvider typeFeatureProvider, IRoleService roleService, INotifier notifier, - IEnumerable permissionProviders - ) + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) { - H = htmlLocalizer; - _notifier = notifier; - _roleService = roleService; - _typeFeatureProvider = typeFeatureProvider; - _permissionProviders = permissionProviders; + _documentStore = documentStore; _roleManager = roleManager; - _rolesDocumentManager = rolesDocumentManager; - S = stringLocalizer; _authorizationService = authorizationService; - _documentStore = documentStore; + _permissionProviders = permissionProviders; + _typeFeatureProvider = typeFeatureProvider; + _roleService = roleService; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; } public async Task Index() @@ -211,42 +205,13 @@ public async Task EditPost(string id, string roleDescription) role.RoleDescription = roleDescription; - var rolesDocument = await _rolesDocumentManager.GetOrCreateMutableAsync(); - var updateRolesDocument = false; - - rolesDocument.PermissionGroups.TryAdd(role.RoleName, new List()); - - var permissionNames = _permissionProviders.SelectMany(x => x.GetDefaultStereotypes()) - .SelectMany(y => y.Permissions ?? Enumerable.Empty()) - .Select(x => x.Name) - .ToList(); - - // Save + // Save. var rolePermissions = new List(); foreach (var key in Request.Form.Keys) { - var permissionName = key; - - if (key.StartsWith("Checkbox.", StringComparison.Ordinal)) - { - permissionName = key.Substring("Checkbox.".Length); - } - - if (!permissionNames.Contains(permissionName, StringComparer.OrdinalIgnoreCase)) - { - // The request contains an invalid permission, let's ignore it - continue; - } - - if (!rolesDocument.PermissionGroups[role.RoleName].Contains(permissionName, StringComparer.OrdinalIgnoreCase)) - { - // Save the permission in the permissions history so it never gets auto assigned to this role. - rolesDocument.PermissionGroups[role.RoleName].Add(permissionName); - updateRolesDocument = true; - } - - if (String.Equals(Request.Form[key], "true", StringComparison.OrdinalIgnoreCase)) + if (key.StartsWith("Checkbox.", StringComparison.Ordinal) && Request.Form[key] == "true") { + var permissionName = key["Checkbox.".Length..]; rolePermissions.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permissionName }); } } @@ -256,11 +221,6 @@ public async Task EditPost(string id, string roleDescription) await _roleManager.UpdateAsync(role); - if (updateRolesDocument) - { - await _rolesDocumentManager.UpdateAsync(rolesDocument); - } - await _notifier.SuccessAsync(H["Role updated successfully."]); return RedirectToAction(nameof(Index)); diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Manifest.cs index e471f0272ab..020797bc8d1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Manifest.cs @@ -5,6 +5,21 @@ Author = ManifestConstants.OrchardCoreTeam, Website = ManifestConstants.OrchardCoreWebsite, Version = ManifestConstants.OrchardCoreVersion, - Description = "The roles module adds the permissions to assign roles to users. It's also provides a set of default roles for which other modules can define default permissions.", + Category = "Security" +)] + +[assembly: Feature( + Id = "OrchardCore.Roles", + Name = "Roles", + Description = "Provides permissions to assign roles to users. Additionally, it updates default roles with default permissions provided by features.", + Dependencies = new[] { "OrchardCore.Roles.Core" }, + Category = "Security" +)] + +[assembly: Feature( + Id = "OrchardCore.Roles.Core", + Name = "Roles Core Services", + Description = "Provides role core services.", + EnabledByDependencyOnly = true, Category = "Security" )] diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs index cb146e71bbc..cabcde1a022 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs @@ -1,19 +1,12 @@ -using System; using System.Collections.Generic; using OrchardCore.Data.Documents; using OrchardCore.Security; -using OrchardCore.Security.Permissions; namespace OrchardCore.Roles.Models { public class RolesDocument : Document { - public List Roles { get; set; } = new List(); - - - /// - /// Keeps track of all permission that were automaticly assigned to a role using /> - /// - public Dictionary> PermissionGroups { get; set; } = new(StringComparer.OrdinalIgnoreCase); + public List Roles { get; set; } = new(); + public Dictionary> MissingFeaturesByRole { get; set; } = new(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs index d4db717b1a5..0f814c24508 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs @@ -67,10 +67,17 @@ public async Task CreateAsync(IRole role, CancellationToken canc throw new ArgumentNullException(nameof(role)); } + var roleToCreate = (Role)role; + var roles = await LoadRolesAsync(); - roles.Roles.Add((Role)role); + roles.Roles.Add(roleToCreate); await UpdateRolesAsync(roles); + var roleCreatedEventHandlers = _serviceProvider.GetRequiredService>(); + + await roleCreatedEventHandlers.InvokeAsync((handler, roleToCreate) => + handler.RoleCreatedAsync(roleToCreate.RoleName), roleToCreate, _logger); + return IdentityResult.Success; } @@ -90,7 +97,9 @@ public async Task DeleteAsync(IRole role, CancellationToken canc } var roleRemovedEventHandlers = _serviceProvider.GetRequiredService>(); - await roleRemovedEventHandlers.InvokeAsync((handler, roleToRemove) => handler.RoleRemovedAsync(roleToRemove.RoleName), roleToRemove, _logger); + + await roleRemovedEventHandlers.InvokeAsync((handler, roleToRemove) => + handler.RoleRemovedAsync(roleToRemove.RoleName), roleToRemove, _logger); var roles = await LoadRolesAsync(); roleToRemove = roles.Roles.FirstOrDefault(r => r.RoleName == roleToRemove.RoleName); @@ -252,8 +261,10 @@ public async Task UpdateAsync(IRole role, CancellationToken canc #endregion IRoleClaimStore +#pragma warning disable CA1816 public void Dispose() { } +#pragma warning restore CA1816 } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs index 8c897b86bc4..aed7ecafa7a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs @@ -1,161 +1,242 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using OrchardCore.Documents; +using OrchardCore.Environment.Extensions; using OrchardCore.Environment.Extensions.Features; using OrchardCore.Environment.Shell; -using OrchardCore.Modules; +using OrchardCore.Environment.Shell.Descriptor.Models; using OrchardCore.Roles.Models; using OrchardCore.Security; using OrchardCore.Security.Permissions; -using OrchardCore.Security.Services; namespace OrchardCore.Roles.Services { - public class RoleUpdater : ModularTenantEvents, IFeatureEventHandler + public class RoleUpdater : IFeatureEventHandler, IRoleCreatedEventHandler, IRoleRemovedEventHandler { - private readonly RoleManager _roleManager; + private readonly ShellDescriptor _shellDescriptor; + private readonly IExtensionManager _extensionManager; + private readonly IDocumentManager _documentManager; private readonly IEnumerable _permissionProviders; + private readonly ITypeFeatureProvider _typeFeatureProvider; private readonly ILogger _logger; - private readonly IRoleService _roleService; - private readonly IDocumentManager _rolesDocumentManager; - private bool _updateInProgress; + private readonly HashSet _installedFeatures = new(); public RoleUpdater( - RoleManager roleManager, + ShellDescriptor shellDescriptor, + IExtensionManager extensionManager, + IDocumentManager documentManager, IEnumerable permissionProviders, - ILogger logger, - IRoleService roleService, - IDocumentManager rolesDocumentManager) + ITypeFeatureProvider typeFeatureProvider, + ILogger logger) { - _roleManager = roleManager; + _shellDescriptor = shellDescriptor; + _extensionManager = extensionManager; + _documentManager = documentManager; _permissionProviders = permissionProviders; + _typeFeatureProvider = typeFeatureProvider; _logger = logger; - _roleService = roleService; - _rolesDocumentManager = rolesDocumentManager; } - Task IFeatureEventHandler.InstallingAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task InstallingAsync(IFeatureInfo feature) => Task.CompletedTask; - Task IFeatureEventHandler.InstalledAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task InstalledAsync(IFeatureInfo feature) => UpdateRolesForInstalledFeatureAsync(feature); - Task IFeatureEventHandler.EnablingAsync(IFeatureInfo feature) => AssignPermissionsToRolesAsync(); + public Task EnablingAsync(IFeatureInfo feature) => Task.CompletedTask; - Task IFeatureEventHandler.EnabledAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task EnabledAsync(IFeatureInfo feature) => UpdateRolesForEnabledFeatureAsync(feature); - Task IFeatureEventHandler.DisablingAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task DisablingAsync(IFeatureInfo feature) => Task.CompletedTask; - Task IFeatureEventHandler.DisabledAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task DisabledAsync(IFeatureInfo feature) => Task.CompletedTask; - Task IFeatureEventHandler.UninstallingAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task UninstallingAsync(IFeatureInfo feature) => Task.CompletedTask; - Task IFeatureEventHandler.UninstalledAsync(IFeatureInfo feature) => Task.CompletedTask; + public Task UninstalledAsync(IFeatureInfo feature) => Task.CompletedTask; - /// - /// This event is called of the very first request to the tenant after a tenant is built/rebuilt. - /// Using this event will ensure that any new permission were added are auto assigned to a role. - /// - /// - public override Task ActivatedAsync() => AssignPermissionsToRolesAsync(); + public Task RoleCreatedAsync(string roleName) => UpdateRoleForInstalledFeaturesAsync(roleName); - /// - /// Checks all available permissions to role mapping from any available . - /// When a new permission is found, auto assign it to the mapped role. If the permission was previously assigned, - /// do not assign the permission. This method could get called multiple time from the same request, - /// so it should not be executed again if the parameter "_updateInProgress" is set to true. - /// - /// - private async Task AssignPermissionsToRolesAsync() + public Task RoleRemovedAsync(string roleName) => RemoveRoleForMissingFeaturesAsync(roleName); + + private async Task UpdateRolesForInstalledFeatureAsync(IFeatureInfo feature) { - if (_updateInProgress) + _installedFeatures.Add(feature.Id); + + var providers = _permissionProviders + .Where(provider => _typeFeatureProvider.GetFeatureForDependency(provider.GetType()).Id == feature.Id); + + if (!providers.Any()) { return; } - _updateInProgress = true; + var updated = false; + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + foreach (var provider in providers) + { + var stereotypes = provider.GetDefaultStereotypes(); + foreach (var stereotype in stereotypes) + { + var role = rolesDocument.Roles.FirstOrDefault(role => role.RoleName == stereotype.Name); + if (role == null) + { + continue; + } + + var permissions = (stereotype.Permissions ?? Enumerable.Empty()) + .Select(stereotype => stereotype.Name); - var roleNames = await _roleService.GetRoleNamesAsync(); + if (UpdateRole(role, permissions, _logger)) + { + updated = true; + } + } + } - if (!roleNames.Any()) + if (updated) + { + await _documentManager.UpdateAsync(rolesDocument); + } + } + + private async Task UpdateRolesForEnabledFeatureAsync(IFeatureInfo feature) + { + if (_installedFeatures.Contains(feature.Id)) { - // Site roles are initially added using the "RolesStep" handler, while no role names are availble. This means - // that RoleUpdater handler was called before the "RolesStep". - // Its likley to be coming from a tenant setup request. Nothing to do. return; } - var rolesDocument = await _rolesDocumentManager.GetOrCreateMutableAsync(); - var updateRolesDocument = false; + var providers = _permissionProviders + .Where(provider => _typeFeatureProvider.GetFeatureForDependency(provider.GetType()).Id == feature.Id); - // Get all the available permissions grouped by role name as defined by IPermissionProvider. - var groups = _permissionProviders.SelectMany(x => x.GetDefaultStereotypes()) - .GroupBy(stereotype => stereotype.Name) - .Select(x => new - { - RoleName = x.Key, - PermissionNames = x.SelectMany(y => y.Permissions ?? Enumerable.Empty()).Select(x => x.Name) - }); + if (!providers.Any()) + { + return; + } - foreach (var group in groups) + var updated = false; + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + foreach (var role in rolesDocument.Roles) { - if (!roleNames.Any(roleName => roleName.Equals(group.RoleName, StringComparison.OrdinalIgnoreCase)) - || await _roleManager.FindByNameAsync(group.RoleName) is not Role role) + if (!rolesDocument.MissingFeaturesByRole.TryGetValue(role.RoleName, out var missingFeatures) || + !missingFeatures.Contains(feature.Id)) { - // A role is mapped in IPermissionProvider, yet it isn't available for the tenant, ignore it. continue; } - var currentPermissionNames = role.RoleClaims.Where(x => x.ClaimType == Permission.ClaimType).Select(x => x.ClaimValue); + updated = true; - var distinctPermissionNames = currentPermissionNames - .Union(group.PermissionNames) - .Distinct() - .ToList(); + missingFeatures.Remove(feature.Id); + UpdateRoleAsync(role, providers, _logger); + } - rolesDocument.PermissionGroups.TryAdd(group.RoleName, new List()); + if (updated) + { + await _documentManager.UpdateAsync(rolesDocument); + } + } - // Get all available permission names that isn't already assigned to the role. - var additionalPermissionNames = distinctPermissionNames.Except(currentPermissionNames); + private async Task UpdateRoleForInstalledFeaturesAsync(string roleName) + { + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + var role = rolesDocument.Roles.FirstOrDefault(role => role.RoleName == roleName); + if (role == null) + { + return; + } - foreach (var permissionName in additionalPermissionNames) - { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("Default role '{Role}' granted permission '{Permission}'", group.RoleName, permissionName); - } + // Get installed features that are no more enabled. + var missingFeatures = _shellDescriptor.Installed + .Except(_shellDescriptor.Features) + .Select(feature => feature.Id) + .ToArray(); - if (rolesDocument.PermissionGroups[group.RoleName].Contains(permissionName, StringComparer.OrdinalIgnoreCase)) - { - // The permission was previously assigned to the role, we can't assign it again. - continue; - } + // And defining at least one 'IPermissionProvider'. + rolesDocument.MissingFeaturesByRole[roleName] = (await _extensionManager.LoadFeaturesAsync(missingFeatures)) + .Where(entry => entry.ExportedTypes.Any(type => type.IsAssignableTo(typeof(IPermissionProvider)))) + .Select(entry => entry.FeatureInfo.Id) + .ToList(); - await _roleManager.AddClaimAsync(role, new Claim(Permission.ClaimType, permissionName)); - } + await _documentManager.UpdateAsync(rolesDocument); - foreach (var distinctPermissionName in distinctPermissionNames) - { - if (rolesDocument.PermissionGroups[group.RoleName].Contains(distinctPermissionName, StringComparer.OrdinalIgnoreCase)) - { - continue; - } + var stereotypes = _permissionProviders + .SelectMany(provider => provider.GetDefaultStereotypes()) + .Where(stereotype => stereotype.Name == roleName); - rolesDocument.PermissionGroups[group.RoleName].Add(distinctPermissionName); - updateRolesDocument = true; - } + if (!stereotypes.Any()) + { + return; + } + + var permissions = stereotypes + .SelectMany(stereotype => stereotype.Permissions ?? Enumerable.Empty()) + .Select(stereotype => stereotype.Name); + + UpdateRole(role, permissions, _logger); + } + + private async Task RemoveRoleForMissingFeaturesAsync(string roleName) + { + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + if (rolesDocument.MissingFeaturesByRole.TryGetValue(roleName, out _)) + { + rolesDocument.MissingFeaturesByRole.Remove(roleName); + await _documentManager.UpdateAsync(rolesDocument); + } + } + + private static bool UpdateRoleAsync(Role role, IEnumerable providers, ILogger logger) + { + var stereotypes = providers + .SelectMany(provider => provider.GetDefaultStereotypes()) + .Where(stereotype => stereotype.Name == role.RoleName); + + if (!stereotypes.Any()) + { + return false; + } + + var permissions = stereotypes + .SelectMany(stereotype => stereotype.Permissions ?? Enumerable.Empty()) + .Select(stereotype => stereotype.Name); + + if (!permissions.Any()) + { + return false; + } + + return UpdateRole(role, permissions, logger); + } + + private static bool UpdateRole(Role role, IEnumerable permissions, ILogger logger) + { + var currentPermissions = role.RoleClaims + .Where(roleClaim => roleClaim.ClaimType == Permission.ClaimType) + .Select(roleClaim => roleClaim.ClaimValue); + + var distinctPermissions = currentPermissions + .Union(permissions) + .Distinct(); + + var additionalPermissions = distinctPermissions.Except(currentPermissions); + if (!additionalPermissions.Any()) + { + return false; } - if (updateRolesDocument) + foreach (var permission in additionalPermissions) { - await _rolesDocumentManager.UpdateAsync(rolesDocument); + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug("Default role '{RoleName}' granted permission '{PermissionName}'.", role.RoleName, permission); + } + + role.RoleClaims.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permission }); } - _updateInProgress = false; + return true; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs index f1337ed3f58..5e3f33507c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs @@ -39,16 +39,10 @@ public override void ConfigureServices(IServiceCollection services) services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); - services.TryAddScoped>(); - services.AddRecipeExecutionStep(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); services.AddScoped(); - services.AddScoped(); services.AddScoped(); + services.AddScoped(); } public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) @@ -92,4 +86,19 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped, AllRolesDeploymentStepDriver>(); } } + + [Feature("OrchardCore.Roles.Core")] + public class RoleUpdaterStartup : StartupBase + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped>(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); + } + } } diff --git a/src/OrchardCore.Modules/OrchardCore.Users/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.Users/Manifest.cs index d8267e9e720..f48a9c842c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/Manifest.cs @@ -11,6 +11,7 @@ Id = "OrchardCore.Users", Name = "Users", Description = "The users module enables authentication UI and user management.", + Dependencies = new[] { "OrchardCore.Roles.Core" }, Category = "Security" )] diff --git a/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/Security/IRoleCreatedEventHandler.cs b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/Security/IRoleCreatedEventHandler.cs new file mode 100644 index 00000000000..2367712e639 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/Security/IRoleCreatedEventHandler.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace OrchardCore.Security +{ + public interface IRoleCreatedEventHandler + { + Task RoleCreatedAsync(string roleName); + } +} diff --git a/src/OrchardCore/OrchardCore.Users.Core/Extensions/UsersServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.Users.Core/Extensions/UsersServiceCollectionExtensions.cs index 7df5d4795f1..9f19d3e2f02 100644 --- a/src/OrchardCore/OrchardCore.Users.Core/Extensions/UsersServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.Users.Core/Extensions/UsersServiceCollectionExtensions.cs @@ -36,9 +36,6 @@ public static IServiceCollection AddUsers(this IServiceCollection services) services.TryAddScoped>(sp => sp.GetRequiredService()); services.TryAddScoped>(sp => sp.GetRequiredService()); - services.TryAddScoped>(); - services.TryAddScoped(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/OrchardCore/OrchardCore.Users.Core/Services/NullRoleStore.cs b/src/OrchardCore/OrchardCore.Users.Core/Services/NullRoleStore.cs index eb655499e8c..a89be4b5a98 100644 --- a/src/OrchardCore/OrchardCore.Users.Core/Services/NullRoleStore.cs +++ b/src/OrchardCore/OrchardCore.Users.Core/Services/NullRoleStore.cs @@ -21,9 +21,11 @@ public Task CreateAsync(IRole role, CancellationToken cancellati public Task DeleteAsync(IRole role, CancellationToken cancellationToken) => Task.FromResult(IdentityResult.Success); +#pragma warning disable CA1816 public void Dispose() { } +#pragma warning restore CA1816 public Task FindByIdAsync(string roleId, CancellationToken cancellationToken) => Task.FromResult(null); diff --git a/src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs b/src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs index a442f887f84..774bc61d29e 100644 --- a/src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs +++ b/src/OrchardCore/OrchardCore.Users.Core/Services/UserStore.cs @@ -805,8 +805,11 @@ public Task SetTokenAsync(IUser user, string loginProvider, string name, string u.UserTokens.Add(userToken); } - // Encrypt the token - userToken.Value = _dataProtectionProvider.CreateProtector(TokenProtector).Protect(value); + // Encrypt the token. + if (userToken != null) + { + userToken.Value = _dataProtectionProvider.CreateProtector(TokenProtector).Protect(value); + } return Task.CompletedTask; } diff --git a/src/docs/reference/modules/Recipes/README.md b/src/docs/reference/modules/Recipes/README.md index 7056f78670b..a7dc03691c6 100644 --- a/src/docs/reference/modules/Recipes/README.md +++ b/src/docs/reference/modules/Recipes/README.md @@ -319,6 +319,9 @@ The Roles step allows you to set permissions to specific roles. } ``` +!!! warning + As of version 1.6, the default roles are no longer auto created. Setup recipe must define the default roles to be used. The `Roles` feature will automatically map all known permissions to the defined roles each time a feature is enabled. + ### Template and AdminTemplate Step The Template and AdminTemplate steps allow you to create Liquid Templates.