Browse Source

reconstitute the IdentityServer module

pull/109/head
cKey 5 years ago
parent
commit
950c125b87
  1. 2
      aspnet-core/modules/account/LINGYUN.Abp.Account.Web/LINGYUN.Abp.Account.Web.csproj
  2. 46
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/AbpIdentityServerPermissionDefinitionProvider.cs
  3. 59
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/AbpIdentityServerPermissions.cs
  4. 3
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/ApiResources/Dto/ApiResourceCreateOrUpdateDto.cs
  5. 5
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/ApiResources/Dto/ApiResourceDto.cs
  6. 5
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/Dto/ClientCloneDto.cs
  7. 11
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/Dto/ClientUpdateDto.cs
  8. 7
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/IClientAppService.cs
  9. 2
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/IdentityResources/Dto/IdentityResourceDto.cs
  10. 25
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Localization/Resources/en.json
  11. 31
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Localization/Resources/zh-Hans.json
  12. 11
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/AbpIdentityServerAppServiceBase.cs
  13. 6
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/AbpIdentityServerAutoMapperProfile.cs
  14. 120
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/ApiResources/ApiResourceAppService.cs
  15. 144
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/Clients/ClientAppService.cs
  16. 32
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/IdentityResources/IdentityResourceAppService.cs
  17. 11
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs
  18. 3
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs
  19. 11
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs
  20. 35
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.EntityFrameworkCore/LINGYUN/Abp/IdentityServer/ApiResources/EfCoreApiResourceRepository.cs
  21. 35
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.EntityFrameworkCore/LINGYUN/Abp/IdentityServer/IdentityResources/EfCoreIdentityResourceRepository.cs
  22. 15
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.HttpApi/LINGYUN/Abp/IdentityServer/AbpIdentityServerHttpApiModule.cs
  23. 21
      aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.HttpApi/LINGYUN/Abp/IdentityServer/Clients/ClientController.cs
  24. BIN
      aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db
  25. 10
      aspnet-core/services/identity-server/LINGYUN.Abp.IdentityServer4.HttpApi.Host/AbpIdentityServerAdminHttpApiHostModule.cs
  26. 2
      vueJs/src/api/api-resources.ts
  27. 74
      vueJs/src/api/clients.ts
  28. 36
      vueJs/src/api/identity-server4.ts
  29. 9
      vueJs/src/api/types.ts
  30. 142
      vueJs/src/lang/en.ts
  31. 142
      vueJs/src/lang/zh.ts
  32. 8
      vueJs/src/router/modules/identityServer.ts
  33. 141
      vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue
  34. 56
      vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceScopeEditForm.vue
  35. 250
      vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceSecretEditForm.vue
  36. 8
      vueJs/src/views/admin/identityServer/api-resources/index.vue
  37. 236
      vueJs/src/views/admin/identityServer/client/components/ClientClaimEditForm.vue
  38. 86
      vueJs/src/views/admin/identityServer/client/components/ClientCloneForm.vue
  39. 210
      vueJs/src/views/admin/identityServer/client/components/ClientCreateForm.vue
  40. 501
      vueJs/src/views/admin/identityServer/client/components/ClientEditForm.vue
  41. 147
      vueJs/src/views/admin/identityServer/client/components/ClientPermissionEditForm.vue
  42. 199
      vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue
  43. 305
      vueJs/src/views/admin/identityServer/client/components/ClientSecretEditForm.vue
  44. 94
      vueJs/src/views/admin/identityServer/client/index.vue
  45. 169
      vueJs/src/views/admin/identityServer/components/PropertiesEditForm.vue
  46. 190
      vueJs/src/views/admin/identityServer/components/ScopeInput.vue
  47. 257
      vueJs/src/views/admin/identityServer/components/SecretEditForm.vue
  48. 62
      vueJs/src/views/admin/identityServer/identity-resources/components/IdentityResourceCreateOrEditForm.vue
  49. 12
      vueJs/src/views/admin/identityServer/identity-resources/index.vue

2
aspnet-core/modules/account/LINGYUN.Abp.Account.Web/LINGYUN.Abp.Account.Web.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\common.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

46
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/AbpIdentityServerPermissionDefinitionProvider.cs

@ -28,57 +28,29 @@ namespace LINGYUN.Abp.IdentityServer
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Create, L("Permissions:Create"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Update, L("Permissions:Update"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Clone, L("Permissions:Clone"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Enabled, L("Permissions:Enabled"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Disabled, L("Permissions:Disabled"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.ManagePermissions, L("Permissions:ManagePermissions"), MultiTenancySides.Host);
// 客户端声明权限
var clientClaimPermissiosn = clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Claims.Default, L("Permissions:Clients:Claims"), MultiTenancySides.Host);
clientClaimPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Claims.Create, L("Permissions:Create"), MultiTenancySides.Host);
clientClaimPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Claims.Update, L("Permissions:Update"), MultiTenancySides.Host);
clientClaimPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Claims.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
// 客户端密钥权限
var clientSecretPermissiosn = clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Secrets.Default, L("Permissions:Clients:Secrets"), MultiTenancySides.Host);
clientSecretPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Secrets.Create, L("Permissions:Create"), MultiTenancySides.Host);
clientSecretPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Secrets.Update, L("Permissions:Update"), MultiTenancySides.Host);
clientSecretPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Secrets.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
// 客户端属性权限
var clientPropertyPermissiosn = clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.Properties.Default, L("Permissions:Clients:Properties"), MultiTenancySides.Host);
clientPropertyPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Properties.Create, L("Permissions:Create"), MultiTenancySides.Host);
clientPropertyPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Properties.Update, L("Permissions:Update"), MultiTenancySides.Host);
clientPropertyPermissiosn.AddChild(AbpIdentityServerPermissions.Clients.Properties.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.ManageClaims, L("Permissions:ManageClaims"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.ManageSecrets, L("Permissions:ManageSecrets"), MultiTenancySides.Host);
clientPermissions.AddChild(AbpIdentityServerPermissions.Clients.ManageProperties, L("Permissions:ManageProperties"), MultiTenancySides.Host);
// Api资源权限
var apiResourcePermissions = identityServerGroup.AddPermission(AbpIdentityServerPermissions.ApiResources.Default, L("Permissions:ApiResources"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Create, L("Permissions:Create"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Update, L("Permissions:Update"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
// Api作用域权限
var apiResourceScopePermissions = apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Scope.Default, L("Permissions:ApiResources:Scope"), MultiTenancySides.Host);
apiResourceScopePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Scope.Create, L("Permissions:Create"), MultiTenancySides.Host);
apiResourceScopePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Scope.Update, L("Permissions:Update"), MultiTenancySides.Host);
apiResourceScopePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Scope.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
// Api密钥权限
var apiResourceSecretPermissions = apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Secrets.Default, L("Permissions:ApiResources:Secrets"), MultiTenancySides.Host);
apiResourceSecretPermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Secrets.Create, L("Permissions:Create"), MultiTenancySides.Host);
apiResourceSecretPermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Secrets.Update, L("Permissions:Update"), MultiTenancySides.Host);
apiResourceSecretPermissions.AddChild(AbpIdentityServerPermissions.ApiResources.Secrets.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.ManageClaims, L("Permissions:ManageClaims"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.ManageSecrets, L("Permissions:ManageSecrets"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.ManageProperties, L("Permissions:ManageProperties"), MultiTenancySides.Host);
apiResourcePermissions.AddChild(AbpIdentityServerPermissions.ApiResources.ManageScopes, L("Permissions:ManageScopes"), MultiTenancySides.Host);
// 身份资源权限
var identityResourcePermissions = identityServerGroup.AddPermission(AbpIdentityServerPermissions.IdentityResources.Default, L("Permissions:IdentityResources"), MultiTenancySides.Host);
identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.Create, L("Permissions:Create"), MultiTenancySides.Host);
identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.Update, L("Permissions:Update"), MultiTenancySides.Host);
identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
// 身份资源属性权限
var identityResourcePropertyPermissiosn = identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.Properties.Default, L("Permissions:IdentityResources:Properties"), MultiTenancySides.Host);
identityResourcePropertyPermissiosn.AddChild(AbpIdentityServerPermissions.IdentityResources.Properties.Create, L("Permissions:Create"), MultiTenancySides.Host);
identityResourcePropertyPermissiosn.AddChild(AbpIdentityServerPermissions.IdentityResources.Properties.Delete, L("Permissions:Delete"), MultiTenancySides.Host);
identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.ManageClaims, L("Permissions:ManageClaims"), MultiTenancySides.Host);
identityResourcePermissions.AddChild(AbpIdentityServerPermissions.IdentityResources.ManageProperties, L("Permissions:ManageProperties"), MultiTenancySides.Host);
}
protected virtual LocalizableString L(string name)

59
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/AbpIdentityServerPermissions.cs

@ -2,7 +2,7 @@
{
public class AbpIdentityServerPermissions
{
public const string GroupName = "IdentityServer";
public const string GroupName = "AbpIdentityServer";
public static class Clients
{
@ -11,33 +11,10 @@
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Clone = Default + ".Clone";
public const string Enabled = Default + ".Enabled";
public const string Disabled = Default + ".Disabled";
public const string ManagePermissions = Default + ".ManagePermissions";
public static class Claims
{
public const string Default = Clients.Default + ".Claims";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class Secrets
{
public const string Default = Clients.Default + ".Secrets";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class Properties
{
public const string Default = Clients.Default + ".Properties";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public const string ManageClaims = Default + ".ManageClaims";
public const string ManageSecrets = Default + ".ManageSecrets";
public const string ManageProperties = Default + ".ManageProperties";
}
public static class ApiResources
@ -46,21 +23,10 @@
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public static class Scope
{
public const string Default = ApiResources.Default + ".Scope";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class Secrets
{
public const string Default = ApiResources.Default + ".Secrets";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public const string ManageScopes = Default + ".ManageScopes";
public const string ManageClaims = Default + ".ManageClaims";
public const string ManageSecrets = Default + ".ManageSecrets";
public const string ManageProperties = Default + ".ManageProperties";
}
public static class IdentityResources
@ -69,13 +35,8 @@
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public static class Properties
{
public const string Default = IdentityResources.Default + ".Properties";
public const string Create = Default + ".Create";
public const string Delete = Default + ".Delete";
}
public const string ManageClaims = Default + ".ManageClaims";
public const string ManageProperties = Default + ".ManageProperties";
}
}
}

3
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/ApiResources/Dto/ApiResourceCreateOrUpdateDto.cs

@ -20,11 +20,14 @@ namespace LINGYUN.Abp.IdentityServer.ApiResources
public List<ApiSecretCreateOrUpdateDto> Secrets { get; set; }
public Dictionary<string, string> Properties { get; set; }
protected ApiResourceCreateOrUpdateDto()
{
UserClaims = new List<string>();
Scopes = new List<ApiScopeDto>();
Secrets = new List<ApiSecretCreateOrUpdateDto>();
Properties = new Dictionary<string, string>();
}
}
}

5
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/ApiResources/Dto/ApiResourceDto.cs

@ -4,7 +4,7 @@ using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.IdentityServer.ApiResources
{
public class ApiResourceDto : FullAuditedEntityDto<Guid>
public class ApiResourceDto : ExtensibleFullAuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -20,11 +20,14 @@ namespace LINGYUN.Abp.IdentityServer.ApiResources
public List<string> UserClaims { get; set; }
public Dictionary<string, string> Properties { get; set; }
public ApiResourceDto()
{
UserClaims = new List<string>();
Scopes = new List<ApiScopeDto>();
Secrets = new List<ApiSecretDto>();
Properties = new Dictionary<string, string>();
}
}
}

5
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/Dto/ClientCloneDto.cs

@ -39,6 +39,10 @@ namespace LINGYUN.Abp.IdentityServer.Clients
/// </summary>
public bool CopyClaim { get; set; }
/// <summary>
/// 复制客户端密钥
/// </summary>
public bool CopySecret { get; set; }
/// <summary>
/// 复制客户端跨域来源
/// </summary>
public bool CopyAllowedCorsOrigin { get; set; }
@ -64,6 +68,7 @@ namespace LINGYUN.Abp.IdentityServer.Clients
CopyPostLogoutRedirectUri = true;
CopyPropertie = true;
CopyRedirectUri = true;
CopySecret = true;
}
}
}

11
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/Dto/ClientUpdateDto.cs

@ -83,13 +83,9 @@ namespace LINGYUN.Abp.IdentityServer.Clients
public int DeviceCodeLifetime { get; set; }
/// <summary>
/// Api资源(AllowScopes)
/// 允许的作用域
/// </summary>
public List<string> ApiResources { get; set; }
/// <summary>
/// 身份资源(AllowScopes)
/// </summary>
public List<string> IdentityResources { get; set; }
public List<string> AllowedScopes { get; set; }
/// <summary>
/// 允许同源
/// </summary>
@ -123,8 +119,7 @@ namespace LINGYUN.Abp.IdentityServer.Clients
{
Enabled = true;
DeviceCodeLifetime = 300;
ApiResources = new List<string>();
IdentityResources = new List<string>();
AllowedScopes = new List<string>();
RedirectUris = new List<string>();
AllowedCorsOrigins = new List<string>();
PostLogoutRedirectUris = new List<string>();

7
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Clients/IClientAppService.cs

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.IdentityServer.Clients
@ -13,5 +14,11 @@ namespace LINGYUN.Abp.IdentityServer.Clients
ClientUpdateDto>
{
Task<ClientDto> CloneAsync(Guid id, ClientCloneDto input);
Task<ListResultDto<string>> GetAssignableApiResourcesAsync();
Task<ListResultDto<string>> GetAssignableIdentityResourcesAsync();
Task<ListResultDto<string>> GetAllDistinctAllowedCorsOriginsAsync();
}
}

2
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/IdentityResources/Dto/IdentityResourceDto.cs

@ -4,7 +4,7 @@ using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.IdentityServer.IdentityResources
{
public class IdentityResourceDto : FullAuditedEntityDto<Guid>
public class IdentityResourceDto : ExtensibleFullAuditedEntityDto<Guid>
{
public string Name { get; set; }

25
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Localization/Resources/en.json

@ -5,19 +5,15 @@
"Permissions:Create": "Create",
"Permissions:Update": "Update",
"Permissions:Clone": "Clone",
"Permissions:Enabled": "Enabled",
"Permissions:Disabled": "Disabled",
"Permissions:Delete": "Delete",
"Permissions:ManageClaims": "Claims",
"Permissions:ManageSecrets": "Secrets",
"Permissions:ManageScopes": "Scopes",
"Permissions:ManageProperties": "Properties",
"Permissions:ManagePermissions": "Permissions",
"Permissions:Clients": "Clients",
"Permissions:Clients:Claims": "Client claim",
"Permissions:Clients:Secrets": "Client secret",
"Permissions:Clients:Properties": "Client property",
"Permissions:ApiResources": "ApiResources",
"Permissions:ApiResources:Scope": "Api scope",
"Permissions:ApiResources:Secrets": "Api secret",
"Permissions:IdentityResources": "IdentityResources",
"Permissions:IdentityResources:Properties": "Identity property",
"ClientIdExisted": "Client id: {0} already exists!",
"ApiResourceNameExisted": "Api resource name: {0} already exists!",
"IdentityResourceNameExisted": "Identity resource name: {0} already exists!",
@ -27,7 +23,12 @@
"ClientPropertyNotFound": "Client property: {0} not found!",
"IdentityResourcePropertyNotFound": "Identity resource property: {0} not found!",
"EncryptionNotImplemented": "Encryption type: {0} not implemented!",
"Information": "Info",
"Basics": "Basics",
"Authentication": "Authentication",
"Token": "Token",
"Consent": "Consent",
"DeviceFlow": "Device Flow",
"Advanced": "Advanced",
"Resource:Enabled": "Enabled",
"Resource:New": "Add New",
"Resource:Edit": "Edit",
@ -53,6 +54,7 @@
"Secret:New": "Add New",
"Secret:Type": "Type",
"Secret:HashType": "Hash Type",
"Secret:HashTypeOnlySharedSecret": "The hash type applies only to the SharedSecret type",
"Secret:Value": "Value",
"Client:Enabled": "Enabled",
"Client:New": "Add New",
@ -66,6 +68,7 @@
"Clone:CopyRedirectUri": "Copy the Client redirect Uri",
"Clone:CopyAllowedScope": "Copy the Client scopes",
"Clone:CopyClaim": "Copy the Client claims",
"Clone:CopySecret": "Copy the Client secrets",
"Clone:CopyAllowedCorsOrigin": "Copy the Client Cors-Origin Uri",
"Clone:CopyPostLogoutRedirectUri": "Copy the Client Redirect Uri",
"Clone:CopyProperties": "Copy the Client Properties",
@ -110,11 +113,15 @@
"Client:UserCodeType": "User Code Type",
"Claims": "Claims",
"Claims:New": "Add New",
"Claims:Delete": "Delete",
"Claims:Type": "Type",
"Claims:Value": "Value",
"Propertites": "Propertites",
"Propertites:New": "Add New",
"Propertites:Key": "Key",
"Propertites:Value": "Value",
"Propertites:Delete": "Delete",
"Propertites:DuplicateKey": "Property already exists and cannot add duplicates!",
"Permissions": "Permissions"
}
}

31
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application.Contracts/LINGYUN/Abp/IdentityServer/Localization/Resources/zh-Hans.json

@ -5,19 +5,15 @@
"Permissions:Create": "新增",
"Permissions:Update": "修改",
"Permissions:Clone": "克隆",
"Permissions:Enabled": "启用",
"Permissions:Disabled": "停用",
"Permissions:Delete": "删除",
"Permissions:ManageClaims": "管理声明",
"Permissions:ManageSecrets": "管理密钥",
"Permissions:ManageScopes": "管理作用域",
"Permissions:ManageProperties": "管理属性",
"Permissions:ManagePermissions": "更改权限",
"Permissions:Clients": "客户端管理",
"Permissions:Clients:Secrets": "客户端密钥",
"Permissions:Clients:Properties": "客户端属性",
"Permissions:Clients:Claims": "客户端声明",
"Permissions:ApiResources": "Api资源管理",
"Permissions:ApiResources:Scope": "授权范围",
"Permissions:ApiResources:Secrets": "Api密钥",
"Permissions:IdentityResources": "身份资源管理",
"Permissions:IdentityResources:Properties": "身份资源属性",
"ClientIdExisted": "客户端标识: {0} 已经存在!",
"ApiResourceNameExisted": "Api资源: {0} 已经存在!",
"IdentityResourceNameExisted": "身份资源: {0} 已经存在!",
@ -27,7 +23,12 @@
"ClientPropertyNotFound": "客户端属性: {0} 不存在!",
"IdentityResourcePropertyNotFound": "身份资源属性: {0} 不存在!",
"EncryptionNotImplemented": "加密类型: {0} 未实现!",
"Information": "信息",
"Basics": "基本信息",
"Authentication": "认证/注销",
"Token": "令牌",
"Consent": "同意屏幕",
"DeviceFlow": "设备流程",
"Advanced": "高级",
"Resource:Enabled": "启用资源",
"Resource:New": "添加新资源",
"Resource:Edit": "编辑资源",
@ -53,6 +54,7 @@
"Secret:New": "添加新密钥",
"Secret:Type": "密钥类型",
"Secret:HashType": "哈希类型",
"Secret:HashTypeOnlySharedSecret": "哈希类型仅适用于 SharedSecret 类型",
"Secret:Value": "值",
"Client:Enabled": "启用客户端",
"Client:New": "添加新客户端",
@ -66,13 +68,14 @@
"Clone:CopyRedirectUri": "复制客户端重定向 Uri",
"Clone:CopyAllowedScope": "复制客户端作用域",
"Clone:CopyClaim": "复制客户端声明",
"Clone:CopySecret": "复制客户端密钥",
"Clone:CopyAllowedCorsOrigin": "复制客户端跨域来源",
"Clone:CopyPostLogoutRedirectUri": "复制客户端注销重定向 Uri",
"Clone:CopyProperties": "复制客户端属性",
"Clone:CopyIdentityProviderRestriction": "复制身份提供程序限制",
"Client:ProtocolType": "协议类型",
"Client:RequireClientSecret": "需要客户端密钥",
"Client:RequirePkce": "需要 Pkce",
"Client:RequiredClientSecret": "需要客户端密钥",
"Client:RequiredPkce": "需要 Pkce",
"Client:AllowedPlainTextPkce": "允许纯文本 Pkce",
"Client:AllowedOfflineAccess": "允许离线访问",
"Client:AllowedScopes": "允许的作用域",
@ -109,12 +112,16 @@
"Client:LogoUri": "徽标 Uri",
"Client:UserCodeType": "用户代码类型",
"Claims": "声明",
"Claims:New": "添加新声明",
"Claims:New": "添加新声明",
"Claims:Delete": "删除声明",
"Claims:Type": "声明类型",
"Claims:Value": "值",
"Propertites": "属性",
"Propertites:New": "添加新属性",
"Propertites:Key": "属性名称",
"Propertites:Value": "值",
"Propertites:Delete": "删除属性",
"Propertites:DuplicateKey": "属性已经存在,不能添加重复项!",
"Permissions": "权限"
}
}

