From 28934aa274cd94bf240fc7c41c85c3864936a5f9 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Wed, 20 Nov 2024 18:45:13 +0300 Subject: [PATCH 01/10] - save --- src/Acl.Net.Core.Database/AclDbContext.cs | 1 + src/Acl.Net.Core.Managers/IResourceManager.cs | 27 +++++++++++++++++-- src/Acl.Net.Core.Managers/ResourceManager.cs | 16 ++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Acl.Net.Core.Database/AclDbContext.cs b/src/Acl.Net.Core.Database/AclDbContext.cs index 47e97a8..573b2ac 100644 --- a/src/Acl.Net.Core.Database/AclDbContext.cs +++ b/src/Acl.Net.Core.Database/AclDbContext.cs @@ -110,6 +110,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(u => u.Id); + entity.HasIndex(u => new { u.Name, u.RoleId }); entity.Property(u => u.Name).IsRequired(); entity.HasOne().WithMany().HasForeignKey(ut => ut.RoleId).IsRequired(); diff --git a/src/Acl.Net.Core.Managers/IResourceManager.cs b/src/Acl.Net.Core.Managers/IResourceManager.cs index cc63ac2..ee5ef5f 100644 --- a/src/Acl.Net.Core.Managers/IResourceManager.cs +++ b/src/Acl.Net.Core.Managers/IResourceManager.cs @@ -12,7 +12,7 @@ public interface IResourceManager : IResourceManager; /// Defines the contract for managing resource-related operations with a specific key type. /// /// The type of the key. -public interface IResourceManager : IResourceManager, Resource> +public interface IResourceManager : IResourceManager, Role, Resource> where TKey : IEquatable; /// @@ -20,12 +20,35 @@ public interface IResourceManager : IResourceManager, Res /// /// The type of the key, which must implement . /// The type representing a user, which must inherit from . +/// The type representing a role, which must inherit from . /// The type representing a resource, which must inherit from . -public interface IResourceManager +public interface IResourceManager where TKey : IEquatable where TUser : User + where TRole : Role where TResource : Resource { + /// + /// Determines whether the specified role is permitted to access the resource identified by its name. + /// + /// The role to check permissions for. + /// The name of the resource to check. + /// + /// if the role is permitted to access the resource; otherwise, . + /// + /// Thrown when the specified resource name does not exist. + public bool IsPermitted(TRole role, string resourceName); + + /// + /// Determines whether the specified role is permitted to access the given resource. + /// + /// The role to check permissions for. + /// The resource to check. + /// + /// if the role is permitted to access the resource; otherwise, . + /// + public bool IsPermitted(TRole role, TResource resource); + /// /// Determines whether the specified user is permitted to access the resource identified by its name. /// diff --git a/src/Acl.Net.Core.Managers/ResourceManager.cs b/src/Acl.Net.Core.Managers/ResourceManager.cs index a403c5c..445b278 100644 --- a/src/Acl.Net.Core.Managers/ResourceManager.cs +++ b/src/Acl.Net.Core.Managers/ResourceManager.cs @@ -46,7 +46,7 @@ IInitialDataSeeder> initialDataSeeder /// The type of the user, which must inherit from . /// The type of the role, which must inherit from . /// The type of the resource, which must inherit from . -public class ResourceManager : IResourceManager +public class ResourceManager : IResourceManager where TKey : IEquatable where TUser : User, new() where TRole : Role @@ -69,6 +69,20 @@ IInitialDataSeeder initialDataSeeder InitialDataSeeder = initialDataSeeder; } + /// + public bool IsPermitted(TRole role, string resourceName) + { + var resource = GetResourceByName(resourceName); + return IsPermitted(role, resource); + } + + /// + public bool IsPermitted(TRole role, TResource resource) + { + return role.Id.Equals(InitialDataSeeder.SeedAdminRole().Id) || + Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); + } + /// public virtual bool IsPermitted(TUser user, string resourceName) { From 77ffbfe088c3a47de8dcd5b6477699e9cccdb124 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 10:52:59 +0300 Subject: [PATCH 02/10] - update code --- src/Acl.Net.Core.Managers/AclManager.cs | 10 ++++----- src/Acl.Net.Core.Managers/IUserManager.cs | 6 +++++- src/Acl.Net.Core.Managers/ResourceManager.cs | 22 ++++++++++++-------- src/Acl.Net.Core.Managers/UserManager.cs | 12 +++++++++++ 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Acl.Net.Core.Managers/AclManager.cs b/src/Acl.Net.Core.Managers/AclManager.cs index fa27873..80432e6 100644 --- a/src/Acl.Net.Core.Managers/AclManager.cs +++ b/src/Acl.Net.Core.Managers/AclManager.cs @@ -119,7 +119,7 @@ public class AclManager : IAclManager InitialDataSeeder; protected readonly IUserManager UserManager; - protected readonly IResourceManager ResourceManager; + protected readonly IResourceManager ResourceManager; /// /// Initializes a new instance of the class @@ -135,20 +135,20 @@ AclDbContext context { InitialDataSeeder = initialDataSeeder; UserManager = new UserManager(context); - ResourceManager = new ResourceManager(context, initialDataSeeder); + ResourceManager = new ResourceManager(context, initialDataSeeder, UserManager); } /// /// Initializes a new instance of the class - /// with the provided , , and . + /// with the provided , , and . /// /// An implementation of used to seed initial role data. /// An implementation of for user management. - /// An implementation of for resource management. + /// An implementation of for resource management. public AclManager( IInitialDataSeeder initialDataSeeder, IUserManager userManager, - IResourceManager resourceManager + IResourceManager resourceManager ) { InitialDataSeeder = initialDataSeeder; diff --git a/src/Acl.Net.Core.Managers/IUserManager.cs b/src/Acl.Net.Core.Managers/IUserManager.cs index 8fda48d..f122353 100644 --- a/src/Acl.Net.Core.Managers/IUserManager.cs +++ b/src/Acl.Net.Core.Managers/IUserManager.cs @@ -22,11 +22,15 @@ public interface IUserManager : IUserManager, Role> /// The type of the key, which must implement . /// The type representing a user, which must inherit from . /// The type representing a role, which must inherit from . -public interface IUserManager +public interface IUserManager where TKey : IEquatable where TUser : User where TRole : Role { + public IEnumerable GetUserRoles(string userName); + + public IEnumerable GetUserRoles(TUser user); + /// /// Retrieves an existing user by name or creates a new one with the specified role if it doesn't exist. /// diff --git a/src/Acl.Net.Core.Managers/ResourceManager.cs b/src/Acl.Net.Core.Managers/ResourceManager.cs index 445b278..2b4e6b0 100644 --- a/src/Acl.Net.Core.Managers/ResourceManager.cs +++ b/src/Acl.Net.Core.Managers/ResourceManager.cs @@ -15,7 +15,7 @@ public class ResourceManager : ResourceManager, IResourceManager /// /// The database context. public ResourceManager(AclDbContext context) - : base(context, new RoleDataSeeder()) + : base(context, new RoleDataSeeder(), new UserManager(context)) { } } @@ -31,11 +31,13 @@ public class ResourceManager : ResourceManager, Role /// The database context. /// The initial data seeder. + /// An implementation of . public ResourceManager( AclDbContext context, - IInitialDataSeeder> initialDataSeeder + IInitialDataSeeder> initialDataSeeder, + IUserManager, Role> userManager ) - : base(context, initialDataSeeder) + : base(context, initialDataSeeder, userManager) { } } @@ -54,19 +56,23 @@ public class ResourceManager : IResourceManager Context; protected readonly IInitialDataSeeder InitialDataSeeder; + protected readonly IUserManager UserManager; /// /// Initializes a new instance of the class. /// /// The database context. /// The initial data seeder for roles. + /// An implementation of . public ResourceManager( AclDbContext context, - IInitialDataSeeder initialDataSeeder + IInitialDataSeeder initialDataSeeder, + IUserManager userManager ) { Context = context; InitialDataSeeder = initialDataSeeder; + UserManager = userManager; } /// @@ -93,14 +99,13 @@ public virtual bool IsPermitted(TUser user, string resourceName) /// public virtual bool IsPermitted(TUser user, TResource resource) { - return user.RoleId.Equals(InitialDataSeeder.SeedAdminRole().Id) || - Context.Resources.Any(r => r.RoleId.Equals(user.RoleId) && r.Id.Equals(resource.Id)); + return UserManager.GetUserRoles(user).Any(u => IsPermitted(u, resource)); } /// public virtual IEnumerable IsPermitted(TUser user, IEnumerable resourceNames) { - return resourceNames.Select(GetResourceByName).Where(resource => IsPermitted(user, resource)); + return IsPermitted(user, resourceNames.Select(GetResourceByName)); } /// @@ -119,8 +124,7 @@ public virtual async Task IsPermittedAsync(TUser user, string resourceName /// public virtual async Task IsPermittedAsync(TUser user, TResource resource) { - return user.RoleId.Equals(InitialDataSeeder.SeedAdminRole().Id) || - await Context.Resources.AnyAsync(r => r.RoleId.Equals(user.RoleId) && r.Id.Equals(resource.Id)); + return await Task.FromResult(IsPermitted(user, resource)); } /// diff --git a/src/Acl.Net.Core.Managers/UserManager.cs b/src/Acl.Net.Core.Managers/UserManager.cs index 547bb6d..e1e9e51 100644 --- a/src/Acl.Net.Core.Managers/UserManager.cs +++ b/src/Acl.Net.Core.Managers/UserManager.cs @@ -58,6 +58,18 @@ public UserManager(AclDbContext context) Context = context; } + public IEnumerable GetUserRoles(TUser user) => GetUserRoles(user.Name); + + public IEnumerable GetUserRoles(string userName) + { + return Context.Roles + .Where(r => Context.Users + .Where(u => u.Name == userName) + .Select(u => u.RoleId) + .Contains(r.Id)) + .ToArray(); + } + /// public virtual TUser UserProcessing(string userName, TRole roleForNewUsers) { From 6f312d7f66ad361f83d772f942a3fd43d65d165f Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 13:12:16 +0300 Subject: [PATCH 03/10] - fix all exist tests --- tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs b/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs index 1824a55..e41362b 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs @@ -50,6 +50,7 @@ internal static AclDbContext CreateContext() context.Users.AddRange(Users); context.Resources.AddRange(Resources); + context.Roles.AddRange(AdminRole, UserRole); context.SaveChanges(); From 8e1be0b7a70901d8ffd6aeaa67ab9ccb95a0fde4 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 14:55:07 +0300 Subject: [PATCH 04/10] - remove async methods --- src/Acl.Net.Core.Managers/AclManager.cs | 33 ---------- src/Acl.Net.Core.Managers/IAclManager.cs | 63 ------------------- src/Acl.Net.Core.Managers/IResourceManager.cs | 57 ----------------- src/Acl.Net.Core.Managers/ResourceManager.cs | 49 --------------- .../Managers/AclManagerTests.cs | 54 ---------------- .../Managers/ResourceManagerTests.cs | 40 ------------ 6 files changed, 296 deletions(-) diff --git a/src/Acl.Net.Core.Managers/AclManager.cs b/src/Acl.Net.Core.Managers/AclManager.cs index 80432e6..f3ddfca 100644 --- a/src/Acl.Net.Core.Managers/AclManager.cs +++ b/src/Acl.Net.Core.Managers/AclManager.cs @@ -188,37 +188,4 @@ public virtual IEnumerable IsPermitted(string userName, IEnumerable - public virtual async Task IsPermittedAsync(string userName, string resourceName) - { - var user = await UserManager.UserProcessingAsync(userName, InitialDataSeeder.SeedUserRole()); - return await ResourceManager.IsPermittedAsync(user, resourceName); - } - - /// - public virtual async Task IsPermittedAsync(TUser user, string resourceName) - { - return await ResourceManager.IsPermittedAsync(user, resourceName); - } - - /// - public virtual async Task IsPermittedAsync(string userName, TResource resource) - { - var user = await UserManager.UserProcessingAsync(userName, InitialDataSeeder.SeedUserRole()); - return await ResourceManager.IsPermittedAsync(user, resource); - } - - /// - public virtual async Task IsPermittedAsync(TUser user, TResource resource) - { - return await ResourceManager.IsPermittedAsync(user, resource); - } - - /// - public virtual async Task> IsPermittedAsync(string userName, IEnumerable resourceNames) - { - var user = await UserManager.UserProcessingAsync(userName, InitialDataSeeder.SeedUserRole()); - return await ResourceManager.IsPermittedAsync(user, resourceNames); - } } diff --git a/src/Acl.Net.Core.Managers/IAclManager.cs b/src/Acl.Net.Core.Managers/IAclManager.cs index 8660db3..db10d40 100644 --- a/src/Acl.Net.Core.Managers/IAclManager.cs +++ b/src/Acl.Net.Core.Managers/IAclManager.cs @@ -79,67 +79,4 @@ public interface IAclManager /// /// Thrown when one or more of the specified resource names do not exist. public IEnumerable IsPermitted(string userName, IEnumerable resourceNames); - - /// - /// Determines asynchronously if the specified user by name is permitted to access the specified resource by name. - /// - /// The name of the user to check permission for. - /// The name of the resource to check permission against. - /// - /// A task that represents the asynchronous operation. - /// The task result contains if the user is permitted to access the resource; - /// otherwise, . - /// - /// Thrown when the specified resource name does not exist. - public Task IsPermittedAsync(string userName, string resourceName); - - /// - /// Determines asynchronously if the specified user object is permitted to access the specified resource by name. - /// - /// The user object to check permission for. - /// The name of the resource to check permission against. - /// - /// A task that represents the asynchronous operation. - /// The task result contains if the user is permitted to access the resource; - /// otherwise, . - /// - /// Thrown when the specified resource name does not exist. - public Task IsPermittedAsync(TUser user, string resourceName); - - /// - /// Determines asynchronously if the specified user by name is permitted to access the specified resource object. - /// - /// The name of the user to check permission for. - /// The resource object to check permission against. - /// - /// A task that represents the asynchronous operation. - /// The task result contains if the user is permitted to access the resource; - /// otherwise, . - /// - public Task IsPermittedAsync(string userName, TResource resource); - - /// - /// Determines asynchronously if the specified user object is permitted to access the specified resource object. - /// - /// The user object to check permission for. - /// The resource object to check permission against. - /// - /// A task that represents the asynchronous operation. - /// The task result contains if the user is permitted to access the resource; - /// otherwise, . - /// - public Task IsPermittedAsync(TUser user, TResource resource); - - /// - /// Determines asynchronously the resources that the specified user by name is permitted to access from a collection of resource names. - /// - /// The name of the user to check permission for. - /// The collection of resource names to check permissions against. - /// - /// A task that represents the asynchronous operation. - /// The task result contains a collection of objects that the user is permitted to access; - /// an empty collection if the user is not permitted to access any of the resources. - /// - /// Thrown when the specified resource name does not exist. - public Task> IsPermittedAsync(string userName, IEnumerable resourceNames); } \ No newline at end of file diff --git a/src/Acl.Net.Core.Managers/IResourceManager.cs b/src/Acl.Net.Core.Managers/IResourceManager.cs index ee5ef5f..2bb9399 100644 --- a/src/Acl.Net.Core.Managers/IResourceManager.cs +++ b/src/Acl.Net.Core.Managers/IResourceManager.cs @@ -91,52 +91,6 @@ public interface IResourceManager /// public IEnumerable IsPermitted(TUser user, IEnumerable resources); - /// - /// Asynchronously determines whether the specified user is permitted to access the resource identified by its name. - /// - /// The user to check permissions for. - /// The name of the resource to check. - /// - /// A task that represents the asynchronous operation. - /// The task result is if the user is permitted to access the resource; otherwise, . - /// - /// Thrown when the specified resource name does not exist. - public Task IsPermittedAsync(TUser user, string resourceName); - - /// - /// Asynchronously determines whether the specified user is permitted to access the given resource. - /// - /// The user to check permissions for. - /// The resource to check. - /// - /// A task that represents the asynchronous operation. - /// The task result is if the user is permitted to access the resource; otherwise, . - /// - public Task IsPermittedAsync(TUser user, TResource resource); - - /// - /// Asynchronously determines which resources from a collection of resource names the specified user is permitted to access. - /// - /// The user to check permissions for. - /// The collection of resource names to check. - /// - /// A task that represents the asynchronous operation. - /// The task result contains an enumerable of that the user is permitted to access. - /// - /// Thrown when the specified resource name does not exist. - public Task> IsPermittedAsync(TUser user, IEnumerable resourceNames); - - /// - /// Asynchronously determines which resources from a collection the specified user is permitted to access. - /// - /// The user to check permissions for. - /// The collection of resources to check. - /// - /// A task that represents the asynchronous operation. - /// The task result contains an enumerable of that the user is permitted to access. - /// - public Task> IsPermittedAsync(TUser user, IEnumerable resources); - /// /// Retrieves a resource by its name. /// @@ -146,15 +100,4 @@ public interface IResourceManager /// /// Thrown when the specified resource name does not exist. public TResource GetResourceByName(string resourceName); - - /// - /// Asynchronously retrieves a resource by its name. - /// - /// The name of the resource to retrieve. - /// - /// A task that represents the asynchronous operation. - /// The task result contains the with the specified name. - /// - /// Thrown when the specified resource name does not exist. - public Task GetResourceByNameAsync(string resourceName); } diff --git a/src/Acl.Net.Core.Managers/ResourceManager.cs b/src/Acl.Net.Core.Managers/ResourceManager.cs index 2b4e6b0..66a7044 100644 --- a/src/Acl.Net.Core.Managers/ResourceManager.cs +++ b/src/Acl.Net.Core.Managers/ResourceManager.cs @@ -114,59 +114,10 @@ public virtual IEnumerable IsPermitted(TUser user, IEnumerable IsPermitted(user, resource)); } - /// - public virtual async Task IsPermittedAsync(TUser user, string resourceName) - { - var resource = await GetResourceByNameAsync(resourceName); - return await IsPermittedAsync(user, resource); - } - - /// - public virtual async Task IsPermittedAsync(TUser user, TResource resource) - { - return await Task.FromResult(IsPermitted(user, resource)); - } - - /// - public virtual async Task> IsPermittedAsync(TUser user, IEnumerable resourceNames) - { - var permittedResources = new List(); - foreach (var resourceName in resourceNames) - { - var resource = await GetResourceByNameAsync(resourceName); - if (await IsPermittedAsync(user, resource)) - { - permittedResources.Add(resource); - } - } - return permittedResources.AsEnumerable(); - } - - /// - public virtual async Task> IsPermittedAsync(TUser user, IEnumerable resources) - { - var permittedResources = new List(); - foreach (var resource in resources) - { - if (await IsPermittedAsync(user, resource)) - { - permittedResources.Add(resource); - } - } - return permittedResources.AsEnumerable(); - } - /// public virtual TResource GetResourceByName(string resourceName) { return Context.Resources.FirstOrDefault(r => r.Name == resourceName) ?? throw new ResourceNotFoundException(resourceName); } - - /// - public virtual async Task GetResourceByNameAsync(string resourceName) - { - return await Context.Resources.FirstOrDefaultAsync(r => r.Name == resourceName) - ?? throw new ResourceNotFoundException(resourceName); - } } diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs index bfa5b71..6f0a986 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs @@ -94,58 +94,4 @@ public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResourceByResourc var user = InMemoryAclDbContext.UserAccount; Assert.False(_aclManager.IsPermitted(user, privateResource)); } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnPermittedResources_WhenGivenResourceNames() - { - Assert.Single(await _aclManager.IsPermittedAsync("AdminAccount", new[] { "PrivateResource" })); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnFalse_WhenUserAccessPrivateResource() - { - Assert.False(await _aclManager.IsPermittedAsync("UserAccount", "PrivateResource")); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnTrue_WhenAdminAccessPrivateResource() - { - Assert.True(await _aclManager.IsPermittedAsync("AdminAccount", "PrivateResource")); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnTrue_WhenAdminAccessPrivateResourceByUserObject() - { - var adminUser = InMemoryAclDbContext.AdminAccount; - Assert.True(await _aclManager.IsPermittedAsync(adminUser, "PrivateResource")); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnFalse_WhenUserAccessPrivateResourceByUserObject() - { - var user = InMemoryAclDbContext.UserAccount; - Assert.False(await _aclManager.IsPermittedAsync(user, "PrivateResource")); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnTrue_WhenAdminAccessPrivateResourceByResourceObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - Assert.True(await _aclManager.IsPermittedAsync("AdminAccount", privateResource)); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnFalse_WhenUserAccessPrivateResourceByResourceObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - Assert.False(await _aclManager.IsPermittedAsync("UserAccount", privateResource)); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnFalse_WhenUserAccessPrivateResourceByResourceObjectAndUserObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - var user = InMemoryAclDbContext.UserAccount; - Assert.False(await _aclManager.IsPermittedAsync(user, privateResource)); - } } diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs index 7b8c9f4..963408a 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs @@ -61,30 +61,6 @@ public void IsPermitted_ShouldReturnPermittedResources_WhenGivenResources() Assert.Equal(_privateResource, resources[0]); } - [Fact] - public async Task IsPermittedAsync_ShouldReturnTrue_WhenAdminAccessPrivateResource() - { - Assert.True(await _resourceManager.IsPermittedAsync(_adminUser, _privateResource)); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnFalse_WhenUserAccessPrivateResource() - { - Assert.False(await _resourceManager.IsPermittedAsync(_normalUser, _privateResource)); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnPermittedResources_WhenGivenResourceNames() - { - Assert.Single(await _resourceManager.IsPermittedAsync(_adminUser, new[] { "PrivateResource" })); - } - - [Fact] - public async Task IsPermittedAsync_ShouldReturnPermittedResources_WhenGivenResources() - { - Assert.Single(await _resourceManager.IsPermittedAsync(_adminUser, new[] { _privateResource })); - } - [Fact] public void GetResourceByName_ShouldReturnResource_WhenResourceExists() { @@ -100,20 +76,4 @@ public void GetResourceByName_ShouldThrowResourceNotFoundException_WhenResourceD const string nonExistentResourceName = "NonExistentResource"; Assert.Throws(() => _resourceManager.GetResourceByName(nonExistentResourceName)); } - - [Fact] - public async Task GetResourceByNameAsync_ShouldReturnResource_WhenResourceExists() - { - const string expectedResourceName = "PrivateResource"; - var resource = await _resourceManager.GetResourceByNameAsync(expectedResourceName); - Assert.NotNull(resource); - Assert.Equal(expectedResourceName, resource.Name); - } - - [Fact] - public async Task GetResourceByNameAsync_ShouldThrowResourceNotFoundException_WhenResourceDoesNotExist() - { - const string nonExistentResourceName = "NonExistentResource"; - await Assert.ThrowsAsync(() => _resourceManager.GetResourceByNameAsync(nonExistentResourceName)); - } } \ No newline at end of file From 4979ea915408efa269d610e67c538ab0ec6aecfc Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 16:15:13 +0300 Subject: [PATCH 05/10] - remove UserManager - remove ResourceManager - simplify AclManager - add UserNotFoundException --- src/Acl.Net.Core.Managers/AclManager.cs | 120 ++++------------- .../Exceptions/UserNotFoundException.cs | 15 +++ src/Acl.Net.Core.Managers/IAclManager.cs | 10 +- src/Acl.Net.Core.Managers/IResourceManager.cs | 103 --------------- src/Acl.Net.Core.Managers/IUserManager.cs | 49 ------- src/Acl.Net.Core.Managers/ResourceManager.cs | 123 ------------------ src/Acl.Net.Core.Managers/UserManager.cs | 98 -------------- 7 files changed, 47 insertions(+), 471 deletions(-) create mode 100644 src/Acl.Net.Core.Managers/Exceptions/UserNotFoundException.cs delete mode 100644 src/Acl.Net.Core.Managers/IResourceManager.cs delete mode 100644 src/Acl.Net.Core.Managers/IUserManager.cs delete mode 100644 src/Acl.Net.Core.Managers/ResourceManager.cs delete mode 100644 src/Acl.Net.Core.Managers/UserManager.cs diff --git a/src/Acl.Net.Core.Managers/AclManager.cs b/src/Acl.Net.Core.Managers/AclManager.cs index f3ddfca..2860f62 100644 --- a/src/Acl.Net.Core.Managers/AclManager.cs +++ b/src/Acl.Net.Core.Managers/AclManager.cs @@ -1,5 +1,6 @@ using Acl.Net.Core.Database; using Acl.Net.Core.Database.Entities; +using Acl.Net.Core.Managers.Exceptions; namespace Acl.Net.Core.Managers; @@ -10,9 +11,7 @@ namespace Acl.Net.Core.Managers; public class AclManager : AclManager, IAclManager { /// - /// Initializes a new instance of the class - /// with the default - /// and with default and . + /// Initializes a new instance of the class and with the default . /// /// An implementation, or default . public AclManager( @@ -22,9 +21,8 @@ AclDbContext context { } /// - /// Initializes a new instance of the class - /// with the provided - /// and with default and . + /// Initializes a new instance of the class + /// with the provided and . /// /// An implementation of used to seed initial role data. /// An implementation, or default . @@ -34,35 +32,6 @@ AclDbContext context ) : base(initialDataSeeder, context) { } - - /// - /// Initializes a new instance of the class - /// with the default - /// and with the provided , and . - /// - /// An implementation of . - /// An implementation of . - public AclManager( - IUserManager userManager, - IResourceManager resourceManager - ) - : base(new RoleDataSeeder(), userManager, resourceManager) - { } - - /// - /// Initializes a new instance of the class - /// with the provided , , and . - /// - /// An implementation of used to seed initial role data. - /// An implementation of . - /// An implementation of . - public AclManager( - IInitialDataSeeder> initialDataSeeder, - IUserManager userManager, - IResourceManager resourceManager - ) - : base(initialDataSeeder, userManager, resourceManager) - { } } /// @@ -75,8 +44,7 @@ public class AclManager : AclManager, Role, Resourc { /// /// Initializes a new instance of the class - /// with the provided - /// and with default and . + /// with the provided and . /// /// An implementation of used to seed initial role data. /// An implementation, or default . @@ -86,21 +54,6 @@ AclDbContext context ) : base(initialDataSeeder, context) { } - - /// - /// Initializes a new instance of the class - /// with the provided , , and . - /// - /// An implementation of used to seed initial role data. - /// An implementation of . - /// An implementation of . - public AclManager( - IInitialDataSeeder> initialDataSeeder, - IUserManager userManager, - IResourceManager resourceManager - ) - : base(initialDataSeeder, userManager, resourceManager) - { } } /// @@ -118,13 +71,11 @@ public class AclManager : IAclManager { protected readonly IInitialDataSeeder InitialDataSeeder; - protected readonly IUserManager UserManager; - protected readonly IResourceManager ResourceManager; + protected readonly AclDbContext Context; /// /// Initializes a new instance of the class - /// with the provided - /// and with default and . + /// with the provided and . /// /// An implementation of used to seed initial role data. /// An implementation, or default . @@ -134,58 +85,41 @@ AclDbContext context ) { InitialDataSeeder = initialDataSeeder; - UserManager = new UserManager(context); - ResourceManager = new ResourceManager(context, initialDataSeeder, UserManager); - } - - /// - /// Initializes a new instance of the class - /// with the provided , , and . - /// - /// An implementation of used to seed initial role data. - /// An implementation of for user management. - /// An implementation of for resource management. - public AclManager( - IInitialDataSeeder initialDataSeeder, - IUserManager userManager, - IResourceManager resourceManager - ) - { - InitialDataSeeder = initialDataSeeder; - UserManager = userManager; - ResourceManager = resourceManager; + Context = context; } /// - public virtual bool IsPermitted(string userName, string resourceName) + public bool IsPermitted(TRole role, TResource resource) { - var user = UserManager.UserProcessing(userName, InitialDataSeeder.SeedUserRole()); - return ResourceManager.IsPermitted(user, resourceName); + return IsAdmin(role) || Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); } - /// - public virtual bool IsPermitted(TUser user, string resourceName) + public bool IsPermitted(TUser user, TResource resource) { - return ResourceManager.IsPermitted(user, resourceName); + return IsAdmin(user) || GetUserRoles(user.Name).Any(role => IsPermitted(role, resource)); } - /// - public virtual bool IsPermitted(string userName, TResource resource) + public bool IsAdmin(TKey roleId) => roleId.Equals(InitialDataSeeder.SeedAdminRole().Id); + public bool IsAdmin(TUser user) => IsAdmin(user.RoleId); + public bool IsAdmin(TRole role) => IsAdmin(role.Id); + + public IEnumerable GetUserRoles(string userName) { - var user = UserManager.UserProcessing(userName, InitialDataSeeder.SeedUserRole()); - return ResourceManager.IsPermitted(user, resource); + return Context.Roles + .Where(r => Context.Users + .Where(u => u.Name == userName) + .Select(u => u.RoleId) + .Contains(r.Id)) + .ToArray(); } - /// - public virtual bool IsPermitted(TUser user, TResource resource) + private TResource GetResourceByName(string resourceName) { - return ResourceManager.IsPermitted(user, resource); + return Context.Resources.FirstOrDefault(r => r.Name == resourceName) ?? throw new ResourceNotFoundException(resourceName); } - /// - public virtual IEnumerable IsPermitted(string userName, IEnumerable resourceNames) + private TUser GetUserByName(string userName) { - var user = UserManager.UserProcessing(userName, InitialDataSeeder.SeedUserRole()); - return ResourceManager.IsPermitted(user, resourceNames); + return Context.Users.FirstOrDefault(r => r.Name == userName) ?? throw new UserNotFoundException(userName); } } diff --git a/src/Acl.Net.Core.Managers/Exceptions/UserNotFoundException.cs b/src/Acl.Net.Core.Managers/Exceptions/UserNotFoundException.cs new file mode 100644 index 0000000..314f6c0 --- /dev/null +++ b/src/Acl.Net.Core.Managers/Exceptions/UserNotFoundException.cs @@ -0,0 +1,15 @@ +namespace Acl.Net.Core.Managers.Exceptions; + +/// +/// Represents an exception that is thrown when a specific user is not found. +/// +public class UserNotFoundException : Exception +{ + /// + /// Initializes a new instance of the class with the specified user-name. + /// + /// The name of the user that could not be found. + public UserNotFoundException(string userName) + : base($"User with name '{userName}' not found.") + { } +} \ No newline at end of file diff --git a/src/Acl.Net.Core.Managers/IAclManager.cs b/src/Acl.Net.Core.Managers/IAclManager.cs index db10d40..edf030a 100644 --- a/src/Acl.Net.Core.Managers/IAclManager.cs +++ b/src/Acl.Net.Core.Managers/IAclManager.cs @@ -35,7 +35,7 @@ public interface IAclManager /// if the user is permitted to access the resource; otherwise, . /// /// Thrown when the specified resource name does not exist. - public bool IsPermitted(string userName, string resourceName); + //public bool IsPermitted(string userName, string resourceName); /// /// Determines if the specified user object is permitted to access the specified resource by name. @@ -46,7 +46,7 @@ public interface IAclManager /// if the user is permitted to access the resource; otherwise, . /// /// Thrown when the specified resource name does not exist. - public bool IsPermitted(TUser user, string resourceName); + //public bool IsPermitted(TUser user, string resourceName); /// /// Determines if the specified user by name is permitted to access the specified resource object. @@ -56,7 +56,7 @@ public interface IAclManager /// /// if the user is permitted to access the resource; otherwise, . /// - public bool IsPermitted(string userName, TResource resource); + //public bool IsPermitted(string userName, TResource resource); /// /// Determines if the specified user object is permitted to access the specified resource object. @@ -66,7 +66,7 @@ public interface IAclManager /// /// if the user is permitted to access the resource; otherwise, . /// - public bool IsPermitted(TUser user, TResource resource); + //public bool IsPermitted(TUser user, TResource resource); /// /// Determines the resources that the specified user by name is permitted to access from a collection of resource names. @@ -78,5 +78,5 @@ public interface IAclManager /// an empty collection if the user is not permitted to access any of the resources. /// /// Thrown when one or more of the specified resource names do not exist. - public IEnumerable IsPermitted(string userName, IEnumerable resourceNames); + //public IEnumerable IsPermitted(string userName, IEnumerable resourceNames); } \ No newline at end of file diff --git a/src/Acl.Net.Core.Managers/IResourceManager.cs b/src/Acl.Net.Core.Managers/IResourceManager.cs deleted file mode 100644 index 2bb9399..0000000 --- a/src/Acl.Net.Core.Managers/IResourceManager.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Acl.Net.Core.Database.Entities; -using Acl.Net.Core.Managers.Exceptions; - -namespace Acl.Net.Core.Managers; - -/// -/// Defines the contract for managing resource-related operations with a specific integer key type. -/// -public interface IResourceManager : IResourceManager; - -/// -/// Defines the contract for managing resource-related operations with a specific key type. -/// -/// The type of the key. -public interface IResourceManager : IResourceManager, Role, Resource> - where TKey : IEquatable; - -/// -/// Defines the contract for managing resource-related operations with specific user and resource types. -/// -/// The type of the key, which must implement . -/// The type representing a user, which must inherit from . -/// The type representing a role, which must inherit from . -/// The type representing a resource, which must inherit from . -public interface IResourceManager - where TKey : IEquatable - where TUser : User - where TRole : Role - where TResource : Resource -{ - /// - /// Determines whether the specified role is permitted to access the resource identified by its name. - /// - /// The role to check permissions for. - /// The name of the resource to check. - /// - /// if the role is permitted to access the resource; otherwise, . - /// - /// Thrown when the specified resource name does not exist. - public bool IsPermitted(TRole role, string resourceName); - - /// - /// Determines whether the specified role is permitted to access the given resource. - /// - /// The role to check permissions for. - /// The resource to check. - /// - /// if the role is permitted to access the resource; otherwise, . - /// - public bool IsPermitted(TRole role, TResource resource); - - /// - /// Determines whether the specified user is permitted to access the resource identified by its name. - /// - /// The user to check permissions for. - /// The name of the resource to check. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - /// Thrown when the specified resource name does not exist. - public bool IsPermitted(TUser user, string resourceName); - - /// - /// Determines whether the specified user is permitted to access the given resource. - /// - /// The user to check permissions for. - /// The resource to check. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - public bool IsPermitted(TUser user, TResource resource); - - /// - /// Determines which resources from a collection of resource names the specified user is permitted to access. - /// - /// The user to check permissions for. - /// The collection of resource names to check. - /// - /// An enumerable of that the user is permitted to access. - /// - /// Thrown when the specified resource name does not exist. - public IEnumerable IsPermitted(TUser user, IEnumerable resourceNames); - - /// - /// Determines which resources from a collection the specified user is permitted to access. - /// - /// The user to check permissions for. - /// The collection of resources to check. - /// - /// An enumerable of that the user is permitted to access. - /// - public IEnumerable IsPermitted(TUser user, IEnumerable resources); - - /// - /// Retrieves a resource by its name. - /// - /// The name of the resource to retrieve. - /// - /// The with the specified name. - /// - /// Thrown when the specified resource name does not exist. - public TResource GetResourceByName(string resourceName); -} diff --git a/src/Acl.Net.Core.Managers/IUserManager.cs b/src/Acl.Net.Core.Managers/IUserManager.cs deleted file mode 100644 index f122353..0000000 --- a/src/Acl.Net.Core.Managers/IUserManager.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Acl.Net.Core.Database.Entities; - -namespace Acl.Net.Core.Managers; - -/// -/// Defines the contract for managing user-related operations using integer keys. -/// -public interface IUserManager : IUserManager; - -/// -/// Defines the contract for managing user-related operations with a specific key type. -/// -/// -/// The type of the key, which must implement . -/// -public interface IUserManager : IUserManager, Role> - where TKey : IEquatable; - -/// -/// Defines the contract for managing user-related operations with specific user and role types. -/// -/// The type of the key, which must implement . -/// The type representing a user, which must inherit from . -/// The type representing a role, which must inherit from . -public interface IUserManager - where TKey : IEquatable - where TUser : User - where TRole : Role -{ - public IEnumerable GetUserRoles(string userName); - - public IEnumerable GetUserRoles(TUser user); - - /// - /// Retrieves an existing user by name or creates a new one with the specified role if it doesn't exist. - /// - /// The name of the user to retrieve or create. - /// The role to assign to the user if a new user is created. - /// The existing or newly created instance. - public TUser UserProcessing(string userName, TRole roleForNewUsers); - - /// - /// Asynchronously retrieves an existing user by name or creates a new one with the specified role if it doesn't exist. - /// - /// The name of the user to retrieve or create. - /// The role to assign to the user if a new user is created. - /// A task that represents the asynchronous operation. The task result contains the existing or newly created instance. - public Task UserProcessingAsync(string userName, TRole roleForNewUsers); -} \ No newline at end of file diff --git a/src/Acl.Net.Core.Managers/ResourceManager.cs b/src/Acl.Net.Core.Managers/ResourceManager.cs deleted file mode 100644 index 66a7044..0000000 --- a/src/Acl.Net.Core.Managers/ResourceManager.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Acl.Net.Core.Database; -using Microsoft.EntityFrameworkCore; -using Acl.Net.Core.Database.Entities; -using Acl.Net.Core.Managers.Exceptions; - -namespace Acl.Net.Core.Managers; - -/// -/// Manages resource-related operations with a specific integer key type. -/// -public class ResourceManager : ResourceManager, IResourceManager -{ - /// - /// Initializes a new instance of the class with the specified context. - /// - /// The database context. - public ResourceManager(AclDbContext context) - : base(context, new RoleDataSeeder(), new UserManager(context)) - { } -} - -/// -/// Manages resource-related operations with a specific key type. -/// -/// The type of the key. -public class ResourceManager : ResourceManager, Role, Resource>, IResourceManager - where TKey : IEquatable -{ - /// - /// Initializes a new instance of the class with the specified context and initial data seeder. - /// - /// The database context. - /// The initial data seeder. - /// An implementation of . - public ResourceManager( - AclDbContext context, - IInitialDataSeeder> initialDataSeeder, - IUserManager, Role> userManager - ) - : base(context, initialDataSeeder, userManager) - { } -} - -/// -/// Manages resource-related operations with specific user, role, and resource types. -/// -/// The type of the key, which must implement . -/// The type of the user, which must inherit from . -/// The type of the role, which must inherit from . -/// The type of the resource, which must inherit from . -public class ResourceManager : IResourceManager - where TKey : IEquatable - where TUser : User, new() - where TRole : Role - where TResource : Resource -{ - protected readonly AclDbContext Context; - protected readonly IInitialDataSeeder InitialDataSeeder; - protected readonly IUserManager UserManager; - - /// - /// Initializes a new instance of the class. - /// - /// The database context. - /// The initial data seeder for roles. - /// An implementation of . - public ResourceManager( - AclDbContext context, - IInitialDataSeeder initialDataSeeder, - IUserManager userManager - ) - { - Context = context; - InitialDataSeeder = initialDataSeeder; - UserManager = userManager; - } - - /// - public bool IsPermitted(TRole role, string resourceName) - { - var resource = GetResourceByName(resourceName); - return IsPermitted(role, resource); - } - - /// - public bool IsPermitted(TRole role, TResource resource) - { - return role.Id.Equals(InitialDataSeeder.SeedAdminRole().Id) || - Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); - } - - /// - public virtual bool IsPermitted(TUser user, string resourceName) - { - var resource = GetResourceByName(resourceName); - return IsPermitted(user, resource); - } - - /// - public virtual bool IsPermitted(TUser user, TResource resource) - { - return UserManager.GetUserRoles(user).Any(u => IsPermitted(u, resource)); - } - - /// - public virtual IEnumerable IsPermitted(TUser user, IEnumerable resourceNames) - { - return IsPermitted(user, resourceNames.Select(GetResourceByName)); - } - - /// - public virtual IEnumerable IsPermitted(TUser user, IEnumerable resources) - { - return resources.Where(resource => IsPermitted(user, resource)); - } - - /// - public virtual TResource GetResourceByName(string resourceName) - { - return Context.Resources.FirstOrDefault(r => r.Name == resourceName) - ?? throw new ResourceNotFoundException(resourceName); - } -} diff --git a/src/Acl.Net.Core.Managers/UserManager.cs b/src/Acl.Net.Core.Managers/UserManager.cs deleted file mode 100644 index e1e9e51..0000000 --- a/src/Acl.Net.Core.Managers/UserManager.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Acl.Net.Core.Database; -using Microsoft.EntityFrameworkCore; -using Acl.Net.Core.Database.Entities; - -namespace Acl.Net.Core.Managers; - -/// -/// Manages user-related operations using integer keys. -/// -public class UserManager : UserManager, IUserManager -{ - /// - /// Initializes a new instance of the class. - /// - /// The ACL database context. - public UserManager(AclDbContext context) - : base(context) - { } -} - -/// -/// Manages user-related operations with a specific key type. -/// -/// The type of the key, which must implement . -public class UserManager : UserManager, Role, Resource>, IUserManager - where TKey : IEquatable -{ - /// - /// Initializes a new instance of the class. - /// - /// The ACL database context. - public UserManager(AclDbContext context) - : base(context) - { } -} - -/// -/// Manages user-related operations with specific user, role, and resource types. -/// -/// The type of the key, which must implement . -/// The type of the user, which must inherit from . -/// The type of the role, which must inherit from . -/// The type of the resource, which must inherit from . -public class UserManager : IUserManager - where TKey : IEquatable - where TUser : User, new() - where TRole : Role - where TResource : Resource -{ - protected readonly AclDbContext Context; - - /// - /// Initializes a new instance of the class. - /// - /// The ACL database context. - public UserManager(AclDbContext context) - { - Context = context; - } - - public IEnumerable GetUserRoles(TUser user) => GetUserRoles(user.Name); - - public IEnumerable GetUserRoles(string userName) - { - return Context.Roles - .Where(r => Context.Users - .Where(u => u.Name == userName) - .Select(u => u.RoleId) - .Contains(r.Id)) - .ToArray(); - } - - /// - public virtual TUser UserProcessing(string userName, TRole roleForNewUsers) - { - var user = Context.Users.FirstOrDefault(x => x.Name == userName) - ?? new TUser { Name = userName, RoleId = roleForNewUsers.Id }; - - if (!user.Id.Equals(default)) return user; - Context.Users.Add(user); - Context.SaveChanges(); - - return user; - } - - /// - public virtual async Task UserProcessingAsync(string userName, TRole roleForNewUsers) - { - var user = await Context.Users.FirstOrDefaultAsync(x => x.Name == userName) - ?? new TUser { Name = userName, RoleId = roleForNewUsers.Id }; - - if (!user.Id.Equals(default)) return user; - await Context.Users.AddAsync(user); - await Context.SaveChangesAsync(); - - return user; - } -} From 144d028dde78a0c2277732337b5ff0c6427300a3 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 17:21:41 +0300 Subject: [PATCH 06/10] - simplify `IInitialDataSeeder` --- src/Acl.Net.Core.Database/AclDbContext.cs | 2 +- src/Acl.Net.Core.Database/IInitialDataSeeder.cs | 8 +------- src/Acl.Net.Core.Database/RoleDataSeeder.cs | 6 ------ src/Acl.Net.Core.Managers/AclManager.cs | 11 ++++++++--- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/Acl.Net.Core.Database/AclDbContext.cs b/src/Acl.Net.Core.Database/AclDbContext.cs index 573b2ac..11f9920 100644 --- a/src/Acl.Net.Core.Database/AclDbContext.cs +++ b/src/Acl.Net.Core.Database/AclDbContext.cs @@ -123,7 +123,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasMany().WithOne().HasForeignKey(res => res.RoleId).IsRequired(); - entity.HasData(_seeder.SeedAdminRole(), _seeder.SeedUserRole()); + entity.HasData(_seeder.SeedAdminRole()); }); modelBuilder.Entity(entity => diff --git a/src/Acl.Net.Core.Database/IInitialDataSeeder.cs b/src/Acl.Net.Core.Database/IInitialDataSeeder.cs index 5ec06d9..aac6909 100644 --- a/src/Acl.Net.Core.Database/IInitialDataSeeder.cs +++ b/src/Acl.Net.Core.Database/IInitialDataSeeder.cs @@ -3,7 +3,7 @@ namespace Acl.Net.Core.Database; /// -/// Provides methods for seeding the initial administrative and user roles within the system. +/// Provides methods for seeding the initial roles within the system. /// /// The type of the key used to identify the role. /// The type of the role being seeded. @@ -16,10 +16,4 @@ public interface IInitialDataSeeder /// /// A representing the administrative role. public TRole SeedAdminRole(); - - /// - /// Seeds the user role within the system. - /// - /// A representing the user role. - public TRole SeedUserRole(); } diff --git a/src/Acl.Net.Core.Database/RoleDataSeeder.cs b/src/Acl.Net.Core.Database/RoleDataSeeder.cs index 79c91a2..58fd44a 100644 --- a/src/Acl.Net.Core.Database/RoleDataSeeder.cs +++ b/src/Acl.Net.Core.Database/RoleDataSeeder.cs @@ -10,10 +10,4 @@ public Role SeedAdminRole() { return new Role { Id = 1, Name = "AdminRole" }; } - - /// - public Role SeedUserRole() - { - return new Role { Id = 2, Name = "UserRole" }; - } } diff --git a/src/Acl.Net.Core.Managers/AclManager.cs b/src/Acl.Net.Core.Managers/AclManager.cs index 2860f62..13e4fc0 100644 --- a/src/Acl.Net.Core.Managers/AclManager.cs +++ b/src/Acl.Net.Core.Managers/AclManager.cs @@ -88,10 +88,10 @@ AclDbContext context Context = context; } - /// - public bool IsPermitted(TRole role, TResource resource) + public bool IsPermitted(string userName, string resourceName) { - return IsAdmin(role) || Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); + var user = GetUserByName(userName); + return IsAdmin(user) || GetUserRoles(user.Name).Any(role => IsPermitted(role, GetResourceByName(resourceName))); } public bool IsPermitted(TUser user, TResource resource) @@ -99,6 +99,11 @@ public bool IsPermitted(TUser user, TResource resource) return IsAdmin(user) || GetUserRoles(user.Name).Any(role => IsPermitted(role, resource)); } + public bool IsPermitted(TRole role, TResource resource) + { + return IsAdmin(role) || Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); + } + public bool IsAdmin(TKey roleId) => roleId.Equals(InitialDataSeeder.SeedAdminRole().Id); public bool IsAdmin(TUser user) => IsAdmin(user.RoleId); public bool IsAdmin(TRole role) => IsAdmin(role.Id); From 25a965316a77828b647a09c15a02dab7e67368f4 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Thu, 21 Nov 2024 17:23:16 +0300 Subject: [PATCH 07/10] - simplify role name --- src/Acl.Net.Core.Database/RoleDataSeeder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Acl.Net.Core.Database/RoleDataSeeder.cs b/src/Acl.Net.Core.Database/RoleDataSeeder.cs index 58fd44a..31b964b 100644 --- a/src/Acl.Net.Core.Database/RoleDataSeeder.cs +++ b/src/Acl.Net.Core.Database/RoleDataSeeder.cs @@ -8,6 +8,6 @@ public class RoleDataSeeder : IInitialDataSeeder> /// public Role SeedAdminRole() { - return new Role { Id = 1, Name = "AdminRole" }; + return new Role { Id = 1, Name = "Admin" }; } } From 12dcb8bb76a91ff466781b8b7bb45a0577dddd7e Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Fri, 22 Nov 2024 12:56:16 +0300 Subject: [PATCH 08/10] - downgrade version of EFCore at DB package - update interface IAclmanager - update `IsPermitted(TRole role, TResource resource)`: check by resource name, not by Id --- .../Acl.Net.Core.Database.csproj | 2 +- src/Acl.Net.Core.Database/RoleDataSeeder.cs | 2 +- src/Acl.Net.Core.Managers/AclManager.cs | 15 +++-- src/Acl.Net.Core.Managers/IAclManager.cs | 56 +++++-------------- 4 files changed, 26 insertions(+), 49 deletions(-) diff --git a/src/Acl.Net.Core.Database/Acl.Net.Core.Database.csproj b/src/Acl.Net.Core.Database/Acl.Net.Core.Database.csproj index 8c17732..64f15ba 100644 --- a/src/Acl.Net.Core.Database/Acl.Net.Core.Database.csproj +++ b/src/Acl.Net.Core.Database/Acl.Net.Core.Database.csproj @@ -30,7 +30,7 @@ - + diff --git a/src/Acl.Net.Core.Database/RoleDataSeeder.cs b/src/Acl.Net.Core.Database/RoleDataSeeder.cs index 31b964b..865af86 100644 --- a/src/Acl.Net.Core.Database/RoleDataSeeder.cs +++ b/src/Acl.Net.Core.Database/RoleDataSeeder.cs @@ -10,4 +10,4 @@ public Role SeedAdminRole() { return new Role { Id = 1, Name = "Admin" }; } -} +} \ No newline at end of file diff --git a/src/Acl.Net.Core.Managers/AclManager.cs b/src/Acl.Net.Core.Managers/AclManager.cs index 13e4fc0..ca093d0 100644 --- a/src/Acl.Net.Core.Managers/AclManager.cs +++ b/src/Acl.Net.Core.Managers/AclManager.cs @@ -5,8 +5,8 @@ namespace Acl.Net.Core.Managers; /// -/// Manages access control lists (ACLs) using integer keys. -/// This class provides a simplified interface for managing ACLs with integer keys, by extending the more generic with TKey. +/// Manages access control lists (ACLs) using integer keys.
+/// This class provides a simplified interface for managing ACLs with integer keys, by extending the more generic . ///
public class AclManager : AclManager, IAclManager { @@ -35,7 +35,7 @@ AclDbContext context } /// -/// Manages access control lists (ACLs) using keys of type . +/// Manages access control lists (ACLs) using keys of type .
/// This class provides the base functionality for managing ACLs with specified key types. ///
/// The type of the key, which must implement . @@ -57,14 +57,14 @@ AclDbContext context } /// -/// Manages access control lists (ACLs) using keys, users, roles, and resources of specified types. +/// Manages access control lists (ACLs) using keys, users, roles, and resources of specified types.
/// This class provides the complete functionality for managing ACLs with specified key, user, role, and resource types. ///
/// The type of the key, which must implement . /// The type of the user, which must be a derived type of . /// The type of the role, which must be a derived type of /// The type of the resource, which must be a derived type of -public class AclManager : IAclManager +public class AclManager : IAclManager where TKey : IEquatable where TUser : User, new() where TRole : Role @@ -88,20 +88,23 @@ AclDbContext context Context = context; } + /// public bool IsPermitted(string userName, string resourceName) { var user = GetUserByName(userName); return IsAdmin(user) || GetUserRoles(user.Name).Any(role => IsPermitted(role, GetResourceByName(resourceName))); } + /// public bool IsPermitted(TUser user, TResource resource) { return IsAdmin(user) || GetUserRoles(user.Name).Any(role => IsPermitted(role, resource)); } + /// public bool IsPermitted(TRole role, TResource resource) { - return IsAdmin(role) || Context.Resources.Any(r => r.RoleId.Equals(role.Id) && r.Id.Equals(resource.Id)); + return IsAdmin(role) || Context.Resources.Any(res => res.Name.Equals(resource.Name) && res.RoleId.Equals(role.Id)); } public bool IsAdmin(TKey roleId) => roleId.Equals(InitialDataSeeder.SeedAdminRole().Id); diff --git a/src/Acl.Net.Core.Managers/IAclManager.cs b/src/Acl.Net.Core.Managers/IAclManager.cs index edf030a..59533b5 100644 --- a/src/Acl.Net.Core.Managers/IAclManager.cs +++ b/src/Acl.Net.Core.Managers/IAclManager.cs @@ -12,7 +12,7 @@ public interface IAclManager : IAclManager; /// Defines the contract for Access Control List (ACL) management with support for a specific key type. ///
/// The type of key used to identify users and resources. -public interface IAclManager : IAclManager, Resource> +public interface IAclManager : IAclManager, Role, Resource> where TKey : IEquatable; /// @@ -20,10 +20,12 @@ public interface IAclManager : IAclManager, Resource /// The type of key, which must implement . /// The type representing a user, which must inherit from . +/// The type representing a role, which must inherit from . /// The type representing a resource, which must inherit from . -public interface IAclManager +public interface IAclManager where TKey : IEquatable where TUser : User + where TRole: Role where TResource : Resource { /// @@ -31,52 +33,24 @@ public interface IAclManager /// /// The name of the user to check permission for. /// The name of the resource to check permission against. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - /// Thrown when the specified resource name does not exist. - //public bool IsPermitted(string userName, string resourceName); - - /// - /// Determines if the specified user object is permitted to access the specified resource by name. - /// - /// The user object to check permission for. - /// The name of the resource to check permission against. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - /// Thrown when the specified resource name does not exist. - //public bool IsPermitted(TUser user, string resourceName); - - /// - /// Determines if the specified user by name is permitted to access the specified resource object. - /// - /// The name of the user to check permission for. - /// The resource object to check permission against. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - //public bool IsPermitted(string userName, TResource resource); + /// if the user is permitted to access the resource; otherwise, . + /// Thrown when the specified resource by name does not exist. + /// Thrown when the specified user by name does not exist. + public bool IsPermitted(string userName, string resourceName); /// /// Determines if the specified user object is permitted to access the specified resource object. /// /// The user object to check permission for. /// The resource object to check permission against. - /// - /// if the user is permitted to access the resource; otherwise, . - /// - //public bool IsPermitted(TUser user, TResource resource); + /// if the user is permitted to access the resource; otherwise, . + public bool IsPermitted(TUser user, TResource resource); /// - /// Determines the resources that the specified user by name is permitted to access from a collection of resource names. + /// Determines whether the specified role is permitted to access the given resource. /// - /// The name of the user to check permission for. - /// The collection of resource names to check permissions against. - /// - /// A collection of objects that the user is permitted to access; - /// an empty collection if the user is not permitted to access any of the resources. - /// - /// Thrown when one or more of the specified resource names do not exist. - //public IEnumerable IsPermitted(string userName, IEnumerable resourceNames); + /// The role to check permissions for. + /// The resource to check. + /// if the role is permitted to access the resource; otherwise, . + public bool IsPermitted(TRole role, TResource resource); } \ No newline at end of file From b5d503ec2142e51a79962297d6ac49feec7bc35e Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Fri, 22 Nov 2024 14:26:06 +0300 Subject: [PATCH 09/10] update tests --- .../Acl.Net.Core.Managers.Tests.csproj | 1 + .../DataProvider/AclDbContextTests.cs | 15 -- .../Managers/AclManagerTests.cs | 132 ++++++------------ .../Managers/ResourceManagerTests.cs | 79 ----------- .../Managers/UserManagerTests.cs | 45 ------ .../Mock/InMemoryAclDbContext.cs | 6 +- 6 files changed, 51 insertions(+), 227 deletions(-) delete mode 100644 tests/Acl.Net.Core.Managers.Tests/DataProvider/AclDbContextTests.cs delete mode 100644 tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs delete mode 100644 tests/Acl.Net.Core.Managers.Tests/Managers/UserManagerTests.cs diff --git a/tests/Acl.Net.Core.Managers.Tests/Acl.Net.Core.Managers.Tests.csproj b/tests/Acl.Net.Core.Managers.Tests/Acl.Net.Core.Managers.Tests.csproj index 5a30146..b031d42 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Acl.Net.Core.Managers.Tests.csproj +++ b/tests/Acl.Net.Core.Managers.Tests/Acl.Net.Core.Managers.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/tests/Acl.Net.Core.Managers.Tests/DataProvider/AclDbContextTests.cs b/tests/Acl.Net.Core.Managers.Tests/DataProvider/AclDbContextTests.cs deleted file mode 100644 index e72e7ed..0000000 --- a/tests/Acl.Net.Core.Managers.Tests/DataProvider/AclDbContextTests.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Xunit; -using Acl.Net.Core.Database; - -namespace Acl.Net.Core.Managers.Tests.DataProvider; - -public class AclDbContextTests -{ - [Fact] - public void FirstConstructorTest() - { - var context = new AclDbContext(); - - Assert.NotNull(context); - } -} \ No newline at end of file diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs index 6f0a986..24c3039 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs @@ -1,4 +1,5 @@ using Xunit; +using FluentAssertions; using Acl.Net.Core.Database; using Acl.Net.Core.Managers.Tests.Mock; @@ -6,92 +7,49 @@ namespace Acl.Net.Core.Managers.Tests.Managers; public class AclManagerTests { - private readonly AclManager _aclManager; - - public AclManagerTests() - { - var context = InMemoryAclDbContext.CreateContext(); - _aclManager = new AclManager(context); - } - - [Fact] - public void Ctor_WithContextParameter() - { - var context = InMemoryAclDbContext.CreateContext(); - var aclManager = new AclManager(context); - - Assert.NotNull(aclManager); - } - - [Fact] - public void Ctor_WithManagersParameters() - { - var context = InMemoryAclDbContext.CreateContext(); - var aclManager = new AclManager(new UserManager(context), new ResourceManager(context)); - - Assert.NotNull(aclManager); - } - - [Fact] - public void Ctor_WithManagersAndInitialDataSeederParameters() - { - var context = InMemoryAclDbContext.CreateContext(); - var aclManager = new AclManager(new RoleDataSeeder(), new UserManager(context), new ResourceManager(context)); - - Assert.NotNull(aclManager); - } - - [Fact] - public void IsPermitted_ShouldReturnTrue_WhenAdminAccessPrivateResource() - { - Assert.True(_aclManager.IsPermitted("AdminAccount", "PrivateResource")); - } - - [Fact] - public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResource() - { - Assert.False(_aclManager.IsPermitted("UserAccount", "PrivateResource")); - } - - [Fact] - public void IsPermitted_ShouldReturnPermittedResources_WhenGivenResourceNames() - { - Assert.Single(_aclManager.IsPermitted("AdminAccount", new[] { "PrivateResource" })); - } - - [Fact] - public void IsPermitted_ShouldReturnTrue_WhenAdminAccessPrivateResourceByUserObject() - { - var adminUser = InMemoryAclDbContext.AdminAccount; - Assert.True(_aclManager.IsPermitted(adminUser, "PrivateResource")); - } - - [Fact] - public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResourceByUserObject() - { - var user = InMemoryAclDbContext.UserAccount; - Assert.False(_aclManager.IsPermitted(user, "PrivateResource")); - } - - [Fact] - public void IsPermitted_ShouldReturnTrue_WhenAdminAccessPrivateResourceByResourceObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - Assert.True(_aclManager.IsPermitted("AdminAccount", privateResource)); - } - - [Fact] - public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResourceByResourceObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - Assert.False(_aclManager.IsPermitted("UserAccount", privateResource)); - } - - [Fact] - public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResourceByResourceObjectAndUserObject() - { - var privateResource = InMemoryAclDbContext.PrivateResource; - var user = InMemoryAclDbContext.UserAccount; - Assert.False(_aclManager.IsPermitted(user, privateResource)); + public class Constructors + { + [Fact] + public void WithContextParameter() + { + var context = InMemoryAclDbContext.CreateContext(); + var aclManager = new AclManager(context); + + Assert.NotNull(aclManager); + } + + [Fact] + public void CtorWithManagersAndInitialDataSeederParameters() + { + var context = InMemoryAclDbContext.CreateContext(); + var aclManager = new AclManager(new RoleDataSeeder(), context); + + Assert.NotNull(aclManager); + } + } + + public class IsPermitted + { + private readonly IAclManager _aclManager = new AclManager(InMemoryAclDbContext.CreateContext()); + + [Theory] + [InlineData("AdminAccount", true, false, "NotExistResource")] + [InlineData("AdminAccount", true, false, "PrivateResource")] + [InlineData("AdminAccount", true, false, "PublicResource")] + [InlineData("UserAccount", false, true, "NotExistResource")] + [InlineData("UserAccount", false, false, "PrivateResource")] + [InlineData("UserAccount", true, false, "PublicResource")] + [InlineData("NotExistAccount", false, true, "PublicResource")] + public void Test(string userName, bool expected, bool withError, string resourceName) + { + try + { + _aclManager.IsPermitted(userName, resourceName).Should().Be(expected); + } + catch (Exception) + { + withError.Should().BeTrue(); + } + } } } diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs deleted file mode 100644 index 963408a..0000000 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/ResourceManagerTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Xunit; -using Acl.Net.Core.Database.Entities; -using Acl.Net.Core.Managers.Exceptions; -using Acl.Net.Core.Managers.Tests.Mock; - -namespace Acl.Net.Core.Managers.Tests.Managers; - -public class ResourceManagerTests -{ - private readonly ResourceManager _resourceManager; - private readonly User _adminUser; - private readonly User _normalUser; - private readonly Resource _privateResource; - - public ResourceManagerTests() - { - _resourceManager = new ResourceManager(InMemoryAclDbContext.CreateContext()); - - _adminUser = new User { Id = 2, Name = "AdminAccount", RoleId = 1 }; - _normalUser = new User { Id = 1, Name = "UserAccount", RoleId = 2 }; - _privateResource = new Resource { Id = 2, Name = "PrivateResource", RoleId = 1 }; - } - - [Fact] - public void IsPermitted_ShouldReturnTrue_WhenAdminAccessPrivateResource() - { - Assert.True(_resourceManager.IsPermitted(_adminUser, _privateResource)); - } - - [Fact] - public void IsPermitted_ShouldReturnFalse_WhenUserAccessPrivateResource() - { - Assert.False(_resourceManager.IsPermitted(_normalUser, _privateResource)); - } - - [Fact] - public void IsPermitted_ShouldReturnPermittedResources_WhenGivenResourceNames() - { - var resourceNames = new[] { "PrivateResource", "PublicResource" }; - var resources = _resourceManager.IsPermitted(_adminUser, resourceNames).ToArray(); - - Assert.Equal(2, resources.Length); - } - - [Fact] - public void IsPermitted_ShouldReturnEmpty_WhenGivenResourceNotAllowed() - { - var resourceNames = new[] { "PrivateResource" }; - var resources = _resourceManager.IsPermitted(_normalUser, resourceNames).ToArray(); - - Assert.Empty(resources); - } - - [Fact] - public void IsPermitted_ShouldReturnPermittedResources_WhenGivenResources() - { - var resourcesList = new[] { _privateResource }; - var resources = _resourceManager.IsPermitted(_adminUser, resourcesList).ToArray(); - - Assert.Single(resources); - Assert.Equal(_privateResource, resources[0]); - } - - [Fact] - public void GetResourceByName_ShouldReturnResource_WhenResourceExists() - { - const string expectedResourceName = "PrivateResource"; - var resource = _resourceManager.GetResourceByName(expectedResourceName); - Assert.NotNull(resource); - Assert.Equal(expectedResourceName, resource.Name); - } - - [Fact] - public void GetResourceByName_ShouldThrowResourceNotFoundException_WhenResourceDoesNotExist() - { - const string nonExistentResourceName = "NonExistentResource"; - Assert.Throws(() => _resourceManager.GetResourceByName(nonExistentResourceName)); - } -} \ No newline at end of file diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/UserManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/UserManagerTests.cs deleted file mode 100644 index bd30982..0000000 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/UserManagerTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Xunit; -using Acl.Net.Core.Database.Entities; -using Acl.Net.Core.Managers.Tests.Mock; - -namespace Acl.Net.Core.Managers.Tests.Managers; - -public class UserManagerTests -{ - private readonly UserManager _userManager; - private readonly Role _roleForNewUsers; - - public UserManagerTests() - { - _userManager = new UserManager(InMemoryAclDbContext.CreateContext()); - _roleForNewUsers = new Role { Id = 3, Name = "NewUserRole" }; - } - - [Fact] - public void UserProcessing_ShouldReturnExistingUser_WhenUserExists() - { - var user = _userManager.UserProcessing("UserAccount", _roleForNewUsers); - Assert.Equal(1, user.Id); - } - - [Fact] - public void UserProcessing_ShouldAddNewUser_WhenUserNotExists() - { - var user = _userManager.UserProcessing("NewUser", _roleForNewUsers); - Assert.Equal(_roleForNewUsers.Id, user.RoleId); - } - - [Fact] - public async Task UserProcessingAsync_ShouldReturnExistingUser_WhenUserExists() - { - var user = await _userManager.UserProcessingAsync("UserAccount", _roleForNewUsers); - Assert.Equal(1, user.Id); - } - - [Fact] - public async Task UserProcessingAsync_ShouldAddNewUser_WhenUserNotExists() - { - var user = await _userManager.UserProcessingAsync("NewUser", _roleForNewUsers); - Assert.Equal(_roleForNewUsers.Id, user.RoleId); - } -} diff --git a/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs b/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs index e41362b..cb0f3a2 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Mock/InMemoryAclDbContext.cs @@ -7,8 +7,12 @@ namespace Acl.Net.Core.Managers.Tests.Mock; internal static class InMemoryAclDbContext { private static readonly RoleDataSeeder RoleDataSeeder = new(); - private static readonly Role UserRole = RoleDataSeeder.SeedUserRole(); private static readonly Role AdminRole = RoleDataSeeder.SeedAdminRole(); + private static readonly Role UserRole = new() + { + Id = 2, + Name = "User" + }; internal static User UserAccount = new() { From 81303bb163ce2996ea7640fc2f129829ce5a5888 Mon Sep 17 00:00:00 2001 From: Stanislav Vysotskyi Date: Fri, 22 Nov 2024 14:59:06 +0300 Subject: [PATCH 10/10] - solve --- src/Acl.Net.Core.Database/AclDbContext.cs | 2 +- tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Acl.Net.Core.Database/AclDbContext.cs b/src/Acl.Net.Core.Database/AclDbContext.cs index 11f9920..9f0167f 100644 --- a/src/Acl.Net.Core.Database/AclDbContext.cs +++ b/src/Acl.Net.Core.Database/AclDbContext.cs @@ -123,7 +123,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasMany().WithOne().HasForeignKey(res => res.RoleId).IsRequired(); - entity.HasData(_seeder.SeedAdminRole()); + entity.HasData([_seeder.SeedAdminRole()]); }); modelBuilder.Entity(entity => diff --git a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs index 24c3039..4d305f2 100644 --- a/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs +++ b/tests/Acl.Net.Core.Managers.Tests/Managers/AclManagerTests.cs @@ -30,7 +30,7 @@ public void CtorWithManagersAndInitialDataSeederParameters() public class IsPermitted { - private readonly IAclManager _aclManager = new AclManager(InMemoryAclDbContext.CreateContext()); + private readonly AclManager _aclManager = new(InMemoryAclDbContext.CreateContext()); [Theory] [InlineData("AdminAccount", true, false, "NotExistResource")]