From 4913cbc15847d96806884b87dc3e9c28a87bfb31 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 8 Feb 2019 15:46:10 +0300 Subject: [PATCH] Resolved #728: Allow to set client based permissions like roles/users --- .../MethodInvocationAuthorizationService.cs | 11 ++++- .../ClientPermissionValueProvider.cs | 35 +++++++++++++++ .../Principal/AbpClaimsIdentityExtensions.cs | 44 +++++++++++++++---- .../Volo/Abp/Clients/CurrentClient.cs | 20 +++++++++ .../Volo/Abp/Clients/ICurrentClient.cs | 9 ++++ .../Volo/Abp/Security/Claims/AbpClaimTypes.cs | 5 +++ .../RolePermissionManagementProvider.cs | 2 +- .../UserPermissionManagementProvider.cs | 4 +- .../Volo.Abp.IdentityServer.sln | 9 +++- ...ionManagement.Domain.IdentityServer.csproj | 21 +++++++++ ...ionManagementDomainIdentityServerModule.cs | 18 ++++++++ .../ClientPermissionManagementProvider.cs | 23 ++++++++++ nupkg/common.ps1 | 1 + .../BackendAdminAppGateway.Host.csproj | 1 + .../BackendAdminAppGatewayHostModule.cs | 4 +- 15 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Clients/CurrentClient.cs create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Clients/ICurrentClient.cs create mode 100644 modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo.Abp.PermissionManagement.Domain.IdentityServer.csproj create mode 100644 modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/AbpPermissionManagementDomainIdentityServerModule.cs create mode 100644 modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/ClientPermissionManagementProvider.cs diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 3e5bb45309..1cca959ebe 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Clients; using Volo.Abp.DependencyInjection; using Volo.Abp.Users; @@ -10,11 +11,16 @@ namespace Volo.Abp.Authorization { private readonly IAuthorizationService _authorizationService; private readonly ICurrentUser _currentUser; + private readonly ICurrentClient _currentClient; - public MethodInvocationAuthorizationService(IAuthorizationService authorizationService, ICurrentUser currentUser) + public MethodInvocationAuthorizationService( + IAuthorizationService authorizationService, + ICurrentUser currentUser, + ICurrentClient currentClient) { _authorizationService = authorizationService; _currentUser = currentUser; + _currentClient = currentClient; } public async Task CheckAsync(MethodInvocationAuthorizationContext context) @@ -53,7 +59,8 @@ namespace Volo.Abp.Authorization { if (authorizationAttribute.Policy == null) { - if (!_currentUser.IsAuthenticated) //TODO: What about API calls without user id? + //TODO: Can we find a better, unified, way of checking if current request has been authenticated + if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated) { throw new AbpAuthorizationException("Authorization failed! User has not logged in."); } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs new file mode 100644 index 0000000000..b9eac0bab0 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.Authorization.Permissions +{ + public class ClientPermissionValueProvider : PermissionValueProvider + { + public const string ProviderName = "Client"; + + public override string Name => ProviderName; + + public ClientPermissionValueProvider(IPermissionStore permissionStore) + : base(permissionStore) + { + + } + + public override async Task CheckAsync(PermissionValueCheckContext context) + { + var clientId = context.Principal?.FindFirst(AbpClaimTypes.ClientId)?.Value; + + if (clientId == null) + { + return PermissionValueProviderGrantInfo.NonGranted; + } + + if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, clientId)) + { + return new PermissionValueProviderGrantInfo(true, clientId); + } + + return PermissionValueProviderGrantInfo.NonGranted; + } + } +} diff --git a/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs b/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs index 321aa22914..45d363a7a7 100644 --- a/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs +++ b/framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs @@ -21,6 +21,21 @@ namespace System.Security.Principal return Guid.Parse(userIdOrNull.Value); } + public static Guid? FindUserId([NotNull] this IIdentity identity) + { + Check.NotNull(identity, nameof(identity)); + + var claimsIdentity = identity as ClaimsIdentity; + + var userIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId); + if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace()) + { + return null; + } + + return Guid.Parse(userIdOrNull.Value); + } + public static Guid? FindTenantId([NotNull] this ClaimsPrincipal principal) { Check.NotNull(principal, nameof(principal)); @@ -34,34 +49,47 @@ namespace System.Security.Principal return Guid.Parse(tenantIdOrNull.Value); } - public static Guid? FindUserId([NotNull] this IIdentity identity) + public static Guid? FindTenantId([NotNull] this IIdentity identity) { Check.NotNull(identity, nameof(identity)); var claimsIdentity = identity as ClaimsIdentity; - var userIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId); - if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace()) + var tenantIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId); + if (tenantIdOrNull == null || tenantIdOrNull.Value.IsNullOrWhiteSpace()) { return null; } - return Guid.Parse(userIdOrNull.Value); + return Guid.Parse(tenantIdOrNull.Value); } - public static Guid? FindTenantId([NotNull] this IIdentity identity) + public static string FindClientId([NotNull] this ClaimsPrincipal principal) + { + Check.NotNull(principal, nameof(principal)); + + var clientIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.ClientId); + if (clientIdOrNull == null || clientIdOrNull.Value.IsNullOrWhiteSpace()) + { + return null; + } + + return clientIdOrNull.Value; + } + + public static string FindClientId([NotNull] this IIdentity identity) { Check.NotNull(identity, nameof(identity)); var claimsIdentity = identity as ClaimsIdentity; - var tenantIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId); - if (tenantIdOrNull == null || tenantIdOrNull.Value.IsNullOrWhiteSpace()) + var clientIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.ClientId); + if (clientIdOrNull == null || clientIdOrNull.Value.IsNullOrWhiteSpace()) { return null; } - return Guid.Parse(tenantIdOrNull.Value); + return clientIdOrNull.Value; } } } diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Clients/CurrentClient.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Clients/CurrentClient.cs new file mode 100644 index 0000000000..9e7719faed --- /dev/null +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Clients/CurrentClient.cs @@ -0,0 +1,20 @@ +using System.Security.Principal; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.Clients +{ + public class CurrentClient : ICurrentClient, ITransientDependency + { + public virtual string Id => _principalAccessor.Principal?.FindClientId(); + + public virtual bool IsAuthenticated => Id != null; + + private readonly ICurrentPrincipalAccessor _principalAccessor; + + public CurrentClient(ICurrentPrincipalAccessor principalAccessor) + { + _principalAccessor = principalAccessor; + } + } +} diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Clients/ICurrentClient.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Clients/ICurrentClient.cs new file mode 100644 index 0000000000..a0d0b141c7 --- /dev/null +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Clients/ICurrentClient.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Clients +{ + public interface ICurrentClient + { + string Id { get; } + + bool IsAuthenticated { get; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs index 0edabc957d..b4abf97080 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs @@ -47,5 +47,10 @@ namespace Volo.Abp.Security.Claims /// Default: "phone_number_verified". /// public static string TenantId { get; set; } = "tenantid"; + + /// + /// Default: "client_id". + /// + public static string ClientId { get; set; } = "client_id"; } } diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs index 483bc656a1..09fb389163 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/RolePermissionManagementProvider.cs @@ -36,7 +36,7 @@ namespace Volo.Abp.PermissionManagement.Identity ); } - if (providerName == "User") + if (providerName == UserPermissionValueProvider.ProviderName) { var userId = Guid.Parse(providerKey); var roleNames = await _userRoleFinder.GetRolesAsync(userId); diff --git a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserPermissionManagementProvider.cs b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserPermissionManagementProvider.cs index 43e9644f37..db9a0cbadd 100644 --- a/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserPermissionManagementProvider.cs +++ b/modules/identity/src/Volo.Abp.PermissionManagement.Domain.Identity/Volo/Abp/PermissionManagement/Identity/UserPermissionManagementProvider.cs @@ -8,8 +8,8 @@ namespace Volo.Abp.PermissionManagement.Identity { public override string Name => UserPermissionValueProvider.ProviderName; - public UserPermissionManagementProvider(IPermissionGrantRepository - permissionGrantRepository, + public UserPermissionManagementProvider( + IPermissionGrantRepository permissionGrantRepository, IGuidGenerator guidGenerator, ICurrentTenant currentTenant) : base( diff --git a/modules/identityserver/Volo.Abp.IdentityServer.sln b/modules/identityserver/Volo.Abp.IdentityServer.sln index 81cf037e27..93dd971d47 100644 --- a/modules/identityserver/Volo.Abp.IdentityServer.sln +++ b/modules/identityserver/Volo.Abp.IdentityServer.sln @@ -21,7 +21,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityServer.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityServer.MongoDB.Tests", "test\Volo.Abp.IdentityServer.MongoDB.Tests\Volo.Abp.IdentityServer.MongoDB.Tests.csproj", "{2E18B471-7FCA-497B-90FF-6AA9172CC62F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.IdentityServer.Domain.Tests", "test\Volo.Abp.IdentityServer.Domain.Tests\Volo.Abp.IdentityServer.Domain.Tests.csproj", "{0680D0B6-51C0-4812-8A0B-192FDE717E60}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityServer.Domain.Tests", "test\Volo.Abp.IdentityServer.Domain.Tests\Volo.Abp.IdentityServer.Domain.Tests.csproj", "{0680D0B6-51C0-4812-8A0B-192FDE717E60}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.PermissionManagement.Domain.IdentityServer", "src\Volo.Abp.PermissionManagement.Domain.IdentityServer\Volo.Abp.PermissionManagement.Domain.IdentityServer.csproj", "{072BD630-FB89-45FC-BA2D-12A9745AAB93}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -61,6 +63,10 @@ Global {0680D0B6-51C0-4812-8A0B-192FDE717E60}.Debug|Any CPU.Build.0 = Debug|Any CPU {0680D0B6-51C0-4812-8A0B-192FDE717E60}.Release|Any CPU.ActiveCfg = Release|Any CPU {0680D0B6-51C0-4812-8A0B-192FDE717E60}.Release|Any CPU.Build.0 = Release|Any CPU + {072BD630-FB89-45FC-BA2D-12A9745AAB93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {072BD630-FB89-45FC-BA2D-12A9745AAB93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {072BD630-FB89-45FC-BA2D-12A9745AAB93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {072BD630-FB89-45FC-BA2D-12A9745AAB93}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -74,6 +80,7 @@ Global {9CD1BFDB-DD76-4194-ACAD-A64541AC2069} = {2C792EC1-BA27-44ED-B7CC-D0939553F1B2} {2E18B471-7FCA-497B-90FF-6AA9172CC62F} = {2C792EC1-BA27-44ED-B7CC-D0939553F1B2} {0680D0B6-51C0-4812-8A0B-192FDE717E60} = {2C792EC1-BA27-44ED-B7CC-D0939553F1B2} + {072BD630-FB89-45FC-BA2D-12A9745AAB93} = {59A0FC0F-EA6D-477B-84A7-3B1E41B4C858} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {45562023-C330-4060-A583-2BA10F472D3D} diff --git a/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo.Abp.PermissionManagement.Domain.IdentityServer.csproj b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo.Abp.PermissionManagement.Domain.IdentityServer.csproj new file mode 100644 index 0000000000..a1579f3f31 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo.Abp.PermissionManagement.Domain.IdentityServer.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Volo.Abp.PermissionManagement.Domain.IdentityServer + Volo.Abp.PermissionManagement.Domain.IdentityServer + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/AbpPermissionManagementDomainIdentityServerModule.cs b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/AbpPermissionManagementDomainIdentityServerModule.cs new file mode 100644 index 0000000000..f22cce8345 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/AbpPermissionManagementDomainIdentityServerModule.cs @@ -0,0 +1,18 @@ +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Modularity; + +namespace Volo.Abp.PermissionManagement.IdentityServer +{ + public class AbpPermissionManagementDomainIdentityServerModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.ManagementProviders.Add(); + + options.ProviderPolicies[ClientPermissionValueProvider.ProviderName] = "IdentityServer.Client.ManagePermissions"; + }); + } + } +} diff --git a/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/ClientPermissionManagementProvider.cs b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/ClientPermissionManagementProvider.cs new file mode 100644 index 0000000000..bb4aa54de6 --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer/Volo/Abp/PermissionManagement/IdentityServer/ClientPermissionManagementProvider.cs @@ -0,0 +1,23 @@ +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.PermissionManagement.IdentityServer +{ + public class ClientPermissionManagementProvider : PermissionManagementProvider + { + public override string Name => ClientPermissionValueProvider.ProviderName; + + public ClientPermissionManagementProvider( + IPermissionGrantRepository permissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + permissionGrantRepository, + guidGenerator, + currentTenant) + { + + } + } +} diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 27c0f7ff02..6ac3b2c3d2 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -140,6 +140,7 @@ $projects = ( # modules/identityserver "modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared", "modules/identityserver/src/Volo.Abp.IdentityServer.Domain", + "modules/identityserver/src/Volo.Abp.PermissionManagement.Domain.IdentityServer", "modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore", "modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB", diff --git a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj index a51d66c588..03f9b51d65 100644 --- a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj @@ -27,6 +27,7 @@ + diff --git a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs index c849932e9c..d0549dcfc1 100644 --- a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs +++ b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGatewayHostModule.cs @@ -14,6 +14,7 @@ using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.PermissionManagement.HttpApi; using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.PermissionManagement.IdentityServer; using Volo.Abp.Security.Claims; using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Blogging; @@ -31,7 +32,8 @@ namespace BackendAdminAppGateway.Host typeof(AbpPermissionManagementHttpApiModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(BloggingApplicationContractsModule), - typeof(AbpPermissionManagementDomainIdentityModule) + typeof(AbpPermissionManagementDomainIdentityModule), + typeof(AbpPermissionManagementDomainIdentityServerModule) )] public class BackendAdminAppGatewayHostModule : AbpModule {