11
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/AbpIdentityServerAppServiceBase.cs

@ -1,13 +1,22 @@
using Volo.Abp.Application.Services;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.IdentityServer.Localization;
namespace LINGYUN.Abp.IdentityServer
{
public abstract class AbpIdentityServerAppServiceBase : ApplicationService
{
private IPermissionChecker _permissionChecker;
protected IPermissionChecker PermissionChecker => LazyGetRequiredService(ref _permissionChecker);
protected AbpIdentityServerAppServiceBase()
{
LocalizationResource = typeof(AbpIdentityServerResource);
}
protected virtual async Task<bool> IsGrantAsync(string policy)
{
return await PermissionChecker.IsGrantedAsync(policy);
}
}
}

6
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/AbpIdentityServerAutoMapperProfile.cs

@ -37,10 +37,12 @@ namespace LINGYUN.Abp.IdentityServer
CreateMap<ApiSecret, ApiSecretDto>();
CreateMap<ApiScope, ApiScopeDto>();
CreateMap<ApiResource, ApiResourceDto>()
.ForMember(dto => dto.UserClaims, map => map.MapFrom(src => src.UserClaims.Select(claim => claim.Type).ToList()));
.ForMember(dto => dto.UserClaims, map => map.MapFrom(src => src.UserClaims.Select(claim => claim.Type).ToList()))
.MapExtraProperties();
CreateMap<IdentityResource, IdentityResourceDto>()
.ForMember(dto => dto.UserClaims, map => map.MapFrom(src => src.UserClaims.Select(claim => claim.Type).ToList()));
.ForMember(dto => dto.UserClaims, map => map.MapFrom(src => src.UserClaims.Select(claim => claim.Type).ToList()))
.MapExtraProperties();
}
}
}

120
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/ApiResources/ApiResourceAppService.cs

@ -7,7 +7,6 @@ using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.IdentityServer.ApiResources;
using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource;
namespace LINGYUN.Abp.IdentityServer.ApiResources
@ -55,7 +54,7 @@ namespace LINGYUN.Abp.IdentityServer.ApiResources
{
Enabled = input.Enabled
};
UpdateApiResourceByInput(apiResource, input);
await UpdateApiResourceByInputAsync(apiResource, input);
apiResource = await ApiResourceRepository.InsertAsync(apiResource);
@ -72,7 +71,7 @@ namespace LINGYUN.Abp.IdentityServer.ApiResources
apiResource.Description = input.Description ?? apiResource.Description;
apiResource.Enabled = input.Enabled;
UpdateApiResourceByInput(apiResource, input);
await UpdateApiResourceByInputAsync(apiResource, input);
apiResource = await ApiResourceRepository.UpdateAsync(apiResource);
@ -90,76 +89,95 @@ namespace LINGYUN.Abp.IdentityServer.ApiResources
await CurrentUnitOfWork.SaveChangesAsync();
}
protected virtual void UpdateApiResourceByInput(ApiResource apiResource, ApiResourceCreateOrUpdateDto input)
protected virtual async Task UpdateApiResourceByInputAsync(ApiResource apiResource, ApiResourceCreateOrUpdateDto input)
{
// 删除不存在的UserClaim
apiResource.UserClaims.RemoveAll(claim => !input.UserClaims.Contains(claim.Type));
foreach (var inputClaim in input.UserClaims)
if (await IsGrantAsync(AbpIdentityServerPermissions.ApiResources.ManageClaims))
{
var userClaim = apiResource.FindClaim(inputClaim);
if (userClaim == null)
// 删除不存在的UserClaim
apiResource.UserClaims.RemoveAll(claim => !input.UserClaims.Contains(claim.Type));
foreach (var inputClaim in input.UserClaims)
{
apiResource.AddUserClaim(inputClaim);
var userClaim = apiResource.FindClaim(inputClaim);
if (userClaim == null)
{
apiResource.AddUserClaim(inputClaim);
}
}
}
// 删除不存在的Scope
apiResource.Scopes.RemoveAll(scope => !input.Scopes.Any(inputScope => scope.Name == inputScope.Name));
foreach (var inputScope in input.Scopes)
if (await IsGrantAsync(AbpIdentityServerPermissions.ApiResources.ManageScopes))
{
var scope = apiResource.FindScope(inputScope.Name);
if (scope == null)
// 删除不存在的Scope
apiResource.Scopes.RemoveAll(scope => !input.Scopes.Any(inputScope => scope.Name == inputScope.Name));
foreach (var inputScope in input.Scopes)
{
scope = apiResource.AddScope(
inputScope.Name, inputScope.DisplayName, inputScope.Description,
inputScope.Required, inputScope.Emphasize, inputScope.ShowInDiscoveryDocument);
}
else
{
scope.Required = inputScope.Required;
scope.Emphasize = inputScope.Emphasize;
scope.Description = inputScope.Description;
scope.DisplayName = inputScope.DisplayName;
scope.ShowInDiscoveryDocument = inputScope.ShowInDiscoveryDocument;
// 删除不存在的ScopeUserClaim
scope.UserClaims.RemoveAll(claim => !inputScope.UserClaims.Contains(claim.Type));
}
var scope = apiResource.FindScope(inputScope.Name);
if (scope == null)
{
scope = apiResource.AddScope(
inputScope.Name, inputScope.DisplayName, inputScope.Description,
inputScope.Required, inputScope.Emphasize, inputScope.ShowInDiscoveryDocument);
}
else
{
scope.Required = inputScope.Required;
scope.Emphasize = inputScope.Emphasize;
scope.Description = inputScope.Description;
scope.DisplayName = inputScope.DisplayName;
scope.ShowInDiscoveryDocument = inputScope.ShowInDiscoveryDocument;
// 删除不存在的ScopeUserClaim
scope.UserClaims.RemoveAll(claim => !inputScope.UserClaims.Contains(claim.Type));
}
foreach (var inputScopeClaim in inputScope.UserClaims)
{
var scopeUserClaim = scope.FindClaim(inputScopeClaim);
if (scopeUserClaim == null)
foreach (var inputScopeClaim in inputScope.UserClaims)
{
scope.AddUserClaim(inputScopeClaim);
var scopeUserClaim = scope.FindClaim(inputScopeClaim);
if (scopeUserClaim == null)
{
scope.AddUserClaim(inputScopeClaim);
}
}
}
}
// 删除不存在的Secret
apiResource.Secrets.RemoveAll(secret => !input.Secrets.Any(inputSecret => secret.Type == inputSecret.Type && secret.Value == inputSecret.Value));
foreach (var inputSecret in input.Secrets)
if (await IsGrantAsync(AbpIdentityServerPermissions.ApiResources.ManageSecrets))
{
// 第一次重复校验已经加密过的字符串
if (apiResource.FindSecret(inputSecret.Value, inputSecret.Type) == null)
// 删除不存在的Secret
apiResource.Secrets.RemoveAll(secret => !input.Secrets.Any(inputSecret => secret.Type == inputSecret.Type && secret.Value == inputSecret.Value));
foreach (var inputSecret in input.Secrets)
{
var apiSecretValue = inputSecret.Value;
if (IdentityServerConstants.SecretTypes.SharedSecret.Equals(inputSecret.Type))
// 第一次重复校验已经加密过的字符串
if (apiResource.FindSecret(inputSecret.Value, inputSecret.Type) == null)
{
if (inputSecret.HashType == HashType.Sha256)
var apiSecretValue = inputSecret.Value;
if (IdentityServerConstants.SecretTypes.SharedSecret.Equals(inputSecret.Type))
{
apiSecretValue = inputSecret.Value.Sha256();
if (inputSecret.HashType == HashType.Sha256)
{
apiSecretValue = inputSecret.Value.Sha256();
}
else if (inputSecret.HashType == HashType.Sha512)
{
apiSecretValue = inputSecret.Value.Sha512();
}
}
else if (inputSecret.HashType == HashType.Sha512)
// 加密之后还需要做一次校验 避免出现重复值
var secret = apiResource.FindSecret(apiSecretValue, inputSecret.Type);
if (secret == null)
{
apiSecretValue = inputSecret.Value.Sha512();
apiResource.AddSecret(apiSecretValue, inputSecret.Expiration, inputSecret.Type, inputSecret.Description);
}
}
// 加密之后还需要做一次校验 避免出现重复值
var secret = apiResource.FindSecret(apiSecretValue, inputSecret.Type);
if (secret == null)
{
apiResource.AddSecret(apiSecretValue, inputSecret.Expiration, inputSecret.Type, inputSecret.Description);
}
}
}
if (await IsGrantAsync(AbpIdentityServerPermissions.ApiResources.ManageProperties))
{
// 删除不存在的属性
apiResource.Properties.RemoveAll(scope => !input.Properties.ContainsKey(scope.Key));
foreach (var property in input.Properties)
{
apiResource.Properties[property.Key] = property.Value;
}
}
}

144
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/Clients/ClientAppService.cs

@ -1,4 +1,6 @@
using IdentityServer4;
using LINGYUN.Abp.IdentityServer.ApiResources;
using LINGYUN.Abp.IdentityServer.IdentityResources;
using IdentityServer4;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Authorization;
using System;
@ -16,10 +18,17 @@ namespace LINGYUN.Abp.IdentityServer.Clients
public class ClientAppService : AbpIdentityServerAppServiceBase, IClientAppService
{
protected IClientRepository ClientRepository { get; }
protected IApiResourceRepository ApiResourceRepository { get; }
protected IIdentityResourceRepository IdentityResourceRepository { get; }
public ClientAppService(IClientRepository clientRepository)
public ClientAppService(
IClientRepository clientRepository,
IApiResourceRepository apiResourceRepository,
IIdentityResourceRepository identityResourceRepository)
{
ClientRepository = clientRepository;
ApiResourceRepository = apiResourceRepository;
IdentityResourceRepository = identityResourceRepository;
}
[Authorize(AbpIdentityServerPermissions.Clients.Create)]
@ -155,22 +164,12 @@ namespace LINGYUN.Abp.IdentityServer.Clients
#region AllowScope
// 删除未在身份资源和Api资源中的作用域
client.AllowedScopes.RemoveAll(scope =>
!input.IdentityResources.Contains(scope.Scope) &&
!input.ApiResources.Contains(scope.Scope));
foreach (var apiResource in input.ApiResources)
{
if (client.FindScope(apiResource) == null)
{
client.AddScope(apiResource);
}
}
foreach (var identityResource in input.IdentityResources)
client.AllowedScopes.RemoveAll(scope => !input.AllowedScopes.Contains(scope.Scope));
foreach (var scope in input.AllowedScopes)
{
if (client.FindScope(identityResource) == null)
if (client.FindScope(scope) == null)
{
client.AddScope(identityResource);
client.AddScope(scope);
}
}
@ -245,34 +244,41 @@ namespace LINGYUN.Abp.IdentityServer.Clients
#region Secrets
// 移除已经不存在的客户端密钥
client.ClientSecrets.RemoveAll(secret => !input.Secrets.Any(inputSecret => secret.Value == inputSecret.Value && secret.Type == inputSecret.Type));
var currentSecrets = new List<ClientSecretDto>();
foreach (var inputSecret in input.Secrets)
if (await IsGrantAsync(AbpIdentityServerPermissions.Clients.ManageSecrets))
{
var inputSecretValue = inputSecret.Value;
// 如果是 SharedSecret 类型的密钥
// 采用 IdentityServer4 服务器扩展方法加密
if (IdentityServerConstants.SecretTypes.SharedSecret.Equals(inputSecret.Type))
// 移除已经不存在的客户端密钥
client.ClientSecrets.RemoveAll(secret => !input.Secrets.Any(inputSecret => secret.Value == inputSecret.Value && secret.Type == inputSecret.Type));
foreach (var inputSecret in input.Secrets)
{
if (inputSecret.HashType == HashType.Sha256)
// 先对加密过的进行过滤
if (client.FindSecret(inputSecret.Value, inputSecret.Type) != null)
{
inputSecretValue = inputSecret.Value.Sha256();
continue;
}
else if (inputSecret.HashType == HashType.Sha512)
var inputSecretValue = inputSecret.Value;
// 如果是 SharedSecret 类型的密钥
// 采用 IdentityServer4 服务器扩展方法加密
if (IdentityServerConstants.SecretTypes.SharedSecret.Equals(inputSecret.Type))
{
inputSecretValue = inputSecret.Value.Sha512();
if (inputSecret.HashType == HashType.Sha256)
{
inputSecretValue = inputSecret.Value.Sha256();
}
else if (inputSecret.HashType == HashType.Sha512)
{
inputSecretValue = inputSecret.Value.Sha512();
}
}
else
{
throw new UserFriendlyException(L["EncryptionNotImplemented", inputSecret.Type]);
}
}
else
{
throw new UserFriendlyException(L["EncryptionNotImplemented", inputSecret.Type]);
}
var clientSecret = client.FindSecret(inputSecretValue, inputSecret.Type);
if (clientSecret == null)
{
client.AddSecret(inputSecretValue, inputSecret.Expiration, inputSecret.Type, inputSecret.Description);
var clientSecret = client.FindSecret(inputSecretValue, inputSecret.Type);
if (clientSecret == null)
{
client.AddSecret(inputSecretValue, inputSecret.Expiration, inputSecret.Type, inputSecret.Description);
}
}
}
@ -280,27 +286,34 @@ namespace LINGYUN.Abp.IdentityServer.Clients
#region Properties
// 移除不存在的属性
client.Properties.RemoveAll(prop => input.Properties.Any(inputProp => inputProp.Key == prop.Key && inputProp.Value == prop.Value));
foreach (var inputProp in input.Properties)
if (await IsGrantAsync(AbpIdentityServerPermissions.Clients.ManageProperties))
{
if (client.FindProperty(inputProp.Key, inputProp.Value) == null)
// 移除不存在的属性
client.Properties.RemoveAll(prop => !input.Properties.ContainsKey(prop.Key));
foreach (var inputProp in input.Properties)
{
client.AddProperty(inputProp.Key, inputProp.Value);
if (client.FindProperty(inputProp.Key, inputProp.Value) == null)
{
client.AddProperty(inputProp.Key, inputProp.Value);
}
}
}
#endregion
#region Claims
// 移除已经不存在的客户端声明
client.ClientSecrets.RemoveAll(secret => !input.Claims.Any(inputClaim => secret.Value == inputClaim.Value && secret.Type == inputClaim.Type));
foreach (var inputClaim in input.Claims)
if (await IsGrantAsync(AbpIdentityServerPermissions.Clients.ManageClaims))
{
if (client.FindClaim(inputClaim.Value, inputClaim.Type) == null)
// 移除已经不存在的客户端声明
client.Claims.RemoveAll(secret => !input.Claims.Any(inputClaim => secret.Value == inputClaim.Value && secret.Type == inputClaim.Type));
foreach (var inputClaim in input.Claims)
{
client.AddClaim(inputClaim.Value, inputClaim.Type);
if (client.FindClaim(inputClaim.Value, inputClaim.Type) == null)
{
client.AddClaim(inputClaim.Value, inputClaim.Type);
}
}
}
@ -404,6 +417,13 @@ namespace LINGYUN.Abp.IdentityServer.Clients
client.AddClaim(claim.Value, claim.Type);
}
}
if (input.CopySecret)
{
foreach (var secret in srcClient.ClientSecrets)
{
client.AddSecret(secret.Value, secret.Expiration, secret.Type, secret.Description);
}
}
if (input.CopyIdentityProviderRestriction)
{
foreach (var provider in srcClient.IdentityProviderRestrictions)
@ -438,5 +458,35 @@ namespace LINGYUN.Abp.IdentityServer.Clients
return ObjectMapper.Map<Client, ClientDto>(client);
}
/// <summary>
/// 查询可用的Api资源
/// </summary>
/// <returns></returns>
public virtual async Task<ListResultDto<string>> GetAssignableApiResourcesAsync()
{
var resourceNames = await ApiResourceRepository.GetNamesAsync();
return new ListResultDto<string>(resourceNames);
}
/// <summary>
/// 查询可用的身份资源
/// </summary>
/// <returns></returns>
public virtual async Task<ListResultDto<string>> GetAssignableIdentityResourcesAsync()
{
var resourceNames = await IdentityResourceRepository.GetNamesAsync();
return new ListResultDto<string>(resourceNames);
}
/// <summary>
/// 查询所有不重复的跨域地址
/// </summary>
/// <returns></returns>
public virtual async Task<ListResultDto<string>> GetAllDistinctAllowedCorsOriginsAsync()
{
var corsOrigins = await ClientRepository.GetAllDistinctAllowedCorsOriginsAsync();
return new ListResultDto<string>(corsOrigins);
}
}
}

32
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Application/LINGYUN/Abp/IdentityServer/IdentityResources/IdentityResourceAppService.cs

@ -48,7 +48,7 @@ namespace LINGYUN.Abp.IdentityServer.IdentityResources
var identityResource = new IdentityResource(GuidGenerator.Create(), input.Name, input.DisplayName,
input.Description, input.Enabled, input.Required, input.Emphasize,
input.ShowInDiscoveryDocument);
UpdateApiResourceByInput(identityResource, input);
await UpdateApiResourceByInputAsync(identityResource, input);
await CurrentUnitOfWork.SaveChangesAsync();
@ -61,7 +61,7 @@ namespace LINGYUN.Abp.IdentityServer.IdentityResources
public virtual async Task<IdentityResourceDto> UpdateAsync(Guid id, IdentityResourceCreateOrUpdateDto input)
{
var identityResource = await IdentityResourceRepository.GetAsync(id);
UpdateApiResourceByInput(identityResource, input);
await UpdateApiResourceByInputAsync(identityResource, input);
identityResource = await IdentityResourceRepository.UpdateAsync(identityResource);
await CurrentUnitOfWork.SaveChangesAsync();
@ -75,7 +75,7 @@ namespace LINGYUN.Abp.IdentityServer.IdentityResources
await IdentityResourceRepository.DeleteAsync(id);
}
protected virtual void UpdateApiResourceByInput(IdentityResource identityResource, IdentityResourceCreateOrUpdateDto input)
protected virtual async Task UpdateApiResourceByInputAsync(IdentityResource identityResource, IdentityResourceCreateOrUpdateDto input)
{
if (!string.Equals(identityResource.Name, input.Name, StringComparison.InvariantCultureIgnoreCase))
{
@ -94,22 +94,28 @@ namespace LINGYUN.Abp.IdentityServer.IdentityResources
identityResource.Required = input.Required;
identityResource.ShowInDiscoveryDocument = input.ShowInDiscoveryDocument;
// 删除不存在的UserClaim
identityResource.UserClaims.RemoveAll(claim => input.UserClaims.Contains(claim.Type));
foreach (var inputClaim in input.UserClaims)
if (await IsGrantAsync(AbpIdentityServerPermissions.IdentityResources.ManageClaims))
{
var userClaim = identityResource.FindUserClaim(inputClaim);
if (userClaim == null)
// 删除不存在的UserClaim
identityResource.UserClaims.RemoveAll(claim => input.UserClaims.Contains(claim.Type));
foreach (var inputClaim in input.UserClaims)
{
identityResource.AddUserClaim(inputClaim);
var userClaim = identityResource.FindUserClaim(inputClaim);
if (userClaim == null)
{
identityResource.AddUserClaim(inputClaim);
}
}
}
// 删除不存在的Property
identityResource.Properties.RemoveAll(scope => !input.Properties.ContainsKey(scope.Key));
foreach (var inputProp in input.Properties)
if (await IsGrantAsync(AbpIdentityServerPermissions.IdentityResources.ManageProperties))
{
identityResource.Properties[inputProp.Key] = inputProp.Value;
// 删除不存在的Property
identityResource.Properties.RemoveAll(scope => !input.Properties.ContainsKey(scope.Key));
foreach (var inputProp in input.Properties)
{
identityResource.Properties[inputProp.Key] = inputProp.Value;
}
}
}
}

11
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.IdentityServer.ApiResources
{
public interface IApiResourceRepository : Volo.Abp.IdentityServer.ApiResources.IApiResourceRepository
{
Task<List<string>> GetNamesAsync(CancellationToken cancellationToken = default);
}
}

3
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs

