mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-15 14:24:39 +00:00
perf: update name space
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:visible="iVisible"
|
||||
height="300"
|
||||
title="Processing"
|
||||
width="300"
|
||||
>
|
||||
@@ -39,10 +40,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 5px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 50%;
|
||||
border-top-color: #3498db;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<i class="fa fa-check color-primary" />
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ iValue }}
|
||||
<svg-icon icon-class="ignore" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
@@ -95,7 +94,7 @@ export default {
|
||||
if (cmd === 'add_account') {
|
||||
this.row.present = true
|
||||
}
|
||||
this.row.status = { 'value': '0' }
|
||||
this.row.status = { 'value': '1' }
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.processing = false
|
||||
|
||||
@@ -113,7 +113,7 @@ export default [
|
||||
// },
|
||||
// {
|
||||
// path: ':id',
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateDetail/index.vue'),
|
||||
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateDetail/Application.vue'),
|
||||
// name: 'AccountTemplateDetail',
|
||||
// meta: { title: i18n.t('AccountTemplate') },
|
||||
// hidden: true
|
||||
|
||||
@@ -20,8 +20,7 @@ export default [
|
||||
component: () => import('@/views/sessions/SessionList/index.vue'),
|
||||
name: 'AccountSessionList',
|
||||
meta: {
|
||||
title: i18n.t('账号会话'),
|
||||
menuTitle: i18n.t('账号会话'),
|
||||
title: i18n.t('AccountSessions'),
|
||||
permissions: []
|
||||
}
|
||||
}
|
||||
@@ -46,8 +45,7 @@ export default [
|
||||
component: () => import('@/views/accounts/AccountActivity/AccountActivityList.vue'),
|
||||
name: 'AccountActivityList',
|
||||
meta: {
|
||||
title: i18n.t('活动记录'),
|
||||
menuTitle: i18n.t('活动记录'),
|
||||
title: i18n.t('AccountActivity'),
|
||||
permissions: ['audits.view_operatelog']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export default [
|
||||
component: () => import('@/views/accounts/AccountChangeSecret/index.vue'),
|
||||
name: 'AccountChangeSecretList',
|
||||
meta: {
|
||||
menuTitle: i18n.t('ChangeCredentials'),
|
||||
menuTitle: i18n.t('ChangeSecret'),
|
||||
title: i18n.t('AccountChangeSecret'),
|
||||
permissions: ['accounts.view_changesecretautomation']
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import i18n from '@/i18n/i18n'
|
||||
|
||||
import empty from '@/layout/empty'
|
||||
import automations from './automations'
|
||||
import services from './service'
|
||||
import integrations from './integrations'
|
||||
import security from './security'
|
||||
import activity from './activity'
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
component: Layout,
|
||||
redirect: '/pam/dashboard',
|
||||
meta: {
|
||||
title: '特权账号',
|
||||
title: i18n.t('PAM'),
|
||||
icon: 'pam',
|
||||
type: 'view',
|
||||
showNavSwitcher: true,
|
||||
@@ -36,7 +36,7 @@ export default {
|
||||
name: 'PamAccounts',
|
||||
component: () => import('@/views/pam/Account/index.vue'),
|
||||
meta: {
|
||||
title: i18n.t('特权账号'),
|
||||
title: i18n.t('Accounts'),
|
||||
icon: 'accounts',
|
||||
permissions: []
|
||||
}
|
||||
@@ -57,22 +57,22 @@ export default {
|
||||
name: 'AccountSecurity',
|
||||
component: empty,
|
||||
meta: {
|
||||
title: i18n.t('安全中心'),
|
||||
title: i18n.t('Security'),
|
||||
icon: 'accounts',
|
||||
permissions: []
|
||||
},
|
||||
children: security
|
||||
},
|
||||
{
|
||||
path: '/pam/services',
|
||||
name: 'AccountService',
|
||||
path: '/pam/integrations',
|
||||
name: 'Integrations',
|
||||
component: empty,
|
||||
meta: {
|
||||
title: i18n.t('Service'),
|
||||
title: i18n.t('Integration'),
|
||||
icon: 'accounts',
|
||||
permissions: []
|
||||
},
|
||||
children: services
|
||||
children: integrations
|
||||
},
|
||||
{
|
||||
path: '/pam/activity',
|
||||
|
||||
60
src/router/pam/integrations.js
Normal file
60
src/router/pam/integrations.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import empty from '@/layout/empty.vue'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'services',
|
||||
name: 'Services',
|
||||
component: empty,
|
||||
redirect: {
|
||||
name: 'IntegrationApplicationList'
|
||||
},
|
||||
meta: {
|
||||
app: 'accounts',
|
||||
name: 'Service',
|
||||
icon: 'service',
|
||||
resource: 'integrationapplicaion'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/pam/Integration/Application.vue'),
|
||||
name: 'IntegrationApplicationList',
|
||||
meta: {
|
||||
title: i18n.t('Applications'),
|
||||
permissions: ['accounts.view_integrationapplication']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/pam/Integration/ApplicationCreateUpdate.vue'),
|
||||
name: 'IntegrationApplicationCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('IntegrationApplicationCreate'),
|
||||
permissions: ['accounts.add_integrationapplication']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/pam/Integration/ApplicationCreateUpdate.vue'),
|
||||
name: 'IntegrationApplicationUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('IntegrationApplicationUpdate'),
|
||||
permissions: ['accounts.change_integrationapplication']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/pam/Integration/ApplicationDetail/index.vue'),
|
||||
name: 'ApplicationDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ApplicationDetail'),
|
||||
permissions: ['accounts.view_integrationapplication']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -15,16 +15,15 @@ export default [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/pam/AccountCheck/index.vue'),
|
||||
component: () => import('@/views/pam/RiskDetect/index.vue'),
|
||||
name: 'AccountCheckList',
|
||||
meta: {
|
||||
title: i18n.t('账号检查'),
|
||||
menuTitle: i18n.t('账号检查')
|
||||
title: i18n.t('RiskDetection')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/pam/AccountCheck/AccountCheckCreateUpdate.vue'),
|
||||
component: () => import('@/views/pam/RiskDetect/AccountCheckCreateUpdate.vue'),
|
||||
name: 'AccountCheckCreateUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import empty from '@/layout/empty.vue'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'services',
|
||||
name: 'ServiceIntegration',
|
||||
component: empty,
|
||||
redirect: {
|
||||
name: 'ServiceIntegrationList'
|
||||
},
|
||||
meta: {
|
||||
app: 'accounts',
|
||||
name: 'ServiceIntegration',
|
||||
icon: 'service',
|
||||
resource: 'serviceintegration'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/pam/ServiceIntegration/index.vue'),
|
||||
name: 'ServiceIntegrationList',
|
||||
meta: {
|
||||
title: i18n.t('ServiceIntegration'),
|
||||
menuTitle: i18n.t('ServiceIntegration'),
|
||||
permissions: ['accounts.view_serviceintegration']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/pam/ServiceIntegration/ServiceIntegrationCreateUpdate.vue'),
|
||||
name: 'ServiceIntegrationCreate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ServiceIntegrationCreate'),
|
||||
permissions: ['accounts.add_serviceintegration']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/pam/ServiceIntegration/ServiceIntegrationCreateUpdate.vue'),
|
||||
name: 'ServiceIntegrationUpdate',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ServiceIntegrationUpdate'),
|
||||
permissions: ['accounts.change_serviceintegration']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/pam/ServiceIntegration/ServiceIntegrationDetail/index.vue'),
|
||||
name: 'ServiceIntegrationDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('ServiceIntegrationDetail'),
|
||||
permissions: ['accounts.view_serviceintegration']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,175 +0,0 @@
|
||||
<template>
|
||||
<span>
|
||||
<span v-if="iValue === '0'" class="risk-handler">
|
||||
<el-dropdown trigger="click" @command="handleRisk">
|
||||
<el-button class="confirm action" size="mini">
|
||||
<i class="fa fa-check" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="item of iActions" :key="item.name" :command="item.name">
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-tooltip :content="$tc('Ignore')" :open-delay="400">
|
||||
<el-button class="ignore action" size="mini">
|
||||
<svg-icon icon-class="ignore" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip v-else :content="iLabel" :open-delay="400" class="platform-status">
|
||||
<span v-if="iValue === '1'">
|
||||
<i class="fa fa-check color-primary" />
|
||||
</span>
|
||||
<span v-else>
|
||||
<svg-icon icon-class="ignore" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<ReviewDraw :row="row" :visible.sync="reviewDrawer" />
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
||||
import ReviewDraw from '@/views/pam/AccountCheck/RiskHandlerFormatter/ReviewDraw.vue'
|
||||
|
||||
export default {
|
||||
name: 'RiskSummaryFormatter',
|
||||
components: { ReviewDraw },
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs),
|
||||
reviewDrawer: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
return this.getActions()
|
||||
},
|
||||
iValue() {
|
||||
if (this.cellValueIsLabelChoice()) {
|
||||
return this.cellValue.value
|
||||
} else {
|
||||
return this.cellValue
|
||||
}
|
||||
},
|
||||
iLabel() {
|
||||
if (this.cellValueIsLabelChoice()) {
|
||||
return this.cellValue.label
|
||||
} else {
|
||||
return this.cellValue
|
||||
}
|
||||
},
|
||||
iConfirmIcon() {
|
||||
const icon = this.formatterArgs.confirmIcon
|
||||
if (typeof icon === 'function') {
|
||||
return icon({ row: this.row, cellValue: this.cellValue })
|
||||
} else {
|
||||
return icon
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRisk(cmd) {
|
||||
const data = {
|
||||
username: this.row.username,
|
||||
asset: this.row.asset.id,
|
||||
risk: this.row.risk.value,
|
||||
action: cmd
|
||||
}
|
||||
switch (cmd) {
|
||||
case 'review':
|
||||
this.reviewDrawer = true
|
||||
break
|
||||
default:
|
||||
this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data).then(() => {
|
||||
this.row.status = 'confirmed'
|
||||
})
|
||||
}
|
||||
},
|
||||
getActions() {
|
||||
const actions = [
|
||||
{
|
||||
name: 'disable_remote',
|
||||
label: this.$t('Disable remote'),
|
||||
has: ['long_time_no_login', 'new_found']
|
||||
},
|
||||
{
|
||||
name: 'delete_remote',
|
||||
label: this.$t('Delete Account'),
|
||||
has: ['long_time_no_login', 'new_found']
|
||||
},
|
||||
{
|
||||
name: 'delete_both',
|
||||
label: this.$t('Delete Both'),
|
||||
has: ['long_time_no_login']
|
||||
},
|
||||
{
|
||||
name: 'add_account',
|
||||
label: this.$t('Add to Account'),
|
||||
has: ['new_found']
|
||||
},
|
||||
{
|
||||
name: 'change_password',
|
||||
label: this.$t('Change Password'),
|
||||
has: ['long_time_password', 'weak_password', 'password_expired']
|
||||
},
|
||||
// {
|
||||
// name: 'addPrivilegedAccount',
|
||||
// label: this.$t('Add Privileged Account'),
|
||||
// has: ['no_admin_account']
|
||||
// },
|
||||
// {
|
||||
// name: 'modifyPassword',
|
||||
// label: this.$t('Modify Password'),
|
||||
// has: ['password_error']
|
||||
// },
|
||||
{
|
||||
name: 'review',
|
||||
label: this.$t('Review'),
|
||||
has: ['group_changed', 'sudo_changed', 'authorized_key_changed', 'account_deleted', 'others']
|
||||
}
|
||||
]
|
||||
return actions.filter(action => {
|
||||
return action.has.includes(this.row.risk.value) || action.name === 'review'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.action.el-button--mini {
|
||||
cursor: pointer;
|
||||
padding: 1px 4px;
|
||||
|
||||
&.confirm {
|
||||
::v-deep i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.remove {
|
||||
::v-deep i {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
&.ignore {
|
||||
::v-deep svg.svg-icon {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.draw-body {
|
||||
padding: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
49
src/views/pam/Integration/Application.vue
Normal file
49
src/views/pam/Integration/Application.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<TabPage
|
||||
:active-menu.sync="activeMenu"
|
||||
:submenu="tab.submenu"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TabPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
name: 'IntegrationApplicationList',
|
||||
components: {
|
||||
TabPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
activeMenu: 'service',
|
||||
tab: {
|
||||
submenu: [
|
||||
{
|
||||
name: 'application',
|
||||
title: this.$t('Applications'),
|
||||
hidden: !this.$hasPerm('accounts.view_integrationapplication'),
|
||||
component: () => import('@/views/pam/Integration/ApplicationList.vue')
|
||||
},
|
||||
{
|
||||
name: 'records',
|
||||
title: this.$t('Records'),
|
||||
hidden: !this.$hasPerm('audits.view_serviceaccesslog'),
|
||||
component: () => import('@/views/pam/Integration/components/CallRecords.vue')
|
||||
},
|
||||
{
|
||||
name: 'docs',
|
||||
title: this.$t('Documentation'),
|
||||
hidden: !this.$hasPerm('accounts.view_integrationapplication'),
|
||||
component: () => import('@/views/pam/Integration/SDKList.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
async mounted() {
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
@@ -8,7 +8,7 @@ import { UploadField } from '@/components'
|
||||
import { JSONManyToManySelect } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'ServiceIntegrationCreateUpdate',
|
||||
name: 'IntegrationApplicationCreateUpdate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
@@ -16,9 +16,9 @@ export default {
|
||||
const vm = this
|
||||
return {
|
||||
logo_file: null,
|
||||
url: '/api/v1/accounts/service-integrations/',
|
||||
url: '/api/v1/accounts/integration-applications/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'logo_image']],
|
||||
[this.$t('Basic'), ['name', 'logo']],
|
||||
[this.$t('AccountPolicy'), ['accounts', 'ip_group']],
|
||||
[this.$t('Other'), ['is_active', 'comment']]
|
||||
],
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
logo_image: {
|
||||
logo: {
|
||||
component: UploadField,
|
||||
el: {
|
||||
width: '10%',
|
||||
@@ -66,11 +66,11 @@ export default {
|
||||
},
|
||||
hasSaveContinue: false,
|
||||
createSuccessNextRoute: {
|
||||
name: 'ServiceIntegrationDetail'
|
||||
name: 'ApplicationDetail'
|
||||
},
|
||||
performSubmit(values) {
|
||||
const formData = new FormData()
|
||||
delete values['logo_image']
|
||||
delete values['logo']
|
||||
|
||||
if (!Array.isArray(values.ip_group)) {
|
||||
values.ip_group = values.ip_group ? values.ip_group.split(',') : []
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
formData.append(key, value)
|
||||
}
|
||||
if (vm.logo_file) {
|
||||
formData.append('logo_image', vm.logo_file)
|
||||
formData.append('logo', vm.logo_file)
|
||||
}
|
||||
return this.$axios[this.method](this.iUrl, formData)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import AutoDetailCard from '@/components/Cards/DetailCard/auto'
|
||||
import SecretDialog from '@/components/Dialog/Secret.vue'
|
||||
|
||||
export default {
|
||||
name: 'ServiceIntegrationInfo',
|
||||
name: 'IntegrationApplicationInfo',
|
||||
components: {
|
||||
SecretDialog,
|
||||
AutoDetailCard,
|
||||
@@ -42,12 +42,12 @@ export default {
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('Generate'),
|
||||
disabled: !this.$hasPerm('accounts.change_serviceintegration') || !this.object.is_active
|
||||
disabled: !this.$hasPerm('accounts.change_integrationapplication') || !this.object.is_active
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/accounts/service-integrations/${this.object.id}/secret/`,
|
||||
`/api/v1/accounts/integration-applications/${this.object.id}/secret/`,
|
||||
).then(res => {
|
||||
this.$refs.secretDialog.show(res)
|
||||
})
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
}
|
||||
}
|
||||
],
|
||||
url: `/api/v1/accounts/service-integrations/${this.object.id}`,
|
||||
url: `/api/v1/accounts/integration-applications/${this.object.id}`,
|
||||
detailFields: [
|
||||
'id', 'name', 'date_created', 'date_updated', 'comment', 'is_active'
|
||||
]
|
||||
@@ -8,27 +8,27 @@
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage } from '@/layout/components'
|
||||
import ServiceIntegrationInfo from './ServiceIntegrationInfo.vue'
|
||||
import IntegrationApplicationInfo from './ServiceInfo.vue'
|
||||
import ServiceCallRecords from '../components/CallRecords.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
ServiceCallRecords,
|
||||
ServiceIntegrationInfo
|
||||
IntegrationApplicationInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
object: {},
|
||||
config: {
|
||||
titlePrefix: this.$t('ServiceIntegrationDetail'),
|
||||
activeMenu: 'ServiceIntegrationInfo',
|
||||
url: '/api/v1/accounts/service-integrations',
|
||||
titlePrefix: this.$t('ApplicationDetail'),
|
||||
activeMenu: 'IntegrationApplicationInfo',
|
||||
url: '/api/v1/accounts/integration-applications',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('Basic'),
|
||||
name: 'ServiceIntegrationInfo',
|
||||
hidden: () => !this.$hasPerm('accounts.view_serviceintegration')
|
||||
name: 'IntegrationApplicationInfo',
|
||||
hidden: () => !this.$hasPerm('accounts.view_integrationapplication')
|
||||
},
|
||||
{
|
||||
name: 'ServiceCallRecords',
|
||||
@@ -16,8 +16,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/accounts/service-integrations/',
|
||||
permissions: { app: 'accounts', resource: 'serviceintegration' }
|
||||
url: '/api/v1/accounts/integration-applications/',
|
||||
permissions: { app: 'accounts', resource: 'integrationapplication' }
|
||||
},
|
||||
headerActions: {
|
||||
hasImport: false,
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
},
|
||||
subComponentProps: {
|
||||
getImage: (obj) => {
|
||||
return obj.logo_image
|
||||
return obj.logo
|
||||
},
|
||||
getInfos: (obj) => {
|
||||
return [
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
]
|
||||
},
|
||||
handleUpdate: (obj) => {
|
||||
this.$router.push({ name: 'ServiceIntegrationUpdate', params: { id: obj.id }})
|
||||
this.$router.push({ name: 'IntegrationApplicationUpdate', params: { id: obj.id }})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
})
|
||||
},
|
||||
getSdkInfo() {
|
||||
const url = `/api/v1/accounts/service-integrations/sdks/?language=${this.currentLanguage}`
|
||||
const url = `/api/v1/accounts/integration-applications/sdks/?language=${this.currentLanguage}`
|
||||
this.$axios.get(url).then(res => {
|
||||
this.readme = res.readme
|
||||
this.$nextTick(() => {
|
||||
@@ -1,28 +1,31 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-if="iVisible"
|
||||
:title="$t('Details')"
|
||||
:visible.sync="iVisible"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
direction="rtl"
|
||||
style="z-index: 999"
|
||||
title="审查"
|
||||
@open="handleOpen"
|
||||
>
|
||||
<div class="drawer-body">
|
||||
<el-timeline :reverse="true">
|
||||
<el-timeline-item
|
||||
v-for="detail in row.details"
|
||||
:key="detail.datetime"
|
||||
:icon="getDetailIcon(detail)"
|
||||
:timestamp="formatTimestamp(detail.datetime)"
|
||||
:type="getDetailType(detail)"
|
||||
placement="top"
|
||||
>
|
||||
<span v-html="handleDetail(row, detail)" />
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
<div class="drawer-footer">
|
||||
<div v-if="showButtons" class="drawer-footer">
|
||||
<span class="buttons">
|
||||
<el-button size="small" type="primary" @click="handleConfirm">确认</el-button>
|
||||
<el-button size="small">忽略</el-button>
|
||||
<el-button size="small" type="primary" @click="handleConfirm">{{ $t("Confirm") }}</el-button>
|
||||
<el-button size="small">{{ $t('Ignore') }}</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</el-drawer>
|
||||
@@ -30,6 +33,7 @@
|
||||
|
||||
<script>
|
||||
import { toSafeLocalDateStr } from '@/utils/time'
|
||||
import { riskActions } from './const'
|
||||
|
||||
export default {
|
||||
name: 'ReviewDraw',
|
||||
@@ -41,10 +45,16 @@ export default {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
riskActions
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
@@ -54,21 +64,59 @@ export default {
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
},
|
||||
actionMap() {
|
||||
return riskActions.reduce((acc, cur) => {
|
||||
acc[cur.name] = cur
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
handleOpen() {
|
||||
},
|
||||
formatTimestamp(datetime) {
|
||||
return toSafeLocalDateStr(datetime)
|
||||
},
|
||||
handleConfirm() {
|
||||
|
||||
},
|
||||
getDetailIcon(detail) {
|
||||
if (detail.status === '1') {
|
||||
return 'el-icon-check'
|
||||
} else if (detail.status === '0') {
|
||||
return 'el-icon-close'
|
||||
}
|
||||
},
|
||||
getDetailType(detail) {
|
||||
if (detail.type !== 'process') {
|
||||
return ''
|
||||
}
|
||||
if (detail.status === '1') {
|
||||
return 'primary'
|
||||
} else {
|
||||
return 'danger'
|
||||
}
|
||||
},
|
||||
handleDetail(row, detail) {
|
||||
if (!detail.type || ['init', 'refind'].includes(detail.type)) {
|
||||
return this.handleInit(row, detail)
|
||||
} else if (detail.type === 'process') {
|
||||
return this.handleProcess(row, detail)
|
||||
}
|
||||
},
|
||||
handleProcess(row, detail) {
|
||||
const { action, processor } = detail
|
||||
const actionLabel = this.actionMap[action]?.label || action
|
||||
return `${processor} ${actionLabel}`
|
||||
},
|
||||
handleInit(row, detail) {
|
||||
switch (row.risk.value) {
|
||||
case 'ghost':
|
||||
return '发现时间: ' + this.formatTimestamp(detail.datetime)
|
||||
case 'zombie':
|
||||
return '上次登录时间: ' + this.formatTimestamp(detail.date)
|
||||
case 'new_found':
|
||||
return this.$tc('New found')
|
||||
case 'long_time_no_login':
|
||||
return this.$tc('Last login time') + ': ' + this.formatTimestamp(detail.date)
|
||||
case 'group_changed':
|
||||
case 'sudoers_changed':
|
||||
case 'authorized_key_changed':
|
||||
@@ -78,9 +126,9 @@ ${detail.diff}
|
||||
</pre>
|
||||
`
|
||||
case 'long_time_password':
|
||||
return '上次改密时间: ' + this.formatTimestamp(detail.date)
|
||||
return this.$t('Last change time') + ': ' + this.formatTimestamp(detail.date)
|
||||
case 'account_deleted':
|
||||
return '账号被删除'
|
||||
return this.$t('Account deleted')
|
||||
default:
|
||||
return '其它'
|
||||
}
|
||||
49
src/views/pam/RiskDetect/RiskHandlerFormatter/const.js
Normal file
49
src/views/pam/RiskDetect/RiskHandlerFormatter/const.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const riskActions = [
|
||||
{
|
||||
name: 'disable_remote',
|
||||
label: i18n.t('Disable remote'),
|
||||
has: ['long_time_no_login', 'new_found']
|
||||
},
|
||||
{
|
||||
name: 'delete_remote',
|
||||
label: i18n.t('Delete Account'),
|
||||
has: ['long_time_no_login', 'new_found']
|
||||
},
|
||||
{
|
||||
name: 'delete_both',
|
||||
label: i18n.t('Delete Both'),
|
||||
has: ['long_time_no_login']
|
||||
},
|
||||
{
|
||||
name: 'add_account',
|
||||
label: i18n.t('Add to Account'),
|
||||
has: ['new_found'],
|
||||
disabled: async function() {
|
||||
const url = `/api/v1/accounts/accounts/?username=${this.row.username}&asset=${this.row.asset.id}`
|
||||
const data = await this.$axios.get(url)
|
||||
return data.length > 0
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'change_password',
|
||||
label: i18n.t('Change Password'),
|
||||
has: ['long_time_password', 'weak_password', 'password_expired']
|
||||
},
|
||||
// {
|
||||
// name: 'addPrivilegedAccount',
|
||||
// label: i18n.t('Add Privileged Account'),
|
||||
// has: ['no_admin_account']
|
||||
// },
|
||||
// {
|
||||
// name: 'modifyPassword',
|
||||
// label: i18n.t('Modify Password'),
|
||||
// has: ['password_error']
|
||||
// },
|
||||
{
|
||||
name: 'review',
|
||||
label: i18n.t('Review'),
|
||||
has: ['group_changed', 'sudo_changed', 'authorized_key_changed', 'account_deleted', 'others']
|
||||
}
|
||||
]
|
||||
180
src/views/pam/RiskDetect/RiskHandlerFormatter/index.vue
Normal file
180
src/views/pam/RiskDetect/RiskHandlerFormatter/index.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<span>
|
||||
<span v-if="iValue === '0'" class="risk-handler">
|
||||
<el-dropdown trigger="click" @command="handleRisk" @visible-change="handleVisibleChange">
|
||||
<el-button class="confirm action" size="mini">
|
||||
<i class="fa fa-check" />
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
v-for="item of actions"
|
||||
:key="item.name"
|
||||
:command="item.name"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-tooltip :content="$tc('Ignore')" :open-delay="400">
|
||||
<el-button class="ignore action" size="mini" @click="handleRisk('ignore')">
|
||||
<svg-icon icon-class="ignore" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip v-else :content="$t('Detail')" :open-delay="400" class="platform-status">
|
||||
<el-button size="mini" type="text" @click="showDetail">
|
||||
<i v-if="iValue === '1'" class="fa fa-check color-primary" />
|
||||
<svg-icon v-else icon-class="ignore" />
|
||||
{{ iLabel }}
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<ReviewDraw :row="row" :show-buttons="reviewButtons" :visible.sync="reviewDrawer" />
|
||||
<ProcessingDialog :visible="processing" />
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import BaseFormatter from '@/components/Table/TableFormatters/base.vue'
|
||||
import ReviewDraw from '@/views/pam/RiskDetect/RiskHandlerFormatter/ReviewDraw.vue'
|
||||
import ProcessingDialog from '@/components/Dialog/ProcessingDialog.vue'
|
||||
import { riskActions } from './const'
|
||||
|
||||
export default {
|
||||
name: 'RiskSummaryFormatter',
|
||||
components: { ProcessingDialog, ReviewDraw },
|
||||
extends: BaseFormatter,
|
||||
props: {
|
||||
formatterArgsDefault: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reviewDrawer: false,
|
||||
reviewButtons: true,
|
||||
processing: false,
|
||||
actions: [],
|
||||
formatterArgs: Object.assign(this.formatterArgsDefault, this.col.formatterArgs)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActions() {
|
||||
return this.getActions()
|
||||
},
|
||||
iValue() {
|
||||
if (this.cellValueIsLabelChoice()) {
|
||||
return this.cellValue.value
|
||||
} else {
|
||||
return this.cellValue
|
||||
}
|
||||
},
|
||||
iLabel() {
|
||||
if (this.cellValueIsLabelChoice()) {
|
||||
return this.cellValue.label
|
||||
} else {
|
||||
return this.cellValue
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showDetail() {
|
||||
this.reviewButtons = false
|
||||
this.reviewDrawer = true
|
||||
},
|
||||
handleReview() {
|
||||
this.reviewButtons = true
|
||||
this.reviewDrawer = true
|
||||
},
|
||||
async handleCommon(cmd) {
|
||||
const data = {
|
||||
username: this.row.username,
|
||||
asset: this.row.asset.id,
|
||||
risk: this.row.risk.value,
|
||||
action: cmd
|
||||
}
|
||||
this.processing = true
|
||||
this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data).then(() => {
|
||||
if (cmd !== 'ignore') {
|
||||
this.row.status = { value: '1', label: this.$t('Confirmed') }
|
||||
} else {
|
||||
this.row.status = { value: '2', label: this.$t('Ignored') }
|
||||
}
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.processing = false
|
||||
this.$axios.get(`/api/v1/accounts/account-risks/${this.row.id}/`).then((res) => {
|
||||
Object.assign(this.row, res)
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
handleRisk(cmd) {
|
||||
if (cmd === 'review') {
|
||||
this.handleReview()
|
||||
} else {
|
||||
this.handleCommon(cmd)
|
||||
}
|
||||
},
|
||||
async checkDisabled(action) {
|
||||
let disabled = action.disabled === undefined ? false : action.disabled
|
||||
if (typeof disabled === 'function') {
|
||||
disabled = await action.disabled.call(this)
|
||||
}
|
||||
return disabled
|
||||
},
|
||||
async handleVisibleChange(visible) {
|
||||
if (!visible) {
|
||||
return
|
||||
}
|
||||
if (this.actions.length === 0) {
|
||||
this.actions = await this.getActions()
|
||||
}
|
||||
return this.actions.length > 0
|
||||
},
|
||||
checkHas(action) {
|
||||
return action.has.includes(this.row.risk.value) || action.name === 'review'
|
||||
},
|
||||
async getActions() {
|
||||
let actions = _.cloneDeep(riskActions)
|
||||
actions = actions.filter((action) => {
|
||||
return this.checkHas(action)
|
||||
})
|
||||
for (const action of actions) {
|
||||
action.disabled = await this.checkDisabled(action)
|
||||
}
|
||||
return actions
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.action.el-button--mini {
|
||||
cursor: pointer;
|
||||
padding: 1px 4px;
|
||||
|
||||
&.confirm {
|
||||
::v-deep i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.remove {
|
||||
::v-deep i {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
&.ignore {
|
||||
::v-deep svg.svg-icon {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.draw-body {
|
||||
padding: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -16,28 +16,28 @@ export default {
|
||||
activeMenu: 'AccountRisks',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('检查结果'),
|
||||
title: this.$t('DetectResults'),
|
||||
name: 'AccountRisks',
|
||||
hidden: !this.$hasPerm('accounts.view_accountrisk'),
|
||||
component: () => import('@/views/pam/AccountCheck/AccountRiskList.vue')
|
||||
component: () => import('@/views/pam/RiskDetect/AccountRiskList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('检查任务'),
|
||||
title: this.$t('DetectTasks'),
|
||||
name: 'AccountCheckTask',
|
||||
hidden: !this.$hasPerm('accounts.view_accountcheckautomation'),
|
||||
component: () => import('./AccountCheckTaskList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('执行历史'),
|
||||
title: this.$t('Executions'),
|
||||
name: 'AccountCheckExecution',
|
||||
hidden: !this.$hasPerm('accounts.view_accountcheckautomation'),
|
||||
component: () => import('./AccountCheckExecutionList.vue')
|
||||
},
|
||||
{
|
||||
title: this.$t('检查引擎'),
|
||||
title: this.$t('DetectEngines'),
|
||||
name: 'AccountCheckEngine',
|
||||
hidden: !this.$hasPerm('accounts.view_accountcheckautomation'),
|
||||
component: () => import('@/views/pam/AccountCheck/AccountCheckEngine.vue')
|
||||
component: () => import('@/views/pam/RiskDetect/AccountCheckEngine.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<TabPage
|
||||
:active-menu.sync="activeMenu"
|
||||
:submenu="tab.submenu"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TabPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
name: 'ServiceIntegrationList',
|
||||
components: {
|
||||
TabPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
activeMenu: 'service',
|
||||
tab: {
|
||||
submenu: [
|
||||
{
|
||||
name: 'service',
|
||||
title: this.$t('RelevantApp'),
|
||||
hidden: !this.$hasPerm('accounts.view_serviceintegration'),
|
||||
component: () => import('@/views/pam/ServiceIntegration/ServiceIntegrationList.vue')
|
||||
},
|
||||
{
|
||||
name: 'call-records',
|
||||
title: this.$t('CallRecords'),
|
||||
hidden: !this.$hasPerm('audits.view_serviceaccesslog'),
|
||||
component: () => import('@/views/pam/ServiceIntegration/components/CallRecords.vue')
|
||||
},
|
||||
{
|
||||
name: 'sdk-doc',
|
||||
title: this.$t('SDK'),
|
||||
hidden: !this.$hasPerm('accounts.view_serviceintegration'),
|
||||
component: () => import('@/views/pam/ServiceIntegration/SDKList.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
async mounted() {
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user