49 changed files with 1827 additions and 2159 deletions
@ -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; |
using Volo.Abp.IdentityServer.Localization; |
||||
|
|
||||
namespace LINGYUN.Abp.IdentityServer |
namespace LINGYUN.Abp.IdentityServer |
||||
{ |
{ |
||||
public abstract class AbpIdentityServerAppServiceBase : ApplicationService |
public abstract class AbpIdentityServerAppServiceBase : ApplicationService |
||||
{ |
{ |
||||
|
private IPermissionChecker _permissionChecker; |
||||
|
protected IPermissionChecker PermissionChecker => LazyGetRequiredService(ref _permissionChecker); |
||||
protected AbpIdentityServerAppServiceBase() |
protected AbpIdentityServerAppServiceBase() |
||||
{ |
{ |
||||
LocalizationResource = typeof(AbpIdentityServerResource); |
LocalizationResource = typeof(AbpIdentityServerResource); |
||||
} |
} |
||||
|
|
||||
|
protected virtual async Task<bool> IsGrantAsync(string policy) |
||||
|
{ |
||||
|
return await PermissionChecker.IsGrantedAsync(policy); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -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 |
||||
|
} |
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
||||
@ -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> |
|
||||
@ -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> |
||||
Loading…
Reference in new issue