@ -1,5 +1,4 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.IdentityServer.Grants;

11
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.Domain/LINGYUN/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.IdentityServer.IdentityResources
{
public interface IIdentityResourceRepository : Volo.Abp.IdentityServer.IdentityResources.IIdentityResourceRepository
{
Task<List<string>> GetNamesAsync(CancellationToken cancellationToken = default);
}
}

35
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.EntityFrameworkCore/LINGYUN/Abp/IdentityServer/ApiResources/EfCoreApiResourceRepository.cs

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
namespace LINGYUN.Abp.IdentityServer.ApiResources
{
[Dependency(ServiceLifetime.Transient)]
[ExposeServices(
typeof(IApiResourceRepository),
typeof(ApiResourceRepository),
typeof(Volo.Abp.IdentityServer.ApiResources.IApiResourceRepository))]
public class EfCoreApiResourceRepository : ApiResourceRepository, IApiResourceRepository
{
public EfCoreApiResourceRepository(
IDbContextProvider<IIdentityServerDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetNamesAsync(CancellationToken cancellationToken = default)
{
return await DbSet
.Select(x => x.Name)
.Distinct()
.ToListAsync(GetCancellationToken(cancellationToken));
}
}
}

35
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.EntityFrameworkCore/LINGYUN/Abp/IdentityServer/IdentityResources/EfCoreIdentityResourceRepository.cs

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.IdentityServer.IdentityResources;
namespace LINGYUN.Abp.IdentityServer.IdentityResources
{
[Dependency(ServiceLifetime.Transient)]
[ExposeServices(
typeof(IIdentityResourceRepository),
typeof(IdentityResourceRepository),
typeof(Volo.Abp.IdentityServer.IdentityResources.IIdentityResourceRepository))]
public class EfCoreIdentityResourceRepository : IdentityResourceRepository, IIdentityResourceRepository
{
public EfCoreIdentityResourceRepository(
IDbContextProvider<IIdentityServerDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetNamesAsync(CancellationToken cancellationToken = default)
{
return await DbSet
.Select(x => x.Name)
.Distinct()
.ToListAsync(GetCancellationToken(cancellationToken));
}
}
}

15
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.HttpApi/LINGYUN/Abp/IdentityServer/AbpIdentityServerHttpApiModule.cs

@ -1,5 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Localization.Resources.AbpUi;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.IdentityServer.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.IdentityServer
@ -17,5 +20,15 @@ namespace LINGYUN.Abp.IdentityServer
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpIdentityServerHttpApiModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<AbpIdentityServerResource>()
.AddBaseTypes(typeof(AbpUiResource));
});
}
}
}

21
aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.HttpApi/LINGYUN/Abp/IdentityServer/Clients/ClientController.cs

@ -57,5 +57,26 @@ namespace LINGYUN.Abp.IdentityServer.Clients
{
return await ClientAppService.CloneAsync(id, input);
}
[HttpGet]
[Route("assignable-api-resources")]
public virtual async Task<ListResultDto<string>> GetAssignableApiResourcesAsync()
{
return await ClientAppService.GetAssignableApiResourcesAsync();
}
[HttpGet]
[Route("assignable-identity-resources")]
public virtual async Task<ListResultDto<string>> GetAssignableIdentityResourcesAsync()
{
return await ClientAppService.GetAssignableIdentityResourcesAsync();
}
[HttpGet]
[Route("distinct-cors-origins")]
public virtual async Task<ListResultDto<string>> GetAllDistinctAllowedCorsOriginsAsync()
{
return await ClientAppService.GetAllDistinctAllowedCorsOriginsAsync();
}
}
}

BIN
aspnet-core/services/apigateway/LINGYUN.ApiGateway.Host/event-bus-cap.db

Binary file not shown.

10
aspnet-core/services/identity-server/LINGYUN.Abp.IdentityServer4.HttpApi.Host/AbpIdentityServerAdminHttpApiHostModule.cs

@ -55,11 +55,11 @@ namespace LINGYUN.Abp.IdentityServer4
typeof(LINGYUN.Abp.Account.AbpAccountHttpApiModule),
typeof(LINGYUN.Abp.IdentityServer.AbpIdentityServerApplicationModule),
typeof(LINGYUN.Abp.IdentityServer.AbpIdentityServerHttpApiModule),
typeof(LINGYUN.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule),
typeof(LINGYUN.Abp.IdentityServer.EntityFrameworkCore.AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpAccountApplicationModule),
typeof(AbpAccountHttpApiModule),
typeof(AbpEntityFrameworkCoreMySQLModule),
typeof(LINGYUN.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule),
typeof(AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
@ -177,12 +177,6 @@ namespace LINGYUN.Abp.IdentityServer4
Configure<RedisCacheOptions>(options =>
{
var redisConfig = ConfigurationOptions.Parse(options.Configuration);
// 单独一个缓存数据库
var databaseConfig = configuration.GetSection("Redis:DefaultDatabase");
if (databaseConfig.Exists())
{
redisConfig.DefaultDatabase = databaseConfig.Get<int>();
}
options.ConfigurationOptions = redisConfig;
options.InstanceName = configuration["Redis:InstanceName"];
});

2
vueJs/src/api/api-resources.ts

@ -81,6 +81,7 @@ export class ApiResourceCreateOrUpdate {
userClaims = new Array<string>()
scopes = new Array<ApiScope>()
secrets = new Array<ApiSecretCreateOrUpdate>()
properties: {[key: string]: string} = {}
}
export class ApiResourceCreate extends ApiResourceCreateOrUpdate {
@ -98,6 +99,7 @@ export class ApiResource extends FullAuditedEntityDto {
userClaims = new Array<string>()
scopes = new Array<ApiScope>()
secrets = new Array<ApiSecretCreateOrUpdate>()
properties: {[key: string]: string} = {}
}
export class ApiResourceGetByPaged extends PagedAndSortedResultRequestDto {

74
vueJs/src/api/clients.ts

@ -1,5 +1,5 @@
import ApiService from './serviceBase'
import { FullAuditedEntityDto, PagedAndSortedResultRequestDto, PagedResultDto, SecretBase, Claim } from './types'
import { FullAuditedEntityDto, PagedAndSortedResultRequestDto, ListResultDto, PagedResultDto, SecretBase, Claim } from './types'
const sourceUrl = '/api/identity-server/clients'
const serviceUrl = process.env.VUE_APP_BASE_API
@ -36,6 +36,21 @@ export default class ClientService {
const _url = sourceUrl + '/' + id
return ApiService.Delete(_url, serviceUrl)
}
public static getAssignableApiResources() {
const _url = sourceUrl + '/assignable-api-resources'
return ApiService.Get<ListResultDto<string>>(_url, serviceUrl)
}
public static getAssignableIdentityResources() {
const _url = sourceUrl + '/assignable-identity-resources'
return ApiService.Get<ListResultDto<string>>(_url, serviceUrl)
}
public static getAllDistinctAllowedCorsOrigins() {
const _url = sourceUrl + '/distinct-cors-origins'
return ApiService.Get<ListResultDto<string>>(_url, serviceUrl)
}
}
export enum HashType {
@ -53,6 +68,8 @@ export class ClientGetByPaged extends PagedAndSortedResultRequestDto {
export class ClientClaim extends Claim {}
export class ClientSecret extends SecretCreateOrUpdate {}
export class ClientClone {
sourceClientId = ''
clientId = ''
@ -62,6 +79,7 @@ export class ClientClone {
copyRedirectUri = true
copyAllowedScope = true
copyClaim = true
copySecret = true
copyAllowedCorsOrigin = true
copyPostLogoutRedirectUri = true
copyPropertie = true
@ -113,14 +131,14 @@ export class Client extends FullAuditedEntityDto {
userCodeType?: string
deviceCodeLifetime!: number
allowedScopes = new Array<string>()
clientSecrets = new Array<string>()
clientSecrets = new Array<ClientSecret>()
allowedGrantTypes = new Array<string>()
allowedCorsOrigins = new Array<string>()
redirectUris = new Array<string>()
postLogoutRedirectUris = new Array<string>()
identityProviderRestrictions = new Array<string>()
claims = new Array<string>()
properties = new Array<string>()
claims = new Array<ClientClaim>()
properties:{[key: string]: string} = {}
}
export class ClientCreateOrUpdate {
@ -167,8 +185,7 @@ export class ClientUpdate extends ClientCreateOrUpdate {
userSsoLifetime!: number
userCodeType? = ''
deviceCodeLifetime = 300
apiResources = new Array<string>()
identityResources = new Array<string>()
allowedScopes = new Array<string>()
allowedCorsOrigins = new Array<string>()
redirectUris = new Array<string>()
postLogoutRedirectUris = new Array<string>()
@ -176,4 +193,49 @@ export class ClientUpdate extends ClientCreateOrUpdate {
properties: {[key: string]: string} = {}
secrets = new Array<SecretCreateOrUpdate>()
claims = new Array<ClientClaim>()
public updateByClient(client: Client) {
this.clientUri = client.clientUri
this.logoUri = client.logoUri
this.enabled = client.enabled
this.protocolType = client.protocolType
this.requireClientSecret = client.requireClientSecret
this.requireConsent = client.requireConsent
this.allowRememberConsent = client.allowRememberConsent
this.alwaysIncludeUserClaimsInIdToken = client.alwaysIncludeUserClaimsInIdToken
this.requirePkce = client.requirePkce
this.allowPlainTextPkce = client.allowPlainTextPkce
this.allowAccessTokensViaBrowser = client.allowAccessTokensViaBrowser
this.frontChannelLogoutUri = client.frontChannelLogoutUri
this.frontChannelLogoutSessionRequired = client.frontChannelLogoutSessionRequired
this.backChannelLogoutUri = client.backChannelLogoutUri
this.backChannelLogoutSessionRequired = client.backChannelLogoutSessionRequired
this.allowOfflineAccess = client.allowOfflineAccess
this.identityTokenLifetime = client.identityTokenLifetime
this.accessTokenLifetime = client.accessTokenLifetime
this.authorizationCodeLifetime = client.authorizationCodeLifetime
this.consentLifetime = client.consentLifetime
this.absoluteRefreshTokenLifetime = client.absoluteRefreshTokenLifetime
this.slidingRefreshTokenLifetime = client.slidingRefreshTokenLifetime
this.refreshTokenUsage = client.refreshTokenUsage
this.updateAccessTokenClaimsOnRefresh = client.updateAccessTokenClaimsOnRefresh
this.refreshTokenExpiration = client.refreshTokenExpiration
this.accessTokenType = client.accessTokenType
this.enableLocalLogin = client.enableLocalLogin
this.includeJwtId = client.includeJwtId
this.alwaysSendClientClaims = client.alwaysSendClientClaims
this.clientClaimsPrefix = client.clientClaimsPrefix
this.pairWiseSubjectSalt = client.pairWiseSubjectSalt
this.userSsoLifetime = client.userSsoLifetime
this.userCodeType = client.userCodeType
this.deviceCodeLifetime = client.deviceCodeLifetime
this.allowedCorsOrigins = client.allowedCorsOrigins
this.redirectUris = client.redirectUris
this.postLogoutRedirectUris = client.postLogoutRedirectUris
this.identityProviderRestrictions = client.identityProviderRestrictions
this.allowedScopes = client.allowedScopes
this.secrets = client.clientSecrets
this.claims = client.claims
this.properties = client.properties
}
}

36
vueJs/src/api/identity-server4.ts

@ -0,0 +1,36 @@
import ApiService from './serviceBase'
const openIdConfigurationUrl = '/.well-known/openid-configuration'
export default class IdentityServer4Service {
public static getOpenIdConfiguration() {
return ApiService.Get<OpenIdConfiguration>(openIdConfigurationUrl)
}
}
export class OpenIdConfiguration {
issuer!: string
jwks_uri!: string
authorization_endpoint!: string
token_endpoint!: string
userinfo_endpoint!: string
end_session_endpoint!: string
check_session_iframe!: string
revocation_endpoint!: string
introspection_endpoint!: string
device_authorization_endpoint!: string
frontchannel_logout_supported!: boolean
frontchannel_logout_session_supported!: boolean
backchannel_logout_supported!: boolean
backchannel_logout_session_supported!: boolean
scopes_supported = new Array<string>()
claims_supported = new Array<string>()
grant_types_supported = new Array<string>()
response_types_supported = new Array<string>()
response_modes_supported = new Array<string>()
token_endpoint_auth_methods_supported = new Array<string>()
id_token_signing_alg_values_supported = new Array<string>()
subject_types_supported = new Array<string>()
code_challenge_methods_supported = new Array<string>()
request_parameter_supported!: boolean
}

9
vueJs/src/api/types.ts

@ -259,9 +259,14 @@ export class SecretBase implements ISecret {
expiration: Date | undefined
}
export enum HashType {
Sha256,
Sha512
}
export class Claim implements IClaim {
type!: string
value!: string
type = ''
value = ''
constructor(
type: string,

142
vueJs/src/lang/en.ts

@ -427,148 +427,6 @@ export default {
aggregateJsonPath: 'Json路径',
definedAggregatorProviders: '聚合提供者'
},
identityServer: {
otherOpera: '更多操作',
enabled: '启用客户端',
disbled: '停用客户端',
clientStatus: '客户端状态',
deleteClient: '删除客户端',
updateClient: '编辑客户端',
updateClientByName: '编辑客户端 {name}',
deleteClientById: '是否要删除客户端: {id}',
deleteClientSuccess: '客户端: {id} 已删除!',
createClientSuccess: '客户端: {id} 已添加!',
updateClientSuccess: '客户端: {id} 已修改!',
clientClaim: '客户端声明',
clientProperty: '客户端属性',
clientSecret: '客户端密钥',
clientPermission: '客户端权限',
deleteSecret: '删除密钥',
deleteSecretByType: '是否要删除客户端密钥: {type}',
deleteSecretSuccess: '客户端密钥: {type} 已删除!',
createSecretSuccess: '客户端密钥: {type} 已添加!',
createSecret: '添加客户端密钥',
deleteClaim: '删除声明',
deleteClaimByType: '是否要删除客户端声明: {type}',
deleteClaimSuccess: '客户端声明: {type} 已删除!',
createClaimSuccess: '客户端声明: {type} 已添加!',
createClaim: '添加客户端声明',
deleteProperty: '删除属性',
deleteClientPropertyByType: '是否要删除客户端属性: {key}',
deleteClientPropertySuccess: '客户端属性: {key} 已删除!',
createClientPropertySuccess: '客户端属性: {key} 已添加!',
createClientProperty: '添加客户端属性',
createClient: '添加客户端',
clientId: '客户端标识',
clientName: '客户端名称',
description: '客户端说明',
cloneClint: '克隆客户端',
copyAllowedGrantType: '复制客户端授权类型',
copyRedirectUri: '复制客户端重定向Uri',
copyAllowedScope: '复制客户端作用域',
copyClaim: '复制客户端声明',
copyAllowedCorsOrigin: '复制客户端跨域来源',
copyPostLogoutRedirectUri: '复制客户端注销重定向Uri',
copyPropertie: '复制客户端属性',
copyIdentityProviderRestriction: '复制身份提供程序限制',
protocolType: '协议类型',
requireClientSecret: '需要客户端密钥',
requirePkce: '需要Pkce',
allowPlainTextPkce: '允许纯文本Pkce',
allowOfflineAccess: '允许离线访问',
allowedScopes: '允许的作用域',
redirectUris: '重定向Uri',
allowedGrantTypes: '允许的授权类型',
allowAccessTokensViaBrowser: '允许通过浏览器访问令牌',
identityTokenLifetime: '身份令牌有效期(s)',
accessTokenLifetime: '访问令牌有效期(s)',
authorizationCodeLifetime: '授权码有效期(s)',
absoluteRefreshTokenLifetime: '绝对刷新令牌有效期(s)',
slidingRefreshTokenLifetime: '滚动刷新令牌有效期(s)',
deviceCodeLifetime: '设备授权码有效期(s)',
clientClaimsPrefix: '客户端声明前缀',
basicOptions: '基本设置',
frontChannelLogoutUri: '前端通道注销 Uri',
frontChannelLogoutSessionRequired: '需要前端通道注销会话',
backChannelLogoutUri: '后端通道退出 Uri',
backChannelLogoutSessionRequired: '需要后端通道注销会话',
enableLocalLogin: '启用本地登录',
postLogoutRedirectUris: '注销重定向 Uri',
identityProviderRestrictions: '身份提供程序限制',
userSsoLifetime: '用户 SSO 生命周期',
accessTokenType: '访问令牌类型',
refreshTokenUsage: '刷新令牌使用情况',
refreshTokenExpiration: '刷新令牌过期方式',
allowedCorsOrigins: '允许跨域来源',
updateAccessTokenClaimsOnRefresh: '刷新时更新访问令牌声明',
includeJwtId: '包括 Jwt 标识',
alwaysSendClientClaims: '始终发送客户端声明',
alwaysIncludeUserClaimsInIdToken: '始终在标识令牌中包含用户声明',
pairWiseSubjectSalt: '配对主体盐',
requireConsent: '需要同意',
allowRememberConsent: '允许记住同意',
clientUri: '客户端 Uri',
logoUri: '徽标 Uri',
userCodeType: '用户代码类型',
secretType: '密钥类型',
secretValue: '密钥值',
secretHashType: '哈希类型',
hashOnlySharedSecret: '哈希类型仅适用于 SharedSecret 类型',
secretDescription: '密钥说明',
expiration: '过期日期',
claimType: '声明类型',
claimValue: '声明值',
propertyKey: '属性名称',
propertyValue: '属性值',
createApiResource: '添加Api资源',
updateApiResource: '编辑Api资源',
updateApiResourceByName: '编辑Api资源 {name}',
deleteApiResourceByName: '删除Api资源 {name}',
createApiResourceSuccess: 'Api资源 {name} 已添加!',
deleteApiResourceSuccess: 'Api资源 {name} 已删除!',
updateApiResourceSuccess: 'Api资源 {name} 已修改!',
createApiSecret: '添加Api密钥',
deleteApiSecret: '删除Api密钥',
deleteApiSecretByType: '删除Api密钥 {type}',
createApiSecretSuccess: 'Api密钥 {type} 已添加!',
deleteApiSecretSuccess: 'Api密钥 {type} 已删除!',
apiResourceSecret: 'Api 密钥',
createApiScope: '添加Api作用域',
deleteApiScope: '删除Api作用域',
deleteApiScopeByName: '删除Api作用域 {name}',
createApiScopeSuccess: 'Api作用域 {name} 已添加!',
deleteApiScopeSuccess: 'Api作用域 {name} 已删除!',
apiResourceScope: 'Api 作用域',
deleteApiResource: '删除资源',
resourceName: '资源名称',
resourceDisplayName: '显示名称',
enabledResource: '启用资源',
resourceStatus: '资源状态',
resourceDescription: '资源说明',
resourceUserClaims: '用户声明',
apiScopeName: '名称',
apiScopeDisplayName: '显示名称',
apiScopeDescription: '描述',
apiScopeRequired: '必须',
apiScopeEmphasize: '强调',
apiScopeShowInDiscoveryDocument: '在发现文档显示',
createIdentityResource: '添加身份资源',
updateIdentityResource: '编辑身份资源',
identityResourceProperties: '资源属性',
deleteIdentityResource: '删除资源',
updateIdentityResourceByName: '编辑身份资源 {name}',
deleteIdentityResourceByName: '删除身份资源 {name}',
createIdentityResourceSuccess: '身份资源 {name} 已添加!',
deleteIdentityResourceSuccess: '身份资源 {name} 已删除!',
updateIdentityResourceSuccess: '身份资源 {name} 已修改!',
identityResourceRequired: '必须',
identityResourceEmphasize: '强调',
identityResourceShowInDiscoveryDocument: '在发现文档显示',
deleteIdentityPropertyByKey: '是否要删除身份资源属性: {key}',
deleteIdentityPropertySuccess: '身份资源属性: {key} 已删除!',
createIdentityPropertySuccess: '身份资源属性: {key} 已添加!',
createIdentityProperty: '添加身份资源属性'
},
tenant: {
createTenant: '创建租户',
updateTenant: '编辑租户',

142
vueJs/src/lang/zh.ts

@ -431,148 +431,6 @@ export default {
aggregateJsonPath: 'Json路径',
definedAggregatorProviders: '聚合提供者'
},
identityServer: {
otherOpera: '更多操作',
enabled: '启用客户端',
disbled: '停用客户端',
clientStatus: '客户端状态',
deleteClient: '删除客户端',
updateClient: '编辑客户端',
updateClientByName: '编辑客户端 {name}',
deleteClientById: '是否要删除客户端: {id}',
deleteClientSuccess: '客户端: {id} 已删除!',
createClientSuccess: '客户端: {id} 已添加!',
updateClientSuccess: '客户端: {id} 已修改!',
clientClaim: '客户端声明',
clientProperty: '客户端属性',
clientSecret: '客户端密钥',
clientPermission: '客户端权限',
deleteSecret: '删除密钥',
deleteSecretByType: '是否要删除客户端密钥: {type}',
deleteSecretSuccess: '客户端密钥: {type} 已删除!',
createSecretSuccess: '客户端密钥: {type} 已添加!',
createSecret: '添加客户端密钥',
deleteClaim: '删除声明',
deleteClaimByType: '是否要删除客户端声明: {type}',
deleteClaimSuccess: '客户端声明: {type} 已删除!',
createClaimSuccess: '客户端声明: {type} 已添加!',
createClaim: '添加客户端声明',
deleteProperty: '删除属性',
deleteClientPropertyByType: '是否要删除客户端属性: {key}',
deleteClientPropertySuccess: '客户端属性: {key} 已删除!',
createClientPropertySuccess: '客户端属性: {key} 已添加!',
createClientProperty: '添加客户端属性',
createClient: '添加客户端',
clientId: '客户端标识',
clientName: '客户端名称',
description: '客户端说明',
cloneClint: '克隆客户端',
copyAllowedGrantType: '复制客户端授权类型',
copyRedirectUri: '复制客户端重定向Uri',
copyAllowedScope: '复制客户端作用域',
copyClaim: '复制客户端声明',
copyAllowedCorsOrigin: '复制客户端跨域来源',
copyPostLogoutRedirectUri: '复制客户端注销重定向Uri',
copyPropertie: '复制客户端属性',
copyIdentityProviderRestriction: '复制身份提供程序限制',
protocolType: '协议类型',
requireClientSecret: '需要客户端密钥',
requirePkce: '需要Pkce',
allowPlainTextPkce: '允许纯文本Pkce',
allowOfflineAccess: '允许离线访问',
allowedScopes: '允许的作用域',
redirectUris: '重定向Uri',
allowedGrantTypes: '允许的授权类型',
allowAccessTokensViaBrowser: '允许通过浏览器访问令牌',
identityTokenLifetime: '身份令牌有效期(s)',
accessTokenLifetime: '访问令牌有效期(s)',
authorizationCodeLifetime: '授权码有效期(s)',
absoluteRefreshTokenLifetime: '绝对刷新令牌有效期(s)',
slidingRefreshTokenLifetime: '滚动刷新令牌有效期(s)',
deviceCodeLifetime: '设备授权码有效期(s)',
clientClaimsPrefix: '客户端声明前缀',
basicOptions: '基本设置',
frontChannelLogoutUri: '前端通道注销 Uri',
frontChannelLogoutSessionRequired: '需要前端通道注销会话',
backChannelLogoutUri: '后端通道退出 Uri',
backChannelLogoutSessionRequired: '需要后端通道注销会话',
enableLocalLogin: '启用本地登录',
postLogoutRedirectUris: '注销重定向 Uri',
identityProviderRestrictions: '身份提供程序限制',
userSsoLifetime: '用户 SSO 生命周期',
accessTokenType: '访问令牌类型',
refreshTokenUsage: '刷新令牌使用情况',
refreshTokenExpiration: '刷新令牌过期方式',
allowedCorsOrigins: '允许跨域来源',
updateAccessTokenClaimsOnRefresh: '刷新时更新访问令牌声明',
includeJwtId: '包括 Jwt 标识',
alwaysSendClientClaims: '始终发送客户端声明',
alwaysIncludeUserClaimsInIdToken: '始终在标识令牌中包含用户声明',
pairWiseSubjectSalt: '配对主体盐',
requireConsent: '需要同意',
allowRememberConsent: '允许记住同意',
clientUri: '客户端 Uri',
logoUri: '徽标 Uri',
userCodeType: '用户代码类型',
secretType: '密钥类型',
secretValue: '密钥值',
secretHashType: '哈希类型',
hashOnlySharedSecret: '哈希类型仅适用于 SharedSecret 类型',
secretDescription: '密钥说明',
expiration: '过期日期',
claimType: '声明类型',
claimValue: '声明值',
propertyKey: '属性名称',
propertyValue: '属性值',
createApiResource: '添加Api资源',
updateApiResource: '编辑Api资源',
updateApiResourceByName: '编辑Api资源 {name}',
deleteApiResourceByName: '删除Api资源 {name}',
createApiResourceSuccess: 'Api资源 {name} 已添加!',
deleteApiResourceSuccess: 'Api资源 {name} 已删除!',
updateApiResourceSuccess: 'Api资源 {name} 已修改!',
createApiSecret: '添加Api密钥',
deleteApiSecret: '删除Api密钥',
deleteApiSecretByType: '删除Api密钥 {type}',
createApiSecretSuccess: 'Api密钥 {type} 已添加!',
deleteApiSecretSuccess: 'Api密钥 {type} 已删除!',
apiResourceSecret: 'Api 密钥',
createApiScope: '添加Api作用域',
deleteApiScope: '删除Api作用域',
deleteApiScopeByName: '删除Api作用域 {name}',
createApiScopeSuccess: 'Api作用域 {name} 已添加!',
deleteApiScopeSuccess: 'Api作用域 {name} 已删除!',
apiResourceScope: 'Api 作用域',
deleteApiResource: '删除资源',
resourceName: '资源名称',
resourceDisplayName: '显示名称',
enabledResource: '启用资源',
resourceStatus: '资源状态',
resourceDescription: '资源说明',
resourceUserClaims: '用户声明',
apiScopeName: '名称',
apiScopeDisplayName: '显示名称',
apiScopeDescription: '描述',
apiScopeRequired: '必须',
apiScopeEmphasize: '强调',
apiScopeShowInDiscoveryDocument: '在发现文档显示',
createIdentityResource: '添加身份资源',
updateIdentityResource: '编辑身份资源',
identityResourceProperties: '资源属性',
deleteIdentityResource: '删除资源',
updateIdentityResourceByName: '编辑身份资源 {name}',
deleteIdentityResourceByName: '删除身份资源 {name}',
createIdentityResourceSuccess: '身份资源 {name} 已添加!',
deleteIdentityResourceSuccess: '身份资源 {name} 已删除!',
updateIdentityResourceSuccess: '身份资源 {name} 已修改!',
identityResourceRequired: '必须',
identityResourceEmphasize: '强调',
identityResourceShowInDiscoveryDocument: '在发现文档显示',
deleteIdentityPropertyByKey: '是否要删除身份资源属性: {key}',
deleteIdentityPropertySuccess: '身份资源属性: {key} 已删除!',
createIdentityPropertySuccess: '身份资源属性: {key} 已添加!',
createIdentityProperty: '添加身份资源属性'
},
tenant: {
createTenant: '创建租户',
updateTenant: '编辑租户',

8
vueJs/src/router/modules/identityServer.ts

@ -7,7 +7,7 @@ const identityServerRouter: RouteConfig = {
meta: {
title: 'identityServer',
icon: 'identity-server',
roles: ['IdentityServer.Clients', 'IdentityServer.ApiResources', 'IdentityServer.IdentityResources'],
roles: ['AbpIdentityServer.Clients', 'AbpIdentityServer.ApiResources', 'AbpIdentityServer.IdentityResources'],
alwaysShow: true
},
children: [
@ -18,7 +18,7 @@ const identityServerRouter: RouteConfig = {
meta: {
title: 'clients',
icon: 'client',
roles: ['IdentityServer.Clients']
roles: ['AbpIdentityServer.Clients']
}
},
{
@ -28,7 +28,7 @@ const identityServerRouter: RouteConfig = {
meta: {
title: 'apiresources',
icon: 'api',
roles: ['IdentityServer.ApiResources']
roles: ['AbpIdentityServer.ApiResources']
}
},
{
@ -38,7 +38,7 @@ const identityServerRouter: RouteConfig = {
meta: {
title: 'identityresources',
icon: 'identity',
roles: ['IdentityServer.IdentityResources']
roles: ['AbpIdentityServer.IdentityResources']
}
}
]

141
vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceCreateOrEditForm.vue

@ -15,15 +15,14 @@
ref="formApiResource"
label-width="100px"
:model="apiResource"
:rules="apiResourceRules"
>
<el-tabs
v-model="activeTable"
v-model="activeTabPane"
type="border-card"
>
<el-tab-pane
name="infomation"
:label="$t('AbpIdentityServer.Information')"
name="basics"
:label="$t('AbpIdentityServer.Basics')"
>
<el-form-item
prop="enabled"
@ -36,6 +35,11 @@
<el-form-item
prop="name"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input
v-model="apiResource.name"
@ -76,6 +80,47 @@
/>
</el-tab-pane>
<el-tab-pane
v-if="isEdit"
name="avanced"
>
<el-dropdown
slot="label"
@command="onDropdownMenuItemChanged"
>
<span class="el-dropdown-link">
{{ $t('AbpIdentityServer.Advanced') }}<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="api-resource-scope-edit-form">
{{ $t('AbpIdentityServer.Scope') }}
</el-dropdown-item>
<el-dropdown-item command="secret-edit-form">
{{ $t('AbpIdentityServer.Secret') }}
</el-dropdown-item>
<el-dropdown-item command="properties-edit-form">
{{ $t('AbpIdentityServer.Propertites') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<component
:is="advancedComponent"
:user-claims="apiResourceClaims"
:api-resource-scopes="apiResource.scopes"
:secrets="apiResource.secrets"
:allowed-create="checkPermission(['AbpIdentityServer.ApiResources.ManageProperties'])"
:allowed-delete="checkPermission(['AbpIdentityServer.ApiResources.ManageProperties'])"
:allowed-create-secret="checkPermission(['AbpIdentityServer.ApiResources.ManageSecrets'])"
:allowed-delete-secret="checkPermission(['AbpIdentityServer.ApiResources.ManageSecrets'])"
:properties="apiResource.properties"
@onScopeCreated="apiResourceScopeCreated"
@onScopeDeleted="apiResourceScopeDeleted"
@onSecretCreated="apiResourceSecretCreated"
@onSecretDeleted="apiResourceSecretDeleted"
@onCreated="onPropertyCreated"
@onDeleted="onPropertyDeleted"
/>
</el-tab-pane>
<!-- <el-tab-pane
v-if="isEdit"
name="scopes"
:label="$t('AbpIdentityServer.Scope')"
@ -83,8 +128,8 @@
<api-resource-scope-edit-form
:user-claims="apiResourceClaims"
:api-resource-scopes="apiResource.scopes"
@apiResourceScopeCreated="apiResourceScopeCreated"
@apiResourceScopeDeleted="apiResourceScopeDeleted"
@onScopeCreated="apiResourceScopeCreated"
@onScopeDeleted="apiResourceScopeDeleted"
/>
</el-tab-pane>
<el-tab-pane
@ -93,30 +138,44 @@
:label="$t('AbpIdentityServer.Secret')"
>
<el-card>
<api-resource-secret-edit-form
:api-resource-secrets="apiResource.secrets"
@apiResourceSecretCreated="apiResourceSecretCreated"
@apiResourceSecretDeleted="apiResourceSecretDeleted"
<secret-edit-form
:secrets="apiResource.secrets"
@onSecretCreated="apiResourceSecretCreated"
@onSecretDeleted="apiResourceSecretDeleted"
/>
</el-card>
</el-tab-pane>
<el-tab-pane
v-if="isEdit"
name="properties"
:label="$t('AbpIdentityServer.Propertites')"
>
<properties-edit-form
:allowed-create="checkPermission(['AbpIdentityServer.ApiResources.ManageProperties'])"
:allowed-delete="checkPermission(['AbpIdentityServer.ApiResources.ManageProperties'])"
:properties="apiResource.properties"
@onCreated="onPropertyCreated"
@onDeleted="onPropertyDeleted"
/>
</el-tab-pane> -->
</el-tabs>
<el-form-item>
<el-button
class="cancel"
style="width:100px"
type="info"
@click="onCancel"
>
{{ $t('table.cancel') }}
{{ $t('AbpIdentityServer.Cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
style="width:100px"
@click="onSaveApiResource"
icon="el-icon-check"
@click="onSave"
>
{{ $t('table.confirm') }}
{{ $t('AbpIdentityServer.Save') }}
</el-button>
</el-form-item>
</el-form>
@ -131,21 +190,23 @@ import ApiResourceService, {
ApiResourceCreate,
ApiResourceUpdate,
ApiSecretCreateOrUpdate,
ApiResourceCreateOrUpdate, HashType
ApiResourceCreateOrUpdate
} from '@/api/api-resources'
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
import ClaimTypeApiService from '@/api/cliam-type'
import { checkPermission } from '@/utils/permission'
import { dateFormat } from '@/utils/index'
import { Claim } from '@/api/types'
import ApiResourceSecretEditForm from './ApiResourceSecretEditForm.vue'
import ApiResourceScopeEditForm from './ApiResourceScopeEditForm.vue'
import PropertiesEditForm from '../../components/PropertiesEditForm.vue'
import SecretEditForm from '../../components/SecretEditForm.vue'
@Component({
name: 'ApiResourceCreateOrEditForm',
components: {
ApiResourceScopeEditForm,
ApiResourceSecretEditForm
SecretEditForm,
PropertiesEditForm,
ApiResourceScopeEditForm
},
filters: {
dateTimeFilter(datetime: string) {
@ -170,15 +231,11 @@ export default class extends Vue {
@Prop({ default: '' })
private apiResourceId!: string
private activeTable = 'infomation'
private activeTabPane = 'basics'
private advancedComponent = 'api-resource-scope-edit-form'
private apiResource = new ApiResource()
private apiResourceClaims = new Array<Claim>()
private newApiSecret = new ApiSecretCreateOrUpdate()
private apiResourceRules = {
name: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('AbpIdentityServer.Name') }), trigger: 'blur' }
]
}
get isEdit() {
if (this.apiResourceId) {
@ -205,7 +262,7 @@ export default class extends Vue {
}
private handleGetApiResource() {
this.activeTable = 'infomation'
this.activeTabPane = 'basics'
if (this.apiResourceId && this.showDialog) {
ApiResourceService.getApiResourceById(this.apiResourceId).then(res => {
this.apiResource = res
@ -215,13 +272,13 @@ export default class extends Vue {
}
}
private apiResourceSecretCreated(hashType: HashType, type: string, value: string, description: string, expiration: Date | undefined) {
private apiResourceSecretCreated(secret: any) {
const apiSecret = new ApiSecretCreateOrUpdate()
apiSecret.hashType = hashType
apiSecret.type = type
apiSecret.value = value
apiSecret.description = description
apiSecret.expiration = expiration
apiSecret.hashType = secret.hashType
apiSecret.type = secret.type
apiSecret.value = secret.value
apiSecret.description = secret.description
apiSecret.expiration = secret.expiration
this.apiResource.secrets.push(apiSecret)
}
@ -239,7 +296,7 @@ export default class extends Vue {
apiScope.required = required
apiScope.emphasize = emphasize
apiScope.showInDiscoveryDocument = showInDiscoveryDocument
apiScope.userClaims = userClaims
apiScope.userClaims.push(...userClaims)
this.apiResource.scopes.push(apiScope)
}
@ -248,7 +305,12 @@ export default class extends Vue {
this.apiResource.scopes.splice(scopeIndex, 1)
}
private onSaveApiResource() {
private onDropdownMenuItemChanged(component: any) {
this.activeTabPane = 'avanced'
this.advancedComponent = component
}
private onSave() {
const frmApiResource = this.$refs.formApiResource as any
frmApiResource.validate((valid: boolean) => {
if (valid) {
@ -283,6 +345,7 @@ export default class extends Vue {
apiResource.userClaims = this.apiResource.userClaims
apiResource.scopes = this.apiResource.scopes
apiResource.secrets = this.apiResource.secrets
apiResource.properties = this.apiResource.properties
}
private onFormClosed(changed: boolean) {
@ -294,6 +357,14 @@ export default class extends Vue {
this.onFormClosed(false)
}
private onPropertyCreated(key: string, value: string) {
this.$set(this.apiResource.properties, key, value)
}
private onPropertyDeleted(key: string) {
this.$delete(this.apiResource.properties, key)
}
public resetFields() {
const frmApiResource = this.$refs.formApiResource as any
frmApiResource.resetFields()
@ -310,11 +381,13 @@ export default class extends Vue {
position: absolute;
right: 10px;
top: 20px;
width:100px;
}
.cancel {
position: absolute;
right: 120px;
top: 20px;
width:100px;
}
.full-select {
width: 100%;

56
vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceScopeEditForm.vue

@ -2,20 +2,25 @@
<div>
<el-form
ref="apiResourceScopeEditForm"
v-permission="['AbpIdentityServer.ApiResources.ManageScopes']"
label-width="80px"
:model="apiResourceScope"
:rules="apiResourceScopeRules"
>
<el-tabs
type="border-card"
style="width: 100%;"
>
<el-tab-pane :label="$t('AbpIdentityServer.Information')">
<el-tab-pane :label="$t('AbpIdentityServer.Basics')">
<el-row>
<el-col :span="12">
<el-form-item
prop="name"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input v-model="apiResourceScope.name" />
</el-form-item>
@ -103,7 +108,7 @@
label-width="80px"
>
<el-button
:disabled="!checkPermission(['IdentityServer.ApiResources.Scope.Delete'])"
v-permission="['AbpIdentityServer.ApiResources.ManageScopes']"
type="danger"
icon="el-icon-delete"
size="mini"
@ -113,31 +118,46 @@
{{ $t('AbpIdentityServer.Scope:Delete') }}
</el-button>
<el-tabs type="border-card">
<el-tab-pane :label="$t('AbpIdentityServer.Information')">
<el-tab-pane :label="$t('AbpIdentityServer.Basics')">
<el-row>
<el-col :span="12">
<el-form-item :label="$t('AbpIdentityServer.Name')">
<el-input v-model="scope.name" />
<el-input
v-model="scope.name"
:readonly="readonly"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('AbpIdentityServer.DisplayName')">
<el-input v-model="scope.displayName" />
<el-input
v-model="scope.displayName"
:readonly="readonly"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('AbpIdentityServer.Description')">
<el-input v-model="scope.description" />
<el-input
v-model="scope.description"
:readonly="readonly"
/>
</el-form-item>
<el-row>
<el-col :span="6">
<el-form-item :label="$t('AbpIdentityServer.Required')">
<el-switch v-model="scope.required" />
<el-switch
v-model="scope.required"
:readonly="readonly"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item :label="$t('AbpIdentityServer.Emphasize')">
<el-switch v-model="scope.emphasize" />
<el-switch
v-model="scope.emphasize"
:readonly="readonly"
/>
</el-form-item>
</el-col>
<el-col :span="12">
@ -145,7 +165,10 @@
:label="$t('AbpIdentityServer.ShowInDiscoveryDocument')"
label-width="150px"
>
<el-switch v-model="scope.showInDiscoveryDocument" />
<el-switch
v-model="scope.showInDiscoveryDocument"
:readonly="readonly"
/>
</el-form-item>
</el-col>
</el-row>
@ -204,22 +227,21 @@ export default class ApiResourceScopeEditForm extends Vue {
@Prop({ default: () => { return new Array<ApiScope>() } })
private apiResourceScopes!: ApiScope[]
private apiResourceScope = new ApiScope()
private apiResourceScopeRules = {
name: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('AbpIdentityServer.Name') }), trigger: 'blur' }
]
get readonly() {
return !checkPermission(['AbpIdentityServer.ApiResources.ManageScopes'])
}
private apiResourceScope = new ApiScope()
private handleDeleteApiScope(name: string) {
this.$emit('apiResourceScopeDeleted', name)
this.$emit('onScopeDeleted', name)
}
private onSave() {
const apiResourceScopeEditForm = this.$refs.apiResourceScopeEditForm as Form
apiResourceScopeEditForm.validate(valid => {
if (valid) {
this.$emit('apiResourceScopeCreated',
this.$emit('onScopeCreated',
this.apiResourceScope.name, this.apiResourceScope.required,
this.apiResourceScope.emphasize, this.apiResourceScope.showInDiscoveryDocument,
this.apiResourceScope.userClaims, this.apiResourceScope.displayName, this.apiResourceScope.description)

250
vueJs/src/views/admin/identityServer/api-resources/components/ApiResourceSecretEditForm.vue

@ -1,250 +0,0 @@
<template>
<el-form
ref="apiResourceSecretEditForm"
:model="apiResourceSecret"
label-width="80px"
:rules="apiResourceSecretRules"
>
<el-row>
<el-col :span="12">
<el-form-item
prop="type"
:label="$t('AbpIdentityServer.Secret:Type')"
>
<el-select
v-model="apiResourceSecret.type"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('AbpIdentityServer.Secret:Type')})"
>
<el-option
key="JWK"
label="JsonWebKey"
value="JWK"
/>
<el-option
key="SharedSecret"
label="SharedSecret"
value="SharedSecret"
/>
<el-option
key="X509Name"
label="X509CertificateName"
value="X509Name"
/>
<el-option
key="X509CertificateBase64"
label="X509CertificateBase64"
value="X509CertificateBase64"
/>
<el-option
key="X509Thumbprint"
label="X509CertificateThumbprint"
value="X509Thumbprint"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
prop="hashType"
:label="$t('AbpIdentityServer.Secret:HashType')"
>
<el-popover
ref="popHashType"
placement="top-start"
trigger="hover"
:content="$t('identityServer.hashOnlySharedSecret')"
/>
<el-select
v-model="apiResourceSecret.hashType"
v-popover:popHashType
:disabled="apiResourceSecret.type !== 'SharedSecret'"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('AbpIdentityServer.Secret:HashType')})"
>
<el-option
:key="0"
label="Sha256"
:value="0"
/>
<el-option
:key="1"
label="Sha512"
:value="1"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item
prop="value"
:label="$t('AbpIdentityServer.Secret:Value')"
>
<el-input
v-model="apiResourceSecret.value"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Secret:Value')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('AbpIdentityServer.Description')"
>
<el-input
v-model="apiResourceSecret.description"
/>
</el-form-item>
<el-form-item
prop="expiration"
:label="$t('AbpIdentityServer.Expiration')"
>
<el-date-picker
v-model="apiResourceSecret.expiration"
class="full-select"
type="datetime"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
@click="onSave"
>
{{ $t('AbpIdentityServer.Secret:New') }}
</el-button>
</el-form-item>
<el-table
row-key="value"
:data="apiResourceSecrets"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column
:label="$t('AbpIdentityServer.Secret:Type')"
prop="type"
sortable
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.type }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Secret:Value')"
prop="value"
sortable
width="200px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Description')"
prop="description"
width="120px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.description }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Expiration')"
prop="expiration"
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.expiration | dateTimeFilter }}</span>
</template>
</el-table-column>
<el-table-column
align="center"
width="80px"
fixed="right"
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.ApiResources.Secrets.Delete'])"
type="danger"
icon="el-icon-delete"
size="mini"
@click="handleDeleteApiSecret(row.type, row.value)"
/>
</template>
</el-table-column>
</el-table>
</el-form>
</template>
<script lang="ts">
import { ApiSecretCreateOrUpdate, ApiSecret } from '@/api/api-resources'
import { Component, Vue, Prop } from 'vue-property-decorator'
import { dateFormat } from '@/utils/index'
import { checkPermission } from '@/utils/permission'
import { Form } from 'element-ui'
@Component({
name: 'ApiResourceSecretEditForm',
filters: {
dateTimeFilter(datetime: string) {
if (datetime) {
const date = new Date(datetime)
return dateFormat(date, 'YYYY-mm-dd HH:MM:SS')
}
return ''
}
},
methods: {
checkPermission
}
})
export default class ApiResourceSecretEditForm extends Vue {
@Prop({ default: () => { return new Array<ApiSecret>() } })
private apiResourceSecrets!: ApiSecret[]
private apiResourceSecret = new ApiSecretCreateOrUpdate()
private apiResourceSecretRules = {
type: [
{ required: true, message: this.l('pleaseSelectBy', { key: this.l('AbpIdentityServer.Secret:Type') }), trigger: 'blur' }
],
value: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('AbpIdentityServer.Secret:Value') }), trigger: 'blur' }
]
}
private onSave() {
const apiResourceSecretEditForm = this.$refs.apiResourceSecretEditForm as Form
apiResourceSecretEditForm.validate(valid => {
if (valid) {
this.$emit('apiResourceSecretCreated',
this.apiResourceSecret.hashType, this.apiResourceSecret.type, this.apiResourceSecret.value,
this.apiResourceSecret.description, this.apiResourceSecret.expiration)
apiResourceSecretEditForm.resetFields()
}
})
}
private handleDeleteApiSecret(type: string, value: string) {
this.$emit('apiResourceSecretDeleted', type, value)
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>
<style lang="scss" scoped>
.full-select {
width: 100%;
}
</style>

8
vueJs/src/views/admin/identityServer/api-resources/index.vue

@ -17,12 +17,12 @@
type="primary"
@click="refreshPagedData"
>
{{ $t('searchList') }}
{{ $t('AbpIdentityServer.Search') }}
</el-button>
<el-button
class="filter-item"
type="primary"
:disabled="!checkPermission(['IdentityServer.ApiResources.Create'])"
:disabled="!checkPermission(['AbpIdentityServer.ApiResources.Create'])"
@click="handleShowEditApiResourceForm('')"
>
{{ $t('AbpIdentityServer.Resource:New') }}
@ -121,7 +121,7 @@
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.ApiResources.Update'])"
:disabled="!checkPermission(['AbpIdentityServer.ApiResources.Update'])"
size="mini"
type="primary"
@click="handleShowEditApiResourceForm(row.id, row.name)"
@ -129,7 +129,7 @@
{{ $t('AbpIdentityServer.Resource:Edit') }}
</el-button>
<el-button
:disabled="!checkPermission(['IdentityServer.ApiResources.Delete'])"
:disabled="!checkPermission(['AbpIdentityServer.ApiResources.Delete'])"
size="mini"
type="danger"
@click="handleDeleteApiResource(row.id, row.name)"

236
vueJs/src/views/admin/identityServer/client/components/ClientClaimEditForm.vue

@ -1,85 +1,83 @@
<template>
<el-dialog
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.clientClaim')"
custom-class="modal-form"
:show-close="false"
@close="onFormClosed"
>
<div class="app-container">
<el-form
ref="formClientClaim"
label-width="100px"
:model="clientClaim"
:rules="clientClaimRules"
<div>
<el-form
ref="clientClaimForm"
v-permission="['AbpIdentityServer.Clients.ManageClaims']"
label-width="100px"
:model="clientClaim"
>
<el-form-item
prop="type"
:label="$t('AbpIdentityServer.Claims:Type')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Type')}),
trigger: 'blur'
}"
>
<el-form-item
prop="type"
:label="$t('identityServer.claimType')"
>
<el-select
v-model="clientClaim.type"
style="width: 100%"
:placeholder="$t('pleaseSelectBy', {key: $t('identityServer.claimType')})"
@change="onClaimTypeChanged"
>
<el-option
v-for="claim in claimTypes"
:key="claim.id"
:label="claim.name"
:value="claim.name"
/>
</el-select>
</el-form-item>
<el-form-item
prop="value"
:label="$t('identityServer.claimValue')"
<el-select
v-model="clientClaim.type"
style="width: 100%"
:placeholder="$t('pleaseSelectBy', {key: $t('AbpIdentityServer.Claims:Type')})"
@change="onClaimTypeChanged"
>
<el-input
v-if="hasStringValueType(clientClaim.type)"
v-model="clientClaim.value"
type="text"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.claimValue')})"
/>
<el-input
v-else-if="hasIntegerValueType(clientClaim.type)"
v-model="clientClaim.value"
type="number"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.claimValue')})"
/>
<el-switch
v-else-if="hasBooleanValueType(clientClaim.type)"
v-model="clientClaim.value"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.claimValue')})"
/>
<el-date-picker
v-else-if="hasDateTimeValueType(clientClaim.type)"
v-model="clientClaim.value"
type="datetime"
style="width: 100%"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.claimValue')})"
<el-option
v-for="claim in claimTypes"
:key="claim.id"
:label="claim.name"
:value="claim.name"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
</el-select>
</el-form-item>
<el-form-item
prop="value"
:label="$t('AbpIdentityServer.Claims:Value')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Value')}),
trigger: 'blur'
}"
>
<el-input
v-if="hasStringValueType(clientClaim.type)"
v-model="clientClaim.value"
type="text"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Value')})"
/>
<el-input
v-else-if="hasIntegerValueType(clientClaim.type)"
v-model="clientClaim.value"
type="number"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Value')})"
/>
<el-switch
v-else-if="hasBooleanValueType(clientClaim.type)"
v-model="clientClaim.value"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Value')})"
/>
<el-date-picker
v-else-if="hasDateTimeValueType(clientClaim.type)"
v-model="clientClaim.value"
type="datetime"
style="width: 100%"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Claims:Value')})"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Claims.Create'])"
@click="onSave"
>
<el-button
type="primary"
style="width:180px"
:disabled="!checkPermission(['IdentityServer.Clients.Claims.Create'])"
@click="onSaveClientClaim"
>
{{ $t('identityServer.createClaim') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-divider />
{{ $t('AbpIdentityServer.Claims:New') }}
</el-button>
</el-form-item>
</el-form>
<el-table
row-key="clientId"
@ -90,7 +88,7 @@
style="width: 100%;"
>
<el-table-column
:label="$t('identityServer.claimType')"
:label="$t('AbpIdentityServer.Claims:Type')"
prop="type"
sortable
width="150px"
@ -101,7 +99,7 @@
</template>
</el-table-column>
<el-table-column
:label="$t('identityServer.claimValue')"
:label="$t('AbpIdentityServer.Claims:Value')"
prop="value"
sortable
min-width="100%"
@ -112,30 +110,30 @@
</template>
</el-table-column>
<el-table-column
:label="$t('operaActions')"
:label="$t('AbpIdentityServer.Actions')"
align="center"
width="150px"
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.Clients.Claims.Delete'])"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Claims.Delete'])"
size="mini"
type="danger"
@click="handleDeleteClientClaim(row.type, row.value)"
>
{{ $t('identityServer.deleteClaim') }}
{{ $t('AbpIdentityServer.Claims:Delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script lang="ts">
import { dateFormat } from '@/utils/index'
import ClaimTypeApiService, { IdentityClaimType, IdentityClaimValueType } from '@/api/cliam-type'
import ClientService, { ClientClaim, ClientClaimCreate } from '@/api/clients'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { ClientClaim } from '@/api/clients'
import { Component, Vue, Prop } from 'vue-property-decorator'
import { checkPermission } from '@/utils/permission'
@Component({
@ -145,25 +143,11 @@ import { checkPermission } from '@/utils/permission'
}
})
export default class extends Vue {
@Prop({ default: false })
private showDialog!: boolean
@Prop({ default: '' })
private clientId!: string
@Prop({ default: () => new Array<ClientClaim>() })
private clientClaims!: ClientClaim[]
private clientClaim: ClientClaimCreate
private clientClaim = new ClientClaim('', '')
private claimTypes = new Array<IdentityClaimType>()
private clientClaimRules = {
type: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.claimType') }), trigger: 'change' }
],
value: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.claimValue') }), trigger: 'blur' }
]
}
get cliamType() {
return (claimName: string) => {
@ -183,7 +167,7 @@ export default class extends Vue {
case IdentityClaimValueType.String :
return value
case IdentityClaimValueType.Boolean :
return value.toLowerCase() === 'true'
return value
case IdentityClaimValueType.DateTime :
return dateFormat(new Date(value), 'YYYY-mm-dd HH:MM:SS')
}
@ -214,16 +198,6 @@ export default class extends Vue {
}
}
constructor() {
super()
this.clientClaim = new ClientClaimCreate()
}
@Watch('clientId', { immediate: true })
private onClientIdChanged() {
this.clientClaim.clientId = this.clientId
}
mounted() {
this.handleGetClaimTypes()
}
@ -235,19 +209,7 @@ export default class extends Vue {
}
private handleDeleteClientClaim(type: string, value: string) {
this.$confirm(this.l('identityServer.deleteClaimByType', { type: value }),
this.l('identityServer.deleteClaim'), {
callback: (action) => {
if (action === 'confirm') {
ClientService.deleteClientClaim(this.clientId, type, value).then(() => {
const deleteClaimIndex = this.clientClaims.findIndex(claim => claim.type === type && claim.value === value)
this.clientClaims.splice(deleteClaimIndex, 1)
this.$message.success(this.l('identityServer.deleteClaimSuccess', { type: value }))
this.$emit('clientClaimChanged')
})
}
}
})
this.$emit('onClientClaimDeleted', type, value)
}
private onClaimTypeChanged() {
@ -268,32 +230,16 @@ export default class extends Vue {
}
}
private onSaveClientClaim() {
const frmClientClaim = this.$refs.formClientClaim as any
frmClientClaim.validate((valid: boolean) => {
private onSave() {
const clientClaimForm = this.$refs.clientClaimForm as any
clientClaimForm.validate((valid: boolean) => {
if (valid) {
this.clientClaim.clientId = this.clientId
ClientService.addClientClaim(this.clientClaim).then(claim => {
this.clientClaims.push(claim)
const successMessage = this.l('identityServer.createClaimSuccess', { type: this.clientClaim.type })
this.$message.success(successMessage)
frmClientClaim.resetFields()
this.$emit('clientClaimChanged')
})
this.$emit('onClientClaimCreated', this.clientClaim.type, this.clientClaim.value)
clientClaimForm.resetFields()
}
})
}
private onFormClosed() {
this.resetFields()
this.$emit('closed')
}
public resetFields() {
const frmClientClaim = this.$refs.formClientClaim as any
frmClientClaim.resetFields()
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}

86
vueJs/src/views/admin/identityServer/client/components/ClientCloneForm.vue

@ -3,41 +3,50 @@
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.cloneClint')"
:title="$t('AbpIdentityServer.Client:Clone')"
custom-class="modal-form"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="onFormClosed"
@close="onFormClosed(false)"
>
<div class="app-container">
<el-form
ref="formCloneClient"
label-width="175px"
label-width="180px"
:model="client"
:rules="clientRules"
>
<el-form-item
prop="clientId"
:label="$t('identityServer.clientId')"
:label="$t('AbpIdentityServer.Client:Id')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.clientId"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientId')})"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')})"
/>
</el-form-item>
<el-form-item
prop="clientName"
:label="$t('identityServer.clientName')"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.clientName"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientName')})"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('identityServer.description')"
:label="$t('AbpIdentityServer.Description')"
>
<el-input
v-model="client.description"
@ -45,7 +54,7 @@
</el-form-item>
<el-form-item
prop="copyAllowedGrantType"
:label="$t('identityServer.copyAllowedGrantType')"
:label="$t('AbpIdentityServer.Clone:CopyAllowedGrantType')"
>
<el-switch
v-model="client.copyAllowedGrantType"
@ -53,7 +62,7 @@
</el-form-item>
<el-form-item
prop="copyRedirectUri"
:label="$t('identityServer.copyRedirectUri')"
:label="$t('AbpIdentityServer.Clone:CopyRedirectUri')"
>
<el-switch
v-model="client.copyRedirectUri"
@ -61,7 +70,7 @@
</el-form-item>
<el-form-item
prop="copyAllowedScope"
:label="$t('identityServer.copyAllowedScope')"
:label="$t('AbpIdentityServer.Clone:CopyAllowedScope')"
>
<el-switch
v-model="client.copyAllowedScope"
@ -69,15 +78,23 @@
</el-form-item>
<el-form-item
prop="copyClaim"
:label="$t('identityServer.copyClaim')"
:label="$t('AbpIdentityServer.Clone:CopyClaim')"
>
<el-switch
v-model="client.copyClaim"
/>
</el-form-item>
<el-form-item
prop="copySecret"
:label="$t('AbpIdentityServer.Clone:CopySecret')"
>
<el-switch
v-model="client.copySecret"
/>
</el-form-item>
<el-form-item
prop="copyAllowedCorsOrigin"
:label="$t('identityServer.copyAllowedCorsOrigin')"
:label="$t('AbpIdentityServer.Clone:CopyAllowedCorsOrigin')"
>
<el-switch
v-model="client.copyAllowedCorsOrigin"
@ -85,7 +102,7 @@
</el-form-item>
<el-form-item
prop="copyPostLogoutRedirectUri"
:label="$t('identityServer.copyPostLogoutRedirectUri')"
:label="$t('AbpIdentityServer.Clone:CopyPostLogoutRedirectUri')"
>
<el-switch
v-model="client.copyPostLogoutRedirectUri"
@ -93,7 +110,7 @@
</el-form-item>
<el-form-item
prop="copyPropertie"
:label="$t('identityServer.copyPropertie')"
:label="$t('AbpIdentityServer.Clone:CopyProperties')"
>
<el-switch
v-model="client.copyPropertie"
@ -101,7 +118,7 @@
</el-form-item>
<el-form-item
prop="copyIdentityProviderRestriction"
:label="$t('identityServer.copyIdentityProviderRestriction')"
:label="$t('AbpIdentityServer.Clone:CopyIdentityProviderRestriction')"
>
<el-switch
v-model="client.copyIdentityProviderRestriction"
@ -112,17 +129,19 @@
<el-button
class="cancel"
style="width:100px"
@click="onCancel"
type="info"
@click="onFormClosed(false)"
>
{{ $t('table.cancel') }}
{{ $t('AbpIdentityServer.Cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
style="width:100px"
@click="onCloneClient"
icon="el-icon-check"
@click="onSave"
>
{{ $t('table.confirm') }}
{{ $t('AbpIdentityServer.Save') }}
</el-button>
</el-form-item>
</el-form>
@ -145,14 +164,6 @@ export default class extends Vue {
private clientId!: string
private client: ClientClone
private clientRules = {
clientId: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientId') }), trigger: 'blur' }
],
clientName: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientName') }), trigger: 'blur' }
]
}
constructor() {
super()
@ -164,27 +175,22 @@ export default class extends Vue {
this.client.sourceClientId = this.clientId
}
private onCloneClient() {
private onSave() {
const frmClient = this.$refs.formCloneClient as any
frmClient.validate((valid: boolean) => {
if (valid) {
ClientService.cloneClient(this.client).then(client => {
const successMessage = this.l('identityServer.createClientSuccess', { id: client.clientId })
ClientService.cloneClient(this.clientId, this.client).then(() => {
const successMessage = this.l('global.successful')
this.$message.success(successMessage)
frmClient.resetFields()
this.$emit('closed', true)
this.onFormClosed(true)
})
}
})
}
private onFormClosed() {
this.onCancel()
}
private onCancel() {
private onFormClosed(changed: boolean) {
this.resetFields()
this.$emit('closed')
this.$emit('closed', changed)
}
public resetFields() {

210
vueJs/src/views/admin/identityServer/client/components/ClientCreateForm.vue

@ -3,129 +3,157 @@
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.createClient')"
:title="$t('AbpIdentityServer.Client:New')"
custom-class="modal-form"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="onFormClosed(false)"
>
<div class="app-container">
<el-form
ref="formClient"
label-width="100px"
:model="client"
:rules="clientRules"
<el-form
ref="formClient"
label-width="100px"
:model="client"
>
<el-form-item
prop="clientId"
:label="$t('AbpIdentityServer.Client:Id')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')}),
trigger: 'blur'
}"
>
<el-form-item
prop="clientId"
:label="$t('identityServer.clientId')"
>
<el-input
v-model="client.clientId"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientId')})"
/>
</el-form-item>
<el-form-item
prop="clientName"
:label="$t('identityServer.clientName')"
>
<el-input
v-model="client.clientName"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientName')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('identityServer.description')"
>
<el-input
v-model="client.description"
/>
</el-form-item>
<el-form-item
prop="allowedGrantTypes"
:label="$t('identityServer.allowedGrantTypes')"
label-width="120px"
<el-input
v-model="client.clientId"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')})"
/>
</el-form-item>
<el-form-item
prop="clientName"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.clientName"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('AbpIdentityServer.Description')"
>
<el-input
v-model="client.description"
/>
</el-form-item>
<el-form-item
prop="allowedGrantTypes"
:label="$t('AbpIdentityServer.Client:AllowedGrantTypes')"
label-width="120px"
>
<el-select
v-model="client.allowedGrantTypes"
multiple
filterable
allow-create
clearable
class="full-select"
>
<el-input-tag-ex
v-model="client.allowedGrantTypes"
:data="client.allowedGrantTypes"
label="grantType"
<el-option
v-for="(grantType, index) in supportedGrantypes"
:key="index"
:label="grantType"
:value="grantType"
/>
</el-form-item>
</el-select>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
<el-form-item>
<el-button
class="cancel"
type="info"
@click="onFormClosed(false)"
>
<el-button
type="primary"
style="width:180px"
@click="onSaveClient"
>
{{ $t('identityServer.createClient') }}
</el-button>
</el-form-item>
</el-form>
</div>
{{ $t('AbpIdentityServer.Cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
icon="el-icon-check"
@click="onSave"
>
{{ $t('AbpIdentityServer.Save') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script lang="ts">
import ClientService, { ClientCreate } from '@/api/clients'
import { Component, Vue, Prop } from 'vue-property-decorator'
import ElInputTagEx from '@/components/InputTagEx/index.vue'
import { Form } from 'element-ui'
import ClientClaimEditForm from './ClientClaimEditForm.vue'
import SecretEditForm from '../../components/SecretEditForm.vue'
import PropertiesEditForm from '../../components/PropertiesEditForm.vue'
import ClientApiService, { ClientCreate } from '@/api/clients'
@Component({
name: 'ClientCreateForm',
components: {
ElInputTagEx
SecretEditForm,
PropertiesEditForm,
ClientClaimEditForm
}
})
export default class extends Vue {
export default class ClientCreateForm extends Vue {
@Prop({ default: false })
private showDialog!: boolean
private client: ClientCreate
private clientRules = {
clientId: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientId') }), trigger: 'change' }
],
clientName: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientName') }), trigger: 'blur' }
]
}
@Prop({ default: () => { return new Array<string>() } })
private supportedGrantypes!: string[]
constructor() {
super()
this.client = new ClientCreate()
}
private client = new ClientCreate()
private onSaveClient() {
const frmClient = this.$refs.formClient as any
frmClient.validate((valid: boolean) => {
private onSave() {
const clientEditForm = this.$refs.formClient as Form
clientEditForm.validate(valid => {
if (valid) {
ClientService.createClient(this.client).then(client => {
const successMessage = this.l('identityServer.createClientSuccess', { id: client.clientId })
this.$message.success(successMessage)
this.$emit('clientChanged')
this.onFormClosed(true)
})
ClientApiService.createClient(this.client)
.then(() => {
this.$message.success(this.$t('global.successful').toString())
this.onFormClosed(true)
})
}
})
}
private onFormClosed(changed: boolean) {
this.resetFields()
const clientEditForm = this.$refs.formClient as Form
clientEditForm.resetFields()
this.$emit('closed', changed)
}
public resetFields() {
const frmClient = this.$refs.formClient as any
frmClient.resetFields()
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>
<style lang="scss" scoped>
.full-select {
width: 100%;
}
.confirm {
position: absolute;
right: 10px;
width:100px;
}
.cancel {
position: absolute;
right: 120px;
width:100px;
}
</style>

501
vueJs/src/views/admin/identityServer/client/components/ClientEditForm.vue

@ -3,22 +3,26 @@
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.updateClientByName', {name: client.clientName})"
:title="title"
custom-class="modal-form"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="onFormClosed(false)"
>
<el-form
ref="formClient"
label-width="100px"
:model="client"
:rules="clientRules"
>
<el-tabs>
<el-tab-pane :label="$t('identityServer.basicOptions')">
<el-tabs v-model="activeTabPane">
<el-tab-pane
name="basics"
:label="$t('AbpIdentityServer.Basics')"
>
<el-form-item
prop="enabled"
:label="$t('identityServer.enabled')"
:label="$t('AbpIdentityServer.Client:Enabled')"
>
<el-switch
v-model="client.enabled"
@ -28,29 +32,39 @@
<el-col :span="12">
<el-form-item
prop="clientId"
:label="$t('identityServer.clientId')"
:label="$t('AbpIdentityServer.Client:Id')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.clientId"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientId')})"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:Id')})"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
prop="clientName"
:label="$t('identityServer.clientName')"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.clientName"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.clientName')})"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')})"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item
prop="description"
:label="$t('identityServer.description')"
:label="$t('AbpIdentityServer.Description')"
>
<el-input
v-model="client.description"
@ -58,12 +72,17 @@
</el-form-item>
<el-form-item
prop="protocolType"
:label="$t('identityServer.protocolType')"
:label="$t('AbpIdentityServer.Client:ProtocolType')"
:rules="{
required: true,
message: $t('pleaseSelectBy', {key: $t('AbpIdentityServer.Client:ProtocolType')}),
trigger: 'blur'
}"
>
<el-select
v-model="client.protocolType"
class="full-select"
:placeholder="$t('pleaseSelectBy', {name: $t('identityServer.protocolType')})"
:placeholder="$t('pleaseSelectBy', {name: $t('AbpIdentityServer.Client:ProtocolType')})"
>
<el-option
key="oidc"
@ -76,7 +95,7 @@
<el-col :span="6">
<el-form-item
prop="requireClientSecret"
:label="$t('identityServer.requireClientSecret')"
:label="$t('AbpIdentityServer.Client:RequiredClientSecret')"
label-width="120px"
>
<el-switch
@ -87,7 +106,7 @@
<el-col :span="6">
<el-form-item
prop="requirePkce"
:label="$t('identityServer.requirePkce')"
:label="$t('AbpIdentityServer.Client:RequiredPkce')"
>
<el-switch
v-model="client.requirePkce"
@ -97,7 +116,7 @@
<el-col :span="6">
<el-form-item
prop="allowPlainTextPkce"
:label="$t('identityServer.allowPlainTextPkce')"
:label="$t('AbpIdentityServer.Client:AllowedPlainTextPkce')"
label-width="180px"
>
<el-switch
@ -110,7 +129,7 @@
<el-col :span="6">
<el-form-item
prop="allowOfflineAccess"
:label="$t('identityServer.allowOfflineAccess')"
:label="$t('AbpIdentityServer.Client:AllowedOfflineAccess')"
>
<el-switch
v-model="client.allowOfflineAccess"
@ -120,7 +139,7 @@
<el-col :span="6">
<el-form-item
prop="allowAccessTokensViaBrowser"
:label="$t('identityServer.allowAccessTokensViaBrowser')"
:label="$t('AbpIdentityServer.Client:AllowedAccessTokensViaBrowser')"
label-width="180px"
>
<el-switch
@ -131,38 +150,76 @@
</el-row>
<el-form-item
prop="allowedScopes"
:label="$t('identityServer.allowedScopes')"
:label="$t('AbpIdentityServer.Client:AllowedScopes')"
>
<el-input-tag-ex
<el-select
v-model="client.allowedScopes"
label="scope"
/>
multiple
filterable
allow-create
clearable
style="width: 100%;"
>
<el-option-group :label="$t('AbpIdentityServer.Resource:Api')">
<el-option
v-for="resource in apiResources"
:key="resource"
:label="resource"
:value="resource"
/>
</el-option-group>
<el-option-group :label="$t('AbpIdentityServer.Resource:Identity')">
<el-option
v-for="resource in identityResources"
:key="resource"
:label="resource"
:value="resource"
/>
</el-option-group>
</el-select>
</el-form-item>
<el-form-item
prop="redirectUris"
:label="$t('identityServer.redirectUris')"
:label="$t('AbpIdentityServer.Client:RedirectUris')"
>
<el-input-tag-ex
<el-select
v-model="client.redirectUris"
label="redirectUri"
validate="url"
multiple
filterable
allow-create
clearable
style="width: 100%;"
/>
</el-form-item>
<el-form-item
prop="allowedGrantTypes"
:label="$t('identityServer.allowedGrantTypes')"
:label="$t('AbpIdentityServer.Client:AllowedGrantTypes')"
label-width="120px"
>
<el-input-tag-ex
<el-select
v-model="client.allowedGrantTypes"
label="grantType"
/>
multiple
filterable
allow-create
clearable
class="full-select"
>
<el-option
v-for="(grantType, index) in supportedGrantypes"
:key="index"
:label="grantType"
:value="grantType"
/>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="认证/注销">
<el-tab-pane
name="authentication"
:label="$t('AbpIdentityServer.Authentication')"
>
<el-form-item
prop="enableLocalLogin"
:label="$t('identityServer.enableLocalLogin')"
:label="$t('AbpIdentityServer.Client:EnableLocalLogin')"
>
<el-switch
v-model="client.enableLocalLogin"
@ -172,7 +229,7 @@
<el-col :span="8">
<el-form-item
prop="frontChannelLogoutSessionRequired"
:label="$t('identityServer.frontChannelLogoutSessionRequired')"
:label="$t('AbpIdentityServer.Client:FrontChannelLogoutSessionRequired')"
label-width="160px"
>
<el-switch
@ -183,7 +240,7 @@
<el-col :span="16">
<el-form-item
prop="frontChannelLogoutUri"
:label="$t('identityServer.frontChannelLogoutUri')"
:label="$t('AbpIdentityServer.Client:FrontChannelLogoutUri')"
label-width="140px"
>
<el-input
@ -197,7 +254,7 @@
<el-col :span="8">
<el-form-item
prop="backChannelLogoutSessionRequired"
:label="$t('identityServer.backChannelLogoutSessionRequired')"
:label="$t('AbpIdentityServer.Client:BackChannelLogoutSessionRequired')"
label-width="160px"
>
<el-switch
@ -208,7 +265,7 @@
<el-col :span="16">
<el-form-item
prop="backChannelLogoutUri"
:label="$t('identityServer.backChannelLogoutUri')"
:label="$t('AbpIdentityServer.Client:BackChannelLogoutUri')"
label-width="140px"
>
<el-input
@ -220,28 +277,35 @@
</el-row>
<el-form-item
prop="postLogoutRedirectUris"
:label="$t('identityServer.postLogoutRedirectUris')"
:label="$t('AbpIdentityServer.Client:PostLogoutRedirectUris')"
label-width="140px"
>
<el-input-tag-ex
<el-select
v-model="client.postLogoutRedirectUris"
label="postLogoutRedirectUri"
validate="url"
multiple
filterable
allow-create
clearable
style="width: 100%;"
/>
</el-form-item>
<el-form-item
prop="identityProviderRestrictions"
:label="$t('identityServer.identityProviderRestrictions')"
:label="$t('AbpIdentityServer.Client:IdentityProviderRestrictions')"
label-width="140px"
>
<el-input-tag-ex
<el-select
v-model="client.identityProviderRestrictions"
label="provider"
multiple
filterable
allow-create
clearable
style="width: 100%;"
/>
</el-form-item>
<el-form-item
prop="userSsoLifetime"
:label="$t('identityServer.userSsoLifetime')"
:label="$t('AbpIdentityServer.Client:UserSsoLifetime')"
label-width="140px"
>
<el-input
@ -250,11 +314,19 @@
/>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="令牌">
<el-tab-pane
name="token"
:label="$t('AbpIdentityServer.Token')"
>
<el-form-item
prop="identityTokenLifetime"
:label="$t('identityServer.identityTokenLifetime')"
:label="$t('AbpIdentityServer.Client:IdentityTokenLifetime')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:IdentityTokenLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.identityTokenLifetime"
@ -263,8 +335,13 @@
</el-form-item>
<el-form-item
prop="accessTokenLifetime"
:label="$t('identityServer.accessTokenLifetime')"
:label="$t('AbpIdentityServer.Client:AccessTokenLifetime')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:AccessTokenLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.accessTokenLifetime"
@ -273,13 +350,18 @@
</el-form-item>
<el-form-item
prop="accessTokenType"
:label="$t('identityServer.accessTokenType')"
:label="$t('AbpIdentityServer.Client:AccessTokenType')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseSelectBy', {key: $t('AbpIdentityServer.Client:AccessTokenType')}),
trigger: 'blur'
}"
>
<el-select
v-model="client.accessTokenType"
class="full-select"
:placeholder="$t('pleaseSelectBy', {name: $t('identityServer.accessTokenType')})"
:placeholder="$t('pleaseSelectBy', {name: $t('AbpIdentityServer.Client:AccessTokenType')})"
>
<el-option
:key="0"
@ -295,8 +377,13 @@
</el-form-item>
<el-form-item
prop="authorizationCodeLifetime"
:label="$t('identityServer.authorizationCodeLifetime')"
:label="$t('AbpIdentityServer.Client:AuthorizationCodeLifetime')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:AuthorizationCodeLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.authorizationCodeLifetime"
@ -305,8 +392,13 @@
</el-form-item>
<el-form-item
prop="absoluteRefreshTokenLifetime"
:label="$t('identityServer.absoluteRefreshTokenLifetime')"
:label="$t('AbpIdentityServer.Client:AbsoluteRefreshTokenLifetime')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:AbsoluteRefreshTokenLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.absoluteRefreshTokenLifetime"
@ -315,8 +407,13 @@
</el-form-item>
<el-form-item
prop="slidingRefreshTokenLifetime"
:label="$t('identityServer.slidingRefreshTokenLifetime')"
:label="$t('AbpIdentityServer.Client:SlidingRefreshTokenLifetime')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:SlidingRefreshTokenLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.slidingRefreshTokenLifetime"
@ -325,13 +422,18 @@
</el-form-item>
<el-form-item
prop="refreshTokenUsage"
:label="$t('identityServer.refreshTokenUsage')"
:label="$t('AbpIdentityServer.Client:RefreshTokenUsage')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseSelectBy', {key: $t('AbpIdentityServer.Client:RefreshTokenUsage')}),
trigger: 'blur'
}"
>
<el-select
v-model="client.refreshTokenUsage"
class="full-select"
:placeholder="$t('pleaseSelectBy', {name: $t('identityServer.refreshTokenUsage')})"
:placeholder="$t('pleaseSelectBy', {name: $t('AbpIdentityServer.Client:RefreshTokenUsage')})"
>
<el-option
:key="0"
@ -347,13 +449,18 @@
</el-form-item>
<el-form-item
prop="refreshTokenExpiration"
:label="$t('identityServer.refreshTokenExpiration')"
:label="$t('AbpIdentityServer.Client:RefreshTokenExpiration')"
label-width="165px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:RefreshTokenExpiration')}),
trigger: 'blur'
}"
>
<el-select
v-model="client.refreshTokenExpiration"
class="full-select"
:placeholder="$t('pleaseSelectBy', {name: $t('identityServer.refreshTokenExpiration')})"
:placeholder="$t('pleaseSelectBy', {name: $t('AbpIdentityServer.Client:RefreshTokenExpiration')})"
>
<el-option
:key="0"
@ -369,20 +476,30 @@
</el-form-item>
<el-form-item
prop="allowedCorsOrigins"
:label="$t('identityServer.allowedCorsOrigins')"
:label="$t('AbpIdentityServer.Client:AllowedCorsOrigins')"
label-width="165px"
>
<el-input-tag-ex
<el-select
v-model="client.allowedCorsOrigins"
label="origin"
validate="url"
/>
multiple
filterable
allow-create
clearable
style="width: 100%;"
>
<el-option
v-for="(corsOrigin, index) in distinctAllowedCorsOrigins"
:key="index"
:label="corsOrigin"
:value="corsOrigin"
/>
</el-select>
</el-form-item>
<el-row>
<el-col :span="8">
<el-form-item
prop="updateAccessTokenClaimsOnRefresh"
:label="$t('identityServer.updateAccessTokenClaimsOnRefresh')"
:label="$t('AbpIdentityServer.Client:UpdateAccessTokenClaimsOnRefresh')"
label-width="170px"
>
<el-switch
@ -393,7 +510,7 @@
<el-col :span="8">
<el-form-item
prop="includeJwtId"
:label="$t('identityServer.includeJwtId')"
:label="$t('AbpIdentityServer.Client:IncludeJwtId')"
>
<el-switch
v-model="client.includeJwtId"
@ -405,7 +522,7 @@
<el-col :span="8">
<el-form-item
prop="alwaysSendClientClaims"
:label="$t('identityServer.alwaysSendClientClaims')"
:label="$t('AbpIdentityServer.Client:AlwaysSendClientClaims')"
label-width="170px"
>
<el-switch
@ -416,7 +533,7 @@
<el-col :span="8">
<el-form-item
prop="alwaysIncludeUserClaimsInIdToken"
:label="$t('identityServer.alwaysIncludeUserClaimsInIdToken')"
:label="$t('AbpIdentityServer.Client:AlwaysIncludeUserClaimsInIdToken')"
label-width="210px"
>
<el-switch
@ -427,7 +544,7 @@
</el-row>
<el-form-item
prop="clientClaimsPrefix"
:label="$t('identityServer.clientClaimsPrefix')"
:label="$t('AbpIdentityServer.Client:ClientClaimsPrefix')"
label-width="165px"
>
<el-input
@ -436,7 +553,7 @@
</el-form-item>
<el-form-item
prop="pairWiseSubjectSalt"
:label="$t('identityServer.pairWiseSubjectSalt')"
:label="$t('AbpIdentityServer.Client:PairWiseSubjectSalt')"
label-width="165px"
>
<el-input
@ -444,12 +561,15 @@
/>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="同意屏幕">
<el-tab-pane
name="consent"
:label="$t('AbpIdentityServer.Consent')"
>
<el-row>
<el-col :span="6">
<el-form-item
prop="requireConsent"
:label="$t('identityServer.requireConsent')"
:label="$t('AbpIdentityServer.Client:RequireConsent')"
>
<el-switch
v-model="client.requireConsent"
@ -459,7 +579,7 @@
<el-col :span="6">
<el-form-item
prop="allowRememberConsent"
:label="$t('identityServer.allowRememberConsent')"
:label="$t('AbpIdentityServer.Client:AllowRememberConsent')"
>
<el-switch
v-model="client.allowRememberConsent"
@ -469,7 +589,7 @@
</el-row>
<el-form-item
prop="clientUri"
:label="$t('identityServer.clientUri')"
:label="$t('AbpIdentityServer.Client:ClientUri')"
>
<el-input
v-model="client.clientUri"
@ -477,17 +597,20 @@
</el-form-item>
<el-form-item
prop="logoUri"
:label="$t('identityServer.logoUri')"
:label="$t('AbpIdentityServer.Client:LogoUri')"
>
<el-input
v-model="client.logoUri"
/>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="设备流程">
<el-tab-pane
name="deviceFlow"
:label="$t('AbpIdentityServer.DeviceFlow')"
>
<el-form-item
prop="userCodeType"
:label="$t('identityServer.userCodeType')"
:label="$t('AbpIdentityServer.Client:UserCodeType')"
label-width="155px"
>
<el-input
@ -496,8 +619,13 @@
</el-form-item>
<el-form-item
prop="deviceCodeLifetime"
:label="$t('identityServer.deviceCodeLifetime')"
:label="$t('AbpIdentityServer.Client:DeviceCodeLifetime')"
label-width="155px"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Client:DeviceCodeLifetime')}),
trigger: 'blur'
}"
>
<el-input
v-model="client.deviceCodeLifetime"
@ -505,23 +633,60 @@
/>
</el-form-item>
</el-tab-pane>
<el-tab-pane
name="avanced"
>
<el-dropdown
slot="label"
@command="onDropdownMenuItemChanged"
>
<span class="el-dropdown-link">
{{ $t('AbpIdentityServer.Advanced') }}<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="secret-edit-form">
{{ $t('AbpIdentityServer.Secret') }}
</el-dropdown-item>
<el-dropdown-item command="client-claim-edit-form">
{{ $t('AbpIdentityServer.Claims') }}
</el-dropdown-item>
<el-dropdown-item command="properties-edit-form">
{{ $t('AbpIdentityServer.Propertites') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<component
:is="advancedComponent"
:secrets="client.clientSecrets"
:client-claims="client.claims"
:properties="client.properties"
:allowed-create="checkPermission(['AbpIdentityServer.Clients.ManageProperties'])"
:allowed-delete="checkPermission(['AbpIdentityServer.Clients.ManageProperties'])"
@onSecretCreated="onClientSecretCreated"
@onSecretDeleted="onClientSecretDeleted"
@onClientClaimCreated="onClientClaimCreated"
@onClientClaimDeleted="onClientClaimDeleted"
@onCreated="onClientPropertyCreated"
@onDeleted="onClientPropertyDeleted"
/>
</el-tab-pane>
</el-tabs>
<el-form-item>
<el-button
class="cancel"
style="width:100px"
@click="onCancel"
type="info"
@click="onFormClosed"
>
{{ $t('table.cancel') }}
{{ $t('AbpIdentityServer.Cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
style="width:100px"
@click="onSavedEditClient"
icon="el-icon-check"
@click="onSave"
>
{{ $t('table.confirm') }}
{{ $t('AbpIdentityServer.Save') }}
</el-button>
</el-form-item>
</el-form>
@ -529,100 +694,142 @@
</template>
<script lang="ts">
import ElInputTagEx from '@/components/InputTagEx/index.vue'
import { checkPermission } from '@/utils/permission'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import ClientService, { Client, ClientUpdate } from '@/api/clients'
import ClientApiService, { Client, ClientUpdate, ClientCreateOrUpdate, ClientClaim, SecretCreateOrUpdate } from '@/api/clients'
import ClientClaimEditForm from './ClientClaimEditForm.vue'
import SecretEditForm from '../../components/SecretEditForm.vue'
import PropertiesEditForm from '../../components/PropertiesEditForm.vue'
@Component({
name: 'ClientEditForm',
components: {
ElInputTagEx
SecretEditForm,
PropertiesEditForm,
ClientClaimEditForm
},
methods: {
checkPermission
}
})
export default class extends Vue {
@Prop({ default: false })
private showDialog!: boolean
@Prop({ default: '' })
private title!: string
@Prop({ default: '' })
private clientId!: string
private client: Client
private clientRules = {
clientId: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientId') }), trigger: 'blur' },
{ min: 3, max: 200, message: this.l('fieldLengthMustBeRange', { min: 3, max: 200 }), trigger: 'blur' }
],
clientName: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.clientName') }), trigger: 'blur' }
],
protocolType: [
{ required: true, message: this.l('pleaseSelectBy', { key: this.l('identityServer.protocolType') }), trigger: 'blur' }
],
identityTokenLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.identityTokenLifetime') }), trigger: 'blur' }
],
accessTokenLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.accessTokenLifetime') }), trigger: 'blur' }
],
deviceCodeLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.deviceCodeLifetime') }), trigger: 'blur' }
],
authorizationCodeLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.authorizationCodeLifetime') }), trigger: 'blur' }
],
absoluteRefreshTokenLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.absoluteRefreshTokenLifetime') }), trigger: 'blur' }
],
slidingRefreshTokenLifetime: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.slidingRefreshTokenLifetime') }), trigger: 'blur' }
],
refreshTokenUsage: [
{ required: true, message: this.l('pleaseSelectBy', { key: this.l('identityServer.refreshTokenUsage') }), trigger: 'blur' }
],
refreshTokenExpiration: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.refreshTokenExpiration') }), trigger: 'blur' }
],
accessTokenType: [
{ required: true, message: this.l('pleaseSelectBy', { key: this.l('identityServer.accessTokenType') }), trigger: 'blur' }
]
@Prop({ default: () => { return new Array<string>() } })
private supportedGrantypes!: string[]
private activeTabPane = 'basics'
private advancedComponent = 'secret-edit-form'
private apiResources = new Array<string>()
private identityResources = new Array<string>()
private distinctAllowedCorsOrigins = new Array<string>()
private client = new Client()
@Watch('showDialog', { immediate: true })
private onShowDialogChanged() {
this.handleGetClient()
}
constructor() {
super()
this.client = Client.empty()
mounted() {
this.handleGetResources()
}
@Watch('clientId', { immediate: true })
private handleClientIdChanged(id: string) {
if (id) {
ClientService.getClientById(id).then(client => {
this.client = client
private handleGetResources() {
ClientApiService.getAssignableApiResources()
.then(res => {
this.apiResources = res.items
})
ClientApiService.getAssignableIdentityResources()
.then(res => {
this.identityResources = res.items
})
ClientApiService.getAllDistinctAllowedCorsOrigins()
.then(res => {
this.distinctAllowedCorsOrigins = res.items
})
} else {
this.client = Client.empty()
}
private handleGetClient() {
if (this.showDialog && this.clientId) {
ClientApiService.getClientById(this.clientId)
.then(client => {
this.client = client
})
}
}
private onSavedEditClient() {
private onDropdownMenuItemChanged(component: any) {
this.activeTabPane = 'avanced'
this.advancedComponent = component
}
private onClientSecretCreated(secret: any) {
const clientSecret = new SecretCreateOrUpdate()
clientSecret.hashType = secret.hashType
clientSecret.type = secret.type
clientSecret.value = secret.value
clientSecret.description = secret.description
clientSecret.expiration = secret.expiration
this.client.clientSecrets.push(clientSecret)
}
private onClientSecretDeleted(type: string, value: string) {
const secretIndex = this.client.clientSecrets.findIndex(secret => secret.type === type && secret.value === value)
this.client.clientSecrets.splice(secretIndex, 1)
}
private onClientClaimCreated(type: string, value: string) {
this.client.claims.push(new ClientClaim(type, value))
}
private onClientClaimDeleted(type: string, value: string) {
const claimIndex = this.client.claims.findIndex(c => c.type === type && c.value === value)
this.client.claims.splice(claimIndex, 1)
}
private onClientPropertyCreated(key: string, value: string) {
this.$set(this.client.properties, key, value)
}
private onClientPropertyDeleted(key: string) {
this.$delete(this.client.properties, key)
}
private onSave() {
const clientEditForm = this.$refs.formClient as any
clientEditForm.validate((valid: boolean) => {
if (valid) {
const updateClient = new ClientUpdate()
updateClient.id = this.clientId
updateClient.client.setClient(this.client)
ClientService.updateClient(updateClient).then(clientDto => {
this.client = clientDto
const successMessage = this.l('identityServer.updateClientSuccess', { id: this.client.clientId })
this.$message.success(successMessage)
this.onFormClosed(true)
})
this.updateClientByInput(updateClient)
updateClient.updateByClient(this.client)
ClientApiService.updateClient(this.clientId, updateClient)
.then(client => {
this.client = client
})
}
})
}
private onFormClosed(changed: boolean) {
private updateClientByInput(client: ClientCreateOrUpdate) {
client.clientId = this.client.clientId
client.clientName = this.client.clientName
client.description = this.client.description
client.allowedGrantTypes = this.client.allowedGrantTypes
}
private onFormClosed() {
this.resetFormFields()
this.$emit('closed', changed)
this.$emit('closed')
}
private resetFormFields() {
@ -630,10 +837,6 @@ export default class extends Vue {
clientEditForm.resetFields()
}
private onCancel() {
this.onFormClosed(false)
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
@ -647,9 +850,13 @@ export default class extends Vue {
.confirm {
position: absolute;
right: 10px;
width:100px;
top: 10px;
}
.cancel {
position: absolute;
right: 120px;
width:100px;
top: 10px;
}
</style>

147
vueJs/src/views/admin/identityServer/client/components/ClientPermissionEditForm.vue

@ -1,147 +0,0 @@
<template>
<el-dialog
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.clientPermission')"
custom-class="modal-form"
:show-close="false"
@close="onFormClosed"
>
<el-form
ref="formClient"
label-width="100px"
:model="clientPermission"
label-position="top"
>
<el-form-item v-if="hasLoadPermission">
<permission-tree
ref="PermissionTree"
:expanded="false"
:readonly="!checkPermission(['IdentityServer.Clients.ManagePermissions'])"
:permission="clientPermission"
@onPermissionChanged="onPermissionChanged"
/>
</el-form-item>
<el-form-item>
<el-button
class="cancel"
style="width:100px"
@click="onCancel"
>
{{ $t('table.cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
style="width:100px"
:disabled="!checkPermission(['IdentityServer.Clients.ManagePermissions'])"
@click="onSaveClientPemissions"
>
{{ $t('table.confirm') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script lang="ts">
import { IPermission } from '@/api/types'
import { checkPermission } from '@/utils/permission'
import PermissionTree from '@/components/PermissionTree/index.vue'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import PermissionService, { PermissionDto, UpdatePermissionsDto } from '@/api/permission'
@Component({
name: 'ClientPermissionEditForm',
components: {
PermissionTree
},
methods: {
checkPermission
}
})
export default class extends Vue {
@Prop({ default: false })
private showDialog!: boolean
/** 客户端标识 */
@Prop({ default: '' })
private clientId!: string
/** 客户端权限 */
private clientPermission: PermissionDto
/** 是否已加载权限 */
private hasLoadPermission: boolean
/** 客户端权限已变更 */
private clientPermissionChanged: boolean
/** 变更客户端权限数据 */
private editClientPermissions: IPermission[]
constructor() {
super()
this.hasLoadPermission = false
this.clientPermissionChanged = false
this.clientPermission = new PermissionDto()
this.editClientPermissions = new Array<IPermission>()
}
/** 监听客户端标识变更事件,刷新客户端权限数据 */
@Watch('clientId')
private onClientIdChanged() {
this.handledGetClientPermissions()
}
mounted() {
this.handledGetClientPermissions()
}
private handledGetClientPermissions() {
if (this.showDialog && this.clientId) {
PermissionService.getPermissionsByKey('C', this.clientId).then(permission => {
this.clientPermission = permission
this.hasLoadPermission = true
})
}
}
/**
* @param permissions 变更权限数据
*/
private onPermissionChanged(permissions: IPermission[]) {
this.clientPermissionChanged = true
this.editClientPermissions = permissions
}
/** 保存客户端权限 */
private onSaveClientPemissions() {
if (this.clientPermissionChanged) {
const setClientPermissions = new UpdatePermissionsDto()
setClientPermissions.permissions = this.editClientPermissions
PermissionService.setPermissionsByKey('C', this.clientId, setClientPermissions).then(() => {
this.onFormClosed()
})
}
}
private onFormClosed() {
this.$emit('closed')
}
/** 取消操作 */
private onCancel() {
this.onFormClosed()
}
}
</script>
<style lang="scss" scoped>
.confirm {
position: absolute;
right: 10px;
}
.cancel {
position: absolute;
right: 120px;
}
</style>

199
vueJs/src/views/admin/identityServer/client/components/ClientPropertyEditForm.vue

@ -1,199 +0,0 @@
<template>
<el-dialog
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.clientProperty')"
custom-class="modal-form"
:show-close="false"
@close="onFormClosed"
>
<div class="app-container">
<el-form
ref="formClientProperty"
label-width="100px"
:model="clientProperty"
:rules="clientPropertyRules"
>
<el-form-item
prop="key"
:label="$t('identityServer.propertyKey')"
>
<el-input
v-model="clientProperty.key"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.propertyKey')})"
/>
</el-form-item>
<el-form-item
prop="value"
:label="$t('identityServer.propertyValue')"
>
<el-input
v-model="clientProperty.value"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.propertyValue')})"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
:disabled="!checkPermission(['IdentityServer.Clients.Properties.Create'])"
@click="onSaveClientProperty"
>
{{ $t('identityServer.createClientProperty') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-divider />
<el-table
row-key="key"
:data="clientProperties"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column
:label="$t('identityServer.propertyKey')"
prop="key"
sortable
width="200px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.key }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('identityServer.propertyValue')"
prop="value"
sortable
min-width="400px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('operaActions')"
align="center"
width="150px"
fixed="right"
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.Clients.Properties.Delete'])"
size="mini"
type="primary"
@click="handleDeleteClientProperty(row.key, row.value)"
>
{{ $t('identityServer.deleteProperty') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script lang="ts">
import ClientService, { ClientProperty, ClientPropertyCreate } from '@/api/clients'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { checkPermission } from '@/utils/permission'
@Component({
name: 'ClientPropertyEditForm',
methods: {
checkPermission
}
})
export default class extends Vue {
@Prop({ default: false })
private showDialog!: boolean
@Prop({ default: '' })
private clientId!: string
@Prop({ default: () => new Array<ClientProperty>() })
private clientProperties!: ClientProperty[]
private clientProperty: ClientPropertyCreate
private clientPropertyRules = {
key: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.propertyKey') }), trigger: 'change' }
],
value: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.propertyValue') }), trigger: 'blur' }
]
}
constructor() {
super()
this.clientProperty = new ClientPropertyCreate()
}
@Watch('clientId', { immediate: true })
private onClientIdChanged() {
this.clientProperty.clientId = this.clientId
}
private handleDeleteClientProperty(key: string, value: string) {
this.$confirm(this.l('identityServer.deletePropertyByType', { key: key }),
this.l('identityServer.deleteProperty'), {
callback: (action) => {
if (action === 'confirm') {
ClientService.deleteClientProperty(this.clientId, key, value).then(() => {
const deletePropertyIndex = this.clientProperties.findIndex(p => p.key === key && p.value === value)
this.clientProperties.splice(deletePropertyIndex, 1)
this.$message.success(this.l('identityServer.deletePropertySuccess', { type: value }))
this.$emit('clientPropertyChanged')
})
}
}
})
}
private onSaveClientProperty() {
const frmClientProperty = this.$refs.formClientProperty as any
frmClientProperty.validate((valid: boolean) => {
if (valid) {
this.clientProperty.clientId = this.clientId
ClientService.addClientProperty(this.clientProperty).then(property => {
this.clientProperties.push(property)
const successMessage = this.l('identityServer.createPropertySuccess', { type: this.clientProperty.key })
this.$message.success(successMessage)
this.$emit('clientPropertyChanged')
this.onFormClosed()
})
}
})
}
private onFormClosed() {
this.resetFields()
this.$emit('closed')
}
public resetFields() {
const frmClientProperty = this.$refs.formClientProperty as any
frmClientProperty.resetFields()
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>
<style lang="scss" scoped>
.full-select {
width: 100%;
}
</style>

305
vueJs/src/views/admin/identityServer/client/components/ClientSecretEditForm.vue

@ -1,305 +0,0 @@
<template>
<el-dialog
v-el-draggable-dialog
width="800px"
:visible="showDialog"
:title="$t('identityServer.clientSecret')"
custom-class="modal-form"
:show-close="false"
@close="onFormClosed"
>
<div class="app-container">
<el-form
ref="formClientSecret"
label-width="100px"
:model="clientSecret"
:rules="clientSecretRules"
>
<el-form-item
prop="type"
:label="$t('identityServer.secretType')"
>
<el-select
v-model="clientSecret.type"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('identityServer.secretType')})"
>
<el-option
key="JWK"
label="JsonWebKey"
value="JWK"
/>
<el-option
key="SharedSecret"
label="SharedSecret"
value="SharedSecret"
/>
<el-option
key="X509Name"
label="X509CertificateName"
value="X509Name"
/>
<el-option
key="X509CertificateBase64"
label="X509CertificateBase64"
value="X509CertificateBase64"
/>
<el-option
key="X509Thumbprint"
label="X509CertificateThumbprint"
value="X509Thumbprint"
/>
</el-select>
</el-form-item>
<el-form-item
prop="hashType"
:label="$t('identityServer.secretHashType')"
>
<el-popover
ref="popHashType"
placement="top-start"
trigger="hover"
:content="$t('identityServer.hashOnlySharedSecret')"
/>
<el-select
v-model="clientSecret.hashType"
v-popover:popHashType
:disabled="clientSecret.type !== 'SharedSecret'"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('identityServer.secretHashType')})"
>
<el-option
:key="0"
label="Sha256"
:value="0"
/>
<el-option
:key="1"
label="Sha512"
:value="1"
/>
</el-select>
</el-form-item>
<el-form-item
prop="value"
:label="$t('identityServer.secretValue')"
>
<el-input
v-model="clientSecret.value"
:placeholder="$t('pleaseInputBy', {key: $t('identityServer.secretValue')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('identityServer.secretDescription')"
>
<el-input
v-model="clientSecret.description"
/>
</el-form-item>
<el-form-item
prop="expiration"
:label="$t('identityServer.expiration')"
>
<el-date-picker
v-model="clientSecret.expiration"
class="full-select"
type="datetime"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
:disabled="!checkPermission(['IdentityServer.Clients.Secrets.Create'])"
@click="onSaveClientSecret"
>
{{ $t('identityServer.createSecret') }}
</el-button>
</el-form-item>
</el-form>
</div>
<el-divider />
<el-table
row-key="clientId"
:data="clientSecrets"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column
:label="$t('identityServer.secretType')"
prop="type"
sortable
width="150px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.type }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('identityServer.secretValue')"
prop="value"
sortable
width="200px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('identityServer.secretDescription')"
prop="description"
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.description }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('identityServer.expiration')"
prop="expiration"
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.expiration | dateTimeFilter }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('operaActions')"
align="center"
width="150px"
fixed="right"
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.Clients.Secrets.Delete'])"
size="mini"
type="primary"
@click="handleDeleteClientSecret(row.type, row.value)"
>
{{ $t('identityServer.deleteSecret') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script lang="ts">
import ClientService, { ClientSecret, ClientSecretCreate } from '@/api/clients'
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { dateFormat } from '@/utils/index'
import { checkPermission } from '@/utils/permission'
@Component({
name: 'ClientSecretEditForm',
filters: {
dateTimeFilter(datetime: string) {
if (datetime) {
const date = new Date(datetime)
return dateFormat(date, 'YYYY-mm-dd HH:MM')
}
return ''
}
},
methods: {
checkPermission
}
})
export default class extends Vue {
@Prop({ default: false })
private showDialog!: boolean
@Prop({ default: '' })
private clientId!: string
@Prop({ default: () => new Array<ClientSecret>() })
private clientSecrets!: ClientSecret[]
private clientSecretChanged: boolean
private clientSecret: ClientSecretCreate
private clientSecretRules = {
type: [
{ required: true, message: this.l('pleaseSelectBy', { key: this.l('identityServer.secretType') }), trigger: 'change' }
],
value: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.secretValue') }), trigger: 'blur' }
]
}
constructor() {
super()
this.clientSecretChanged = false
this.clientSecret = new ClientSecretCreate()
}
@Watch('clientId', { immediate: true })
private onClientIdChanged() {
this.clientSecret.clientId = this.clientId
}
private handleDeleteClientSecret(type: string, value: string) {
this.$confirm(this.l('identityServer.deleteSecretByType', { type: value }),
this.l('identityServer.deleteSecret'), {
callback: (action) => {
if (action === 'confirm') {
ClientService.deleteClientSecret(this.clientId, type, value).then(() => {
const deleteSecretIndex = this.clientSecrets.findIndex(secret => secret.type === type)
this.clientSecrets.splice(deleteSecretIndex, 1)
this.$message.success(this.l('identityServer.deleteSecretSuccess', { type: value }))
this.$emit('clientSecretChanged')
})
}
}
})
}
private onSaveClientSecret() {
const frmClientSecret = this.$refs.formClientSecret as any
frmClientSecret.validate((valid: boolean) => {
if (valid) {
this.clientSecret.clientId = this.clientId
ClientService.addClientSecret(this.clientSecret).then(secret => {
this.clientSecrets.push(secret)
const successMessage = this.l('identityServer.createSecretSuccess', { type: this.clientSecret.type })
this.$message.success(successMessage)
this.$emit('clientSecretChanged')
this.onFormClosed()
})
}
})
}
private onFormClosed() {
this.resetFields()
this.$emit('closed')
}
public resetFields() {
const frmClientSecret = this.$refs.formClientSecret as any
frmClientSecret.resetFields()
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>
<style lang="scss" scoped>
.full-select {
width: 100%;
}
</style>

94
vueJs/src/views/admin/identityServer/client/index.vue

@ -17,13 +17,13 @@
type="primary"
@click="refreshPagedData"
>
{{ $t('searchList') }}
{{ $t('AbpIdentityServer.Search') }}
</el-button>
<el-button
class="filter-item"
type="primary"
:disabled="!checkPermission(['IdentityServer.Clients.Create'])"
@click="handleShowCreateClientForm()"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Create'])"
@click="handleShowCreateClientDialog()"
>
{{ $t('AbpIdentityServer.Client:New') }}
</el-button>
@ -175,10 +175,10 @@
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.Clients.Update'])"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Update'])"
size="mini"
type="primary"
@click="handleShowEditClientForm(row)"
@click="handleShowEditClientDialog(row)"
>
{{ $t('AbpIdentityServer.Client:Edit') }}
</el-button>
@ -187,7 +187,7 @@
@command="handleCommand"
>
<el-button
v-permission="['IdentityServer.Clients']"
v-permission="['AbpIdentityServer.Clients']"
size="mini"
type="info"
>
@ -196,20 +196,20 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
:command="{key: 'permissions', row}"
:disabled="!checkPermission(['IdentityServer.Clients.ManagePermissions'])"
:disabled="!checkPermission(['AbpIdentityServer.Clients.ManagePermissions'])"
>
{{ $t('AbpIdentityServer.Permissions') }}
</el-dropdown-item>
<el-dropdown-item
:command="{key: 'clone', row}"
:disabled="!checkPermission(['IdentityServer.Clients.Clone'])"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Clone'])"
>
{{ $t('AbpIdentityServer.Client:Clone') }}
</el-dropdown-item>
<el-dropdown-item
divided
:command="{key: 'delete', row}"
:disabled="!checkPermission(['IdentityServer.Clients.Delete'])"
:disabled="!checkPermission(['AbpIdentityServer.Clients.Delete'])"
>
{{ $t('AbpIdentityServer.Client:Delete') }}
</el-dropdown-item>
@ -228,12 +228,32 @@
@sort-change="handleSortChange"
/>
<client-clone-form
:client-id="editClient.id"
:show-dialog="showCloneClientDialog"
@closed="onCloneClientDialogClosed"
/>
<permission-form
provider-name="C"
:provider-key="editClientId"
:provider-key="editClient.clientId"
:show-dialog="showEditClientPermissionDialog"
:readonly="!checkPermission(['IdentityServer.Clients.ManagePermissions'])"
@closed="handleClientPermissionEditFormClosed"
@closed="onPermissionDialogClosed"
/>
<client-create-form
:supported-grantypes="supportedGrantypes"
:show-dialog="showCreateClientDialog"
@closed="onCreateClientDialogClosed"
/>
<client-edit-form
:supported-grantypes="supportedGrantypes"
:client-id="editClient.id"
:title="editClientTitle"
:show-dialog="showEditClientDialog"
@closed="onEditClientDialogClosed"
/>
</div>
</template>
@ -241,18 +261,26 @@
<script lang="ts">
import { abpPagerFormat } from '@/utils/index'
import { checkPermission } from '@/utils/permission'
import DataListMiXin from '@/mixins/DataListMiXin'
import Component, { mixins } from 'vue-class-component'
import Pagination from '@/components/Pagination/index.vue'
import ClientService, { Client, ClientGetByPaged } from '@/api/clients'
import ClientCloneForm from './components/ClientCloneForm.vue'
import PermissionForm from '@/components/PermissionForm/index.vue'
import ClientCreateForm from './components/ClientCreateForm.vue'
import ClientEditForm from './components/ClientEditForm.vue'
import IdentityServer4Service from '@/api/identity-server4'
import ClientService, { Client, ClientGetByPaged } from '@/api/clients'
@Component({
name: 'IdentityServerClient',
components: {
Pagination,
PermissionForm
PermissionForm,
ClientEditForm,
ClientCloneForm,
ClientCreateForm
},
methods: {
checkPermission,
@ -270,18 +298,20 @@ import PermissionForm from '@/components/PermissionForm/index.vue'
}
})
export default class extends mixins(DataListMiXin) {
private editClientId = ''
private editClient = new Client()
private editClientTitle = ''
private showEditClientDialog = false
private showCloneClientDialog = false
private showCreateClientDialog = false
private showCloneClientDialog = false
private showEditClientPermissionDialog = false
public dataFilter = new ClientGetByPaged()
private supportedGrantypes = new Array<string>()
mounted() {
this.refreshPagedData()
this.handleGetOpenIdConfiguration()
}
protected processDataFilter() {
@ -292,38 +322,42 @@ export default class extends mixins(DataListMiXin) {
return ClientService.getClients(filter)
}
private handleShowCreateClientForm() {
this.editClientTitle = this.l('AbpIdentityServer.Client:New')
private handleGetOpenIdConfiguration() {
IdentityServer4Service.getOpenIdConfiguration()
.then(res => {
this.supportedGrantypes = res.grant_types_supported
})
}
private handleShowCreateClientDialog() {
this.showCreateClientDialog = true
}
private handleShowEditClientForm(client: Client) {
this.editClientId = client.clientId
private handleShowEditClientDialog(client: Client) {
this.editClient = client
this.editClientTitle = this.l('AbpIdentityServer.Client:Name', { 0: this.editClient.clientName })
this.showEditClientDialog = true
}
private handleClientCreateFormClosed(changed: boolean) {
this.showCreateClientDialog = false
if (changed) {
this.refreshPagedData()
}
private onEditClientDialogClosed() {
this.showEditClientDialog = false
}
private handleClientCloneFormClosed(changed: boolean) {
private onCloneClientDialogClosed(changed: boolean) {
this.showCloneClientDialog = false
if (changed) {
this.refreshPagedData()
}
}
private handleClientEditFormClosed(changed: boolean) {
this.showEditClientDialog = false
private onCreateClientDialogClosed(changed: boolean) {
this.showCreateClientDialog = false
if (changed) {
this.refreshPagedData()
}
}
private handleClientPermissionEditFormClosed() {
private onPermissionDialogClosed() {
this.showEditClientPermissionDialog = false
}
@ -342,7 +376,7 @@ export default class extends mixins(DataListMiXin) {
}
private handleCommand(command: {key: string, row: Client}) {
this.editClientId = command.row.clientId
this.editClient = command.row
switch (command.key) {
case 'clone' :
this.showCloneClientDialog = true

169
vueJs/src/views/admin/identityServer/components/PropertiesEditForm.vue

@ -0,0 +1,169 @@
<template>
<div>
<el-form
v-if="allowedCreate"
ref="propertyEditForm"
label-width="100px"
:model="property"
>
<el-form-item
prop="key"
:label="$t('AbpIdentityServer.Propertites:Key')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Propertites:Key')}),
trigger: 'blur'
}"
>
<el-input
v-model="property.key"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Propertites:Key')})"
/>
</el-form-item>
<el-form-item
prop="value"
:label="$t('AbpIdentityServer.Propertites:Value')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Propertites:Value')}),
trigger: 'blur'
}"
>
<el-input
v-model="property.value"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Propertites:Value')})"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
@click="onSave"
>
{{ $t('AbpIdentityServer.Propertites:New') }}
</el-button>
</el-form-item>
</el-form>
<el-table
row-key="key"
:data="editProperties"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column
:label="$t('AbpIdentityServer.Propertites:Key')"
prop="key"
sortable
width="200px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.key }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Propertites:Value')"
prop="value"
sortable
min-width="300px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Actions')"
align="center"
width="80px"
>
<template slot-scope="{row}">
<el-button
size="mini"
type="danger"
:disabled="!allowedDelete"
icon="el-icon-delete"
@click="onDelete(row.key)"
/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { Form } from 'element-ui'
class Property {
key = ''
value = ''
constructor(
key: string,
value: string
) {
this.key = key
this.value = value
}
}
@Component({
name: 'PropertiesEditForm'
})
export default class PropertiesEditForm extends Vue {
@Prop({ default: () => { return {} } })
private properties!: {[key: string]: string}
@Prop({ default: false })
private allowedCreate!: boolean
@Prop({ default: false })
private allowedDelete!: boolean
private editProperties = new Array<Property>()
private property = new Property('', '')
@Watch('properties', { immediate: true, deep: true })
private onPropertiesChanged() {
this.editProperties = new Array<Property>()
Object.keys(this.properties)
.forEach(key => {
this.editProperties.push(new Property(key, this.properties[key]))
})
}
private validateInput() {
return !this.editProperties.some(prop => prop.key === this.property.key)
}
private onSave() {
const propertyEditForm = this.$refs.propertyEditForm as Form
propertyEditForm.validate(valid => {
if (valid) {
if (this.validateInput()) {
this.$emit('onCreated', this.property.key, this.property.value)
propertyEditForm.resetFields()
} else {
this.$message.warning(this.l('AbpIdentityServer.Propertites:DuplicateKey'))
}
}
})
}
private onDelete(key: string) {
this.$emit('onDeleted', key)
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>

190
vueJs/src/views/admin/identityServer/components/ScopeInput.vue

@ -1,190 +0,0 @@
<template>
<div style="width:auto; height:auto;">
<div
class="el-input-tag input-tag-wrapper"
:class="[size ? 'el-input-tag--' + size : '']"
@click="foucusTagInput"
>
<span v-if="scopes.length>0">
<el-tag
v-for="(scope, idx) in scopes"
:key="idx"
:size="size"
:closable="!readOnly"
:disable-transitions="false"
@close="remove(idx)"
>
{{ scope[key] }}
</el-tag>
</span>
<input
v-if="!readOnly"
v-model="newTag"
:size="size"
:class="[size ? 'tag-input--' + size : 'tag-input']"
@keydown="addNew"
@blur="addNew"
>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import { AppModule } from '@/store/modules/app'
import { IScope } from '@/api/types'
@Component({
name: 'ScopeInput'
})
export default class extends Vue {
@Prop({ default: () => new Array<IScope>() })
private scopes!: IScope[]
@Prop({ default: 'scope' })
private key!: string
@Prop({ default: () => [13, 188, 9] })
private addTagOnKeys!: Array<number>
@Prop({ default: false })
private readOnly!: boolean
private size = AppModule.size
public newTag: string
constructor() {
super()
this.newTag = ''
}
private foucusTagInput() {
const tagInputClass = this.size ? '.tag-input--' + this.size : '.tag-input'
if (this.readOnly || !this.$el.querySelector(tagInputClass)) {
} else {
const tagInput = this.$el.querySelector(tagInputClass) as any
tagInput.focus()
}
}
private addNew(e: any) {
if (e && (!this.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) {
return false
}
if (e) {
e.stopPropagation()
e.preventDefault()
}
let addSuucess = false
if (this.newTag.includes(',')) {
this.newTag.split(',').forEach(item => {
if (this.addTag(item.trim())) {
addSuucess = true
}
})
} else {
if (this.addTag(this.newTag.trim())) {
addSuucess = true
}
}
if (addSuucess) {
this.tagChange()
this.newTag = ''
}
}
private addTag(tag: any) {
if (!tag) {
return false
}
tag = tag.trim()
if (!this.scopes.every(scope => scope.scope === tag)) {
this.scopes.push({ scope: tag })
return true
}
return false
}
private remove(index: number) {
this.scopes.splice(index, 1)
this.tagChange()
}
private removeLastTa() {
if (this.newTag) {
return false
}
this.scopes.pop()
this.tagChange()
}
private tagChange() {
this.$emit('input', this.scopes)
}
}
</script>
<style lang="scss" scoped>
.input-tag-wrapper {
position: relative;
font-size: 14px;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: #606266;
display: inline-block;
outline: none;
padding: 0 5px 0 5px;
transition: border-color .2s cubic-bezier(.645,.045,.355,1);
width: 100%;
height: 100%;
}
.el-tag {
margin-right: 4px;
margin-top: 4px;
}
.tag-input--default {
background: transparent;
border: 0;
font-size: 14px;
height: 38px;
outline: none;
padding-left: 0;
width: 150px;
}
.tag-input--mini{
background: transparent;
border: 0;
font-size: 14px;
height: 26px;
outline: none;
padding-left: 0;
width: 150px;
}
.tag-input--small{
background: transparent;
border: 0;
font-size: 14px;
height: 30px;
outline: none;
padding-left: 0;
width: 150px;
}
.tag-input--medium{
background: transparent;
border: 0;
font-size: 14px;
height: 34px;
outline: none;
padding-left: 0;
width: 150px;
}
</style>

257
vueJs/src/views/admin/identityServer/components/SecretEditForm.vue

@ -0,0 +1,257 @@
<template>
<div>
<el-form
v-if="allowedCreateSecret"
ref="secretEditForm"
:model="secret"
label-width="80px"
>
<el-form-item
prop="type"
:label="$t('AbpIdentityServer.Secret:Type')"
:rules="{
required: true,
message: $t('pleaseSelectBy', {key: $t('AbpIdentityServer.Secret:Type')}),
trigger: 'blur'
}"
>
<el-select
v-model="secret.type"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('AbpIdentityServer.Secret:Type')})"
>
<el-option
key="JWK"
label="JsonWebKey"
value="JWK"
/>
<el-option
key="SharedSecret"
label="SharedSecret"
value="SharedSecret"
/>
<el-option
key="X509Name"
label="X509CertificateName"
value="X509Name"
/>
<el-option
key="X509CertificateBase64"
label="X509CertificateBase64"
value="X509CertificateBase64"
/>
<el-option
key="X509Thumbprint"
label="X509CertificateThumbprint"
value="X509Thumbprint"
/>
</el-select>
</el-form-item>
<el-form-item
prop="hashType"
:label="$t('AbpIdentityServer.Secret:HashType')"
>
<el-popover
ref="popHashType"
placement="top-start"
trigger="hover"
:content="$t('AbpIdentityServer.Secret:HashTypeOnlySharedSecret')"
/>
<el-select
v-model="secret.hashType"
v-popover:popHashType
:disabled="secret.type !== 'SharedSecret'"
class="full-select"
:placeholder="$t('pleaseSelectBy', {key: $t('AbpIdentityServer.Secret:HashType')})"
>
<el-option
:key="0"
label="Sha256"
:value="0"
/>
<el-option
:key="1"
label="Sha512"
:value="1"
/>
</el-select>
</el-form-item>
<el-form-item
prop="value"
:label="$t('AbpIdentityServer.Secret:Value')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Secret:Value')}),
trigger: 'blur'
}"
>
<el-input
v-model="secret.value"
:placeholder="$t('pleaseInputBy', {key: $t('AbpIdentityServer.Secret:Value')})"
/>
</el-form-item>
<el-form-item
prop="description"
:label="$t('AbpIdentityServer.Description')"
>
<el-input
v-model="secret.description"
/>
</el-form-item>
<el-form-item
prop="expiration"
:label="$t('AbpIdentityServer.Expiration')"
>
<el-date-picker
v-model="secret.expiration"
class="full-select"
type="datetime"
/>
</el-form-item>
<el-form-item
style="text-align: center;"
label-width="0px"
>
<el-button
type="primary"
style="width:180px"
@click="onSave"
>
{{ $t('AbpIdentityServer.Secret:New') }}
</el-button>
</el-form-item>
</el-form>
<el-table
row-key="value"
:data="secrets"
border
fit
highlight-current-row
style="width: 100%;"
>
<el-table-column
:label="$t('AbpIdentityServer.Secret:Type')"
prop="type"
sortable
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.type }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Secret:Value')"
prop="value"
sortable
width="200px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.value }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Description')"
prop="description"
width="120px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.description }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Expiration')"
prop="expiration"
width="170px"
align="center"
>
<template slot-scope="{row}">
<span>{{ row.expiration | dateTimeFilter }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('AbpIdentityServer.Actions')"
align="center"
width="80px"
fixed="right"
>
<template slot-scope="{row}">
<el-button
:disabled="!allowedDeleteSecret"
type="danger"
icon="el-icon-delete"
size="mini"
@click="handleDeleteApiSecret(row.type, row.value)"
/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
import { ISecret, HashType } from '@/api/types'
import { Component, Vue, Prop } from 'vue-property-decorator'
import { dateFormat } from '@/utils/index'
import { Form } from 'element-ui'
class Secret implements ISecret {
type = ''
value = ''
hashType = HashType.Sha256
description = ''
expiration: Date | undefined = undefined
}
@Component({
name: 'SecretEditForm',
filters: {
dateTimeFilter(datetime: string) {
if (datetime) {
const date = new Date(datetime)
return dateFormat(date, 'YYYY-mm-dd HH:MM:SS')
}
return ''
}
}
})
export default class SecretEditForm extends Vue {
@Prop({ default: () => { return new Array<ISecret>() } })
private secrets!: ISecret[]
@Prop({ default: false })
private allowedCreateSecret!: boolean
@Prop({ default: false })
private allowedDeleteSecret!: boolean
private secret = new Secret()
private onSave() {
const secretEditForm = this.$refs.secretEditForm as Form
secretEditForm.validate(valid => {
if (valid) {
this.$emit('onSecretCreated', this.secret)
secretEditForm.resetFields()
}
})
}
private handleDeleteApiSecret(type: string, value: string) {
this.$emit('onSecretDeleted', type, value)
}
private l(name: string, values?: any[] | { [key: string]: any }) {
return this.$t(name, values).toString()
}
}
</script>
<style lang="scss" scoped>
.full-select {
width: 100%;
}
</style>

62
vueJs/src/views/admin/identityServer/identity-resources/components/IdentityResourceCreateOrEditForm.vue

@ -15,15 +15,14 @@
ref="formIdentityResource"
label-width="130px"
:model="identityResource"
:rules="identityResourceRules"
>
<el-tabs
v-model="activeTable"
type="border-card"
>
<el-tab-pane
name="information"
:label="$t('AbpIdentityServer.Information')"
name="basics"
:label="$t('AbpIdentityServer.Basics')"
>
<el-form-item
prop="enabled"
@ -36,6 +35,11 @@
<el-form-item
prop="name"
:label="$t('AbpIdentityServer.Name')"
:rules="{
required: true,
message: $t('pleaseInputBy', {key: $t('AbpIdentityServer.Name')}),
trigger: 'blur'
}"
>
<el-input
v-model="identityResource.name"
@ -99,23 +103,36 @@
:titles="[$t('AbpIdentityServer.NoClaim'), $t('AbpIdentityServer.ExistsClaim')]"
/>
</el-tab-pane>
<el-tab-pane
v-if="isEdit"
name="properties"
:label="$t('AbpIdentityServer.Propertites')"
>
<properties-edit-form
:properties="identityResource.properties"
:allowed-create="checkPermission(['AbpIdentityServer.IdentityResources.ManageProperties'])"
:allowed-delete="checkPermission(['AbpIdentityServer.IdentityResources.ManageProperties'])"
@onCreated="onPropertyCreated"
@onDeleted="onPropertyDeleted"
/>
</el-tab-pane>
</el-tabs>
<el-form-item>
<el-button
class="cancel"
style="width:100px"
type="info"
@click="onFormClosed(false)"
>
{{ $t('global.cancel') }}
{{ $t('AbpIdentityServer.Cancel') }}
</el-button>
<el-button
class="confirm"
type="primary"
style="width:100px"
icon="el-icon-check"
@click="onSave"
>
{{ $t('global.confirm') }}
{{ $t('AbpIdentityServer.Save') }}
</el-button>
</el-form-item>
</el-form>
@ -124,15 +141,25 @@
</template>
<script lang="ts">
import { checkPermission } from '@/utils/permission'
import { Form } from 'element-ui'
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
import { Claim } from '@/api/types'
import ClaimTypeApiService from '@/api/cliam-type'
import PropertiesEditForm from '../../components/PropertiesEditForm.vue'
import IdentityResourceService, { IdentityResource, IdentityResourceCreateOrUpdate } from '@/api/identity-resources'
@Component({
name: 'IdentityResourceCreateOrEditForm'
name: 'IdentityResourceCreateOrEditForm',
components: {
PropertiesEditForm
},
methods: {
checkPermission
}
})
export default class extends Vue {
@Prop({ default: false })
@ -144,14 +171,9 @@ export default class extends Vue {
@Prop({ default: '' })
private identityResourceId!: string
private activeTable = 'information'
private activeTable = 'basics'
private identityClaims = new Array<Claim>()
private identityResource = new IdentityResource()
private identityResourceRules = {
name: [
{ required: true, message: this.l('pleaseInputBy', { key: this.l('identityServer.resourceName') }), trigger: 'blur' }
]
}
get isEdit() {
if (this.identityResource.id) {
@ -179,7 +201,7 @@ export default class extends Vue {
}
private handleGetIdentityResource() {
this.activeTable = 'information'
this.activeTable = 'basics'
if (this.showDialog && this.identityResourceId) {
IdentityResourceService.getIdentityResourceById(this.identityResourceId).then(resource => {
this.identityResource = resource
@ -189,6 +211,14 @@ export default class extends Vue {
}
}
private onPropertyCreated(key: string, value: string) {
this.$set(this.identityResource.properties, key, value)
}
private onPropertyDeleted(key: string) {
this.$delete(this.identityResource.properties, key)
}
private onSave() {
const frmIdentityResource = this.$refs.formIdentityResource as any
frmIdentityResource.validate((valid: boolean) => {
@ -247,11 +277,13 @@ export default class extends Vue {
position: absolute;
right: 10px;
top: 20px;
width:100px;
}
.cancel {
position: absolute;
right: 120px;
top: 20px;
width:100px;
}
.transfer-scope ::v-deep .el-transfer-panel{
width: 250px;

12
vueJs/src/views/admin/identityServer/identity-resources/index.vue

@ -17,13 +17,13 @@
type="primary"
@click="refreshPagedData"
>
{{ $t('global.searchList') }}
{{ $t('AbpIdentityServer.Search') }}
</el-button>
<el-button
class="filter-item"
type="primary"
:disabled="!checkPermission(['IdentityServer.IdentityResources.Create'])"
@click="handleShowEditIdentityResourceForm"
:disabled="!checkPermission(['AbpIdentityServer.IdentityResources.Create'])"
@click="handleShowEditIdentityResourceForm()"
>
{{ $t('AbpIdentityServer.Resource:New') }}
</el-button>
@ -115,13 +115,13 @@
</template>
</el-table-column>
<el-table-column
:label="$t('global.operaActions')"
:label="$t('AbpIdentityServer.Actions')"
align="center"
width="250px"
>
<template slot-scope="{row}">
<el-button
:disabled="!checkPermission(['IdentityServer.IdentityResources.Update'])"
:disabled="!checkPermission(['AbpIdentityServer.IdentityResources.Update'])"
size="mini"
type="primary"
@click="handleShowEditIdentityResourceForm(row)"
@ -129,7 +129,7 @@
{{ $t('AbpIdentityServer.Resource:Edit') }}
</el-button>
<el-button
:disabled="!checkPermission(['IdentityServer.IdentityResources.Delete'])"
:disabled="!checkPermission(['AbpIdentityServer.IdentityResources.Delete'])"
size="mini"
type="danger"
@click="handleDeleteIdentityResource(row)"

Loading…
Cancel
Save