mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 03:46:26 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48beb502bd | ||
|
|
383a799c6a | ||
|
|
c378b2bf0d |
@@ -57,26 +57,6 @@ export function TestReplayStorage(id) {
|
||||
})
|
||||
}
|
||||
|
||||
function SetToDefaultStorage(url) {
|
||||
return request({
|
||||
url: url,
|
||||
method: 'patch',
|
||||
data: { 'is_default': true }
|
||||
})
|
||||
}
|
||||
|
||||
export function SetToDefaultCommandStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/command-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function SetToDefaultReplayStorage(id) {
|
||||
return SetToDefaultStorage(
|
||||
`/api/v1/terminal/replay-storages/${id}/`,
|
||||
)
|
||||
}
|
||||
|
||||
export function getReplayStorage(id) {
|
||||
return request({
|
||||
url: `/api/v1/terminal/replay-storages/${id}/`,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
@MFAVerifyDone="getAuthInfo"
|
||||
@MFAVerifyCancel="exit"
|
||||
/>
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:destroy-on-close="true"
|
||||
:width="'50'"
|
||||
:visible.sync="showAuthInfo"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="80px" :model="authInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<el-input v-model="authInfo['private_key']" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
export default {
|
||||
name: 'ShowSecretInfo',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTitle: this.$t('common.ViewSecret'),
|
||||
authInfo: {},
|
||||
showAuthInfo: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getAuthInfo()
|
||||
},
|
||||
methods: {
|
||||
getAuthInfo() {
|
||||
const url = `/api/v1/assets/account-secrets/${this.account.id}/`
|
||||
this.$axios.get(url, { disableFlashErrorMsg: true }).then(resp => {
|
||||
this.authInfo = resp
|
||||
this.showAuthInfo = true
|
||||
})
|
||||
},
|
||||
exit() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,91 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
width="50"
|
||||
:title="this.$t('assets.UpdateAssetUserToken')"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
@confirm="handleConfirm()"
|
||||
@cancel="handleCancel()"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form label-position="right" label-width="80px">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<input type="file" @change="onPrivateKeyLoaded">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
export default {
|
||||
name: 'UpdateSecretInfo',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
authInfo: {
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleConfirm() {
|
||||
const data = {}
|
||||
if (this.authInfo.password !== '') {
|
||||
data.password = this.authInfo.password
|
||||
}
|
||||
if (this.authInfo.private_key !== '') {
|
||||
data.private_key = this.authInfo.private_key
|
||||
}
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/accounts/${this.account.id}/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.authInfo = { password: '', private_key: '' }
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
this.$emit('updateAuthDone', res)
|
||||
this.$emit('update:visible', false)
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$tc('common.updateErrorMsg' + ' ' + err))
|
||||
this.$emit('update:visible', false)
|
||||
})
|
||||
},
|
||||
handleCancel() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
onPrivateKeyLoaded(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.authInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,31 +0,0 @@
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const connectivityMeta = {
|
||||
label: i18n.t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
failed: 'fa-times text-danger',
|
||||
unknown: 'fa-circle text-warning'
|
||||
},
|
||||
hasTips: true,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
const mapper = {
|
||||
'ok': i18n.tc('assets.Reachable'),
|
||||
'failed': i18n.tc('assets.Unreachable'),
|
||||
'unknown': i18n.tc('assets.Unknown')
|
||||
}
|
||||
let tips = mapper[cellValue]
|
||||
if (row['date_verified']) {
|
||||
const datetime = toSafeLocalDateStr(row['date_verified'])
|
||||
tips += '<br> ' + datetime
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<ShowSecretInfo v-if="showViewSecretDialog" :visible.sync="showViewSecretDialog" :account="account" />
|
||||
<UpdateSecretInfo :visible.sync="showUpdateSecretDialog" :account="account" @updateAuthDone="onUpdateAuthDone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import { ActionsFormatter, DetailFormatter, DisplayFormatter } from '@/components/TableFormatters'
|
||||
import ShowSecretInfo from './ShowSecretInfo'
|
||||
import UpdateSecretInfo from './UpdateSecretInfo'
|
||||
import { connectivityMeta } from './const'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
UpdateSecretInfo,
|
||||
ShowSecretInfo
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
exportUrl: {
|
||||
type: String,
|
||||
default() {
|
||||
return this.url.replace('/assets/accounts/', '/assets/account-secrets/')
|
||||
}
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showViewSecretDialog: false,
|
||||
showUpdateSecretDialog: false,
|
||||
account: {},
|
||||
tableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
'hostname', 'ip', 'username', 'version', 'connectivity',
|
||||
'systemuser', 'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['username', 'actions'],
|
||||
default: ['hostname', 'ip', 'username', 'version', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
hostname: {
|
||||
prop: 'hostname',
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ip: {
|
||||
width: '120px'
|
||||
},
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
systemuser: {
|
||||
formatter: DisplayFormatter
|
||||
},
|
||||
version: {
|
||||
width: '70px'
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showViewSecretDialog = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$axios.delete(`/api/v1/assets/accounts/${row.id}/`).then(() => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: ({ row }) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/${row.id}/verify/`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function({ row }) {
|
||||
this.account = row
|
||||
this.showUpdateSecretDialog = true
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
exportOptions: {
|
||||
url: this.exportUrl,
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpdateAuthDone(account) {
|
||||
Object.assign(this.account, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
529
src/components/AssetUserTable/index.vue
Normal file
529
src/components/AssetUserTable/index.vue
Normal file
@@ -0,0 +1,529 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<ListTable ref="ListTable" :table-config="iTableConfig" :header-actions="headerActions" />
|
||||
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||
<div v-if="MFAConfirmed">
|
||||
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="MFAInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="MFAInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="MFAInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-row v-else :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAInput" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
<Dialog width="50" :title="this.$t('assets.UpdateAssetUserToken')" :visible.sync="showDialog" @confirm="handleConfirm()" @cancel="handleCancel()">
|
||||
<el-form label-position="right" label-width="80px" :model="dialogInfo">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="dialogInfo.hostname" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Username')">
|
||||
<el-input v-model="dialogInfo.username" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="dialogInfo.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.sshkey')">
|
||||
<input type="file" @change="Onchange">
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
<Dialog :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import ListTable from '@/components/ListTable/index'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
import { ActionsFormatter, DateFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
ListTable,
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
searchExclude: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
extraQuery: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
canExportAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canExportSelected: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canExportFiltered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasLeftActions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
otherActions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
hasImport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasExport: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hasClone: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAConfirmed: false,
|
||||
MFAInput: '',
|
||||
MFAInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
},
|
||||
showDialog: false,
|
||||
showMFADialog: false,
|
||||
dialogInfo: {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
},
|
||||
selectedRows: '',
|
||||
dialogStatus: '',
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
defaultTableConfig: {
|
||||
url: this.url,
|
||||
columns: ['hostname', 'ip', 'username', 'version', 'date_created', 'actions'],
|
||||
columnsMeta: {
|
||||
'hostname': {
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
'ip': {
|
||||
label: this.$t('assets.ip'),
|
||||
width: '120px'
|
||||
},
|
||||
'username': {
|
||||
label: this.$t('assets.Username'),
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
'version': {
|
||||
label: this.$t('assets.Version'),
|
||||
width: '70px'
|
||||
},
|
||||
'date_created': {
|
||||
label: this.$t('assets.date_joined'),
|
||||
formatter: DateFormatter
|
||||
},
|
||||
'actions': {
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
hasClone: this.hasClone,
|
||||
moreActionsTitle: this.$t('common.More'),
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: function(val) {
|
||||
this.dialogStatus = 'viewAutoInfo'
|
||||
this.MFAInfo.asset = val.row.id
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = true
|
||||
this.MFAConfirmed = true
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
title: this.$t('common.Delete'),
|
||||
type: 'primary',
|
||||
callback: (val) => {
|
||||
this.$axios.delete(`/api/v1/assets/asset-users/${val.row.id}/`).then(() => {
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
this.$refs.ListTable.reloadTable()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Test',
|
||||
title: this.$t('common.Test'),
|
||||
callback: (val) => {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/tasks/?id=${val.row.id}`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
title: this.$t('common.Update'),
|
||||
can: !this.$store.getters.currentOrgIsRoot,
|
||||
callback: function(val) {
|
||||
this.showDialog = true
|
||||
this.dialogInfo.asset = val.row.asset
|
||||
this.dialogInfo.hostname = val.row.hostname
|
||||
this.dialogInfo.username = val.row.username
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
extraQuery: this.extraQuery || { latest: 1 }
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: this.hasLeftActions,
|
||||
hasMoreActions: false,
|
||||
hasImport: this.hasImport,
|
||||
hasExport: this.hasExport,
|
||||
hasSearch: true,
|
||||
searchConfig: {
|
||||
exclude: this.searchExclude,
|
||||
options: [
|
||||
{
|
||||
label: this.$t('assets.OnlyLatestVersion'),
|
||||
value: 'latest',
|
||||
children: [
|
||||
{
|
||||
label: this.$t('common.Yes'),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: this.$t('common.No'),
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
return false
|
||||
}
|
||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||
const now = new Date()
|
||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||
},
|
||||
iTableConfig() {
|
||||
const columnsMeta = Object.assign({}, this.defaultTableConfig.columnsMeta, this.tableConfig.columnsMeta || {})
|
||||
const config = Object.assign(this.defaultTableConfig, this.tableConfig)
|
||||
config.columnsMeta = columnsMeta
|
||||
return config
|
||||
},
|
||||
exportOptions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('common.imExport.ExportAll'),
|
||||
value: 'all',
|
||||
can: this.canExportAll
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlySelectedItems'),
|
||||
value: 'selected',
|
||||
can: this.selectedRows.length > 0 && this.canExportSelected
|
||||
},
|
||||
{
|
||||
label: this.$t('common.imExport.ExportOnlyFiltered'),
|
||||
value: 'filtered',
|
||||
can: this.tableHasQuery() && this.canExportFiltered
|
||||
}
|
||||
]
|
||||
},
|
||||
exportTypeOptions() {
|
||||
return [
|
||||
{
|
||||
label: 'CSV',
|
||||
value: 'csv',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
label: 'Excel',
|
||||
value: 'xlsx',
|
||||
can: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.iTableConfig, 'url', iNew)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.otherActions) {
|
||||
const actionColumn = this.iTableConfig.columns[this.iTableConfig.columns.length - 1]
|
||||
for (const item of this.otherActions) {
|
||||
actionColumn.formatterArgs.extraActions.push(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.headerActions.handleExport = this.handleExport || this.defaultHandleExport
|
||||
if (this.handleImport) {
|
||||
this.headerActions.handleImport = this.handleImport
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
MFAConfirm() {
|
||||
if (this.MFAInput.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAInput
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
this.$store.dispatch('users/setMFAVerify')
|
||||
if (this.dialogStatus === 'export') {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
} else {
|
||||
this.$axios.get(`/api/v1/assets/asset-user-auth-infos/${this.MFAInfo.asset}/`).then(res => {
|
||||
this.MFAConfirmed = true
|
||||
this.MFAInfo.hostname = res.hostname
|
||||
this.MFAInfo.password = res.password
|
||||
this.MFAInfo.username = res.username
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
handleMFAConfirm() {
|
||||
this.MFAInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: ''
|
||||
}
|
||||
this.MFAInput = ''
|
||||
this.showMFADialog = false
|
||||
this.MFAConfirmed = false
|
||||
},
|
||||
handleCancel() {
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
Onchange(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.dialogInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
},
|
||||
handleConfirm() {
|
||||
const data = {
|
||||
asset: this.dialogInfo.asset,
|
||||
username: this.dialogInfo.username
|
||||
}
|
||||
if (this.dialogInfo.password !== '') {
|
||||
data.password = this.dialogInfo.password
|
||||
}
|
||||
if (this.dialogInfo.private_key !== '') {
|
||||
data.private_key = this.dialogInfo.private_key
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/asset-users/`,
|
||||
data
|
||||
).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
this.dialogInfo = {
|
||||
asset: '',
|
||||
username: '',
|
||||
hostname: '',
|
||||
password: '',
|
||||
private_key: ''
|
||||
}
|
||||
this.showDialog = false
|
||||
this.$refs.ListTable.reloadTable()
|
||||
},
|
||||
tableQuery() {
|
||||
const listTableRef = this.$refs.ListTable
|
||||
if (!listTableRef) {
|
||||
return {}
|
||||
}
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
delete query['date_from']
|
||||
delete query['date_to']
|
||||
return query
|
||||
},
|
||||
tableHasQuery() {
|
||||
return Object.keys(this.tableQuery()).length > 0
|
||||
},
|
||||
defaultHandleExport({ selectedRows }) {
|
||||
this.selectedRows = selectedRows
|
||||
this.dialogStatus = 'export'
|
||||
if (!this.needMFAVerify) {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
} else {
|
||||
this.showMFADialog = true
|
||||
}
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
async performExport(selectRows, exportOption, q) {
|
||||
const url = `/api/v1/assets/asset-user-auth-infos/`
|
||||
const query = Object.assign({}, q)
|
||||
if (exportOption === 'selected') {
|
||||
const resources = []
|
||||
const data = selectRows
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
resources.push(data[index].id)
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
||||
queryUtil.stringify(query, '=', '&')
|
||||
return this.downloadCsv(url + queryStr)
|
||||
},
|
||||
async performExportConfirm() {
|
||||
const listTableRef = this.$refs.ListTable
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.performExportConfirm()
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.export-item {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.export-form >>> .el-form-item__label {
|
||||
line-height: 2
|
||||
}
|
||||
</style>
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataForm from '@/components/DataForm'
|
||||
import { DataForm } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'NestedField',
|
||||
|
||||
@@ -170,12 +170,12 @@ export default {
|
||||
col.filters = column.choices.map(item => {
|
||||
if (typeof (item.value) === 'boolean') {
|
||||
if (item.value) {
|
||||
return { text: item['display_name'], value: 'True' }
|
||||
return { text: item.display_name, value: 'True' }
|
||||
} else {
|
||||
return { text: item['display_name'], value: 'False' }
|
||||
return { text: item.display_name, value: 'False' }
|
||||
}
|
||||
}
|
||||
return { text: item['display_name'], value: item.value }
|
||||
return { text: item.display_name, value: item.value }
|
||||
})
|
||||
col.sortable = false
|
||||
col['column-key'] = col.prop
|
||||
@@ -223,7 +223,7 @@ export default {
|
||||
defaultColumnsNames = totalColumnsNames.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 最小列
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['actions', 'id'])
|
||||
const minColumnsNames = _.get(this.iConfig, 'columnsShow.min', ['action', 'id'])
|
||||
.filter(n => defaultColumnsNames.indexOf(n) > -1)
|
||||
|
||||
// 应该显示的列
|
||||
|
||||
@@ -1,45 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<MFAVerifyDialog
|
||||
v-if="mfaDialogShow"
|
||||
@MFAVerifyDone="showExportDialog"
|
||||
@MFAVerifyCancel="handleExportCancel"
|
||||
/>
|
||||
<Dialog
|
||||
v-if="exportDialogShow"
|
||||
:title="$t('common.Export')"
|
||||
:visible.sync="exportDialogShow"
|
||||
:destroy-on-close="true"
|
||||
@confirm="handleExportConfirm()"
|
||||
@cancel="handleExportCancel()"
|
||||
>
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</div>
|
||||
<Dialog v-if="showExportDialog" :title="$t('common.Export')" :visible.sync="showExportDialog" :destroy-on-close="true" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
||||
<el-form label-position="left" style="padding-left: 50px">
|
||||
<el-form-item :label="$t('common.fileType' )" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportTypeOption">
|
||||
<el-radio v-for="option of exportTypeOptions" :key="option.value" style="padding: 10px 20px;" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="export-form" :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
||||
<el-radio-group v-model="exportOption">
|
||||
<el-radio v-for="option of exportOptions" :key="option.value" class="export-item" :label="option.value" :disabled="!option.can">{{ option.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MFAVerifyDialog from '@/components/MFAVerifyDialog'
|
||||
import { createSourceIdCache } from '@/api/common'
|
||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
||||
|
||||
export default {
|
||||
name: 'ExportDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
MFAVerifyDialog
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
selectedRows: {
|
||||
@@ -48,15 +32,7 @@ export default {
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
beforeExport: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
mfaVerifyRequired: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: () => ''
|
||||
},
|
||||
performExport: {
|
||||
type: Function,
|
||||
@@ -79,12 +55,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exportDialogShow: false,
|
||||
showExportDialog: false,
|
||||
exportOption: 'all',
|
||||
exportTypeOption: 'csv',
|
||||
meta: {},
|
||||
mfaVerified: false,
|
||||
mfaDialogShow: false
|
||||
meta: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -141,26 +115,13 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url, name }) => {
|
||||
// Todo: 没有时间了,只能先这么处理了
|
||||
if (url === this.url || url.indexOf('account') > -1) {
|
||||
this.showExportDialog()
|
||||
this.$eventBus.$on('showExportDialog', ({ selectedRows, url }) => {
|
||||
if (url === this.url) {
|
||||
this.showExportDialog = true
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
showExportDialog() {
|
||||
if (!this.mfaVerifyRequired) {
|
||||
this.exportDialogShow = true
|
||||
return
|
||||
}
|
||||
// 这是需要校验 MFA 的
|
||||
if (!this.mfaDialogShow) {
|
||||
this.mfaDialogShow = true
|
||||
} else {
|
||||
this.exportDialogShow = true
|
||||
}
|
||||
},
|
||||
downloadCsv(url) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
@@ -178,6 +139,11 @@ export default {
|
||||
}
|
||||
const spm = await createSourceIdCache(resources)
|
||||
query['spm'] = spm.spm
|
||||
} else if (exportOption === 'filtered') {
|
||||
// console.log(listTableRef)
|
||||
// console.log(listTableRef.dataTable)
|
||||
// delete query['limit']
|
||||
// delete query['offset']
|
||||
}
|
||||
query['format'] = this.exportTypeOption
|
||||
const queryStr =
|
||||
@@ -190,17 +156,14 @@ export default {
|
||||
const query = listTableRef.dataTable.getQuery()
|
||||
delete query['limit']
|
||||
delete query['offset']
|
||||
await this.beforeExport()
|
||||
return this.performExport(this.selectedRows, this.exportOption, query)
|
||||
},
|
||||
async handleExportConfirm() {
|
||||
await this.handleExport()
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
},
|
||||
handleExportCancel() {
|
||||
this.exportDialogShow = false
|
||||
this.mfaDialogShow = false
|
||||
this.showExportDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ExportDialog :selected-rows="selectedRows" v-bind="exportOptions" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" v-bind="importOptions" v-on="$listeners" />
|
||||
<ExportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
<ImportDialog :selected-rows="selectedRows" :url="url" v-bind="$attrs" v-on="$listeners" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@ import ExportDialog from './ExportDialog'
|
||||
import ImportDialog from './ImportDialog'
|
||||
|
||||
export default {
|
||||
name: 'ImExportDialog',
|
||||
name: 'DialogAction',
|
||||
components: {
|
||||
ExportDialog,
|
||||
ImportDialog
|
||||
@@ -20,13 +20,9 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
url: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
<script>
|
||||
import DataTable from '@/components/DataTable'
|
||||
import { sleep, getUpdateObjURL } from '@/utils/common'
|
||||
import { sleep } from '@/utils/common'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/TableFormatters'
|
||||
export default {
|
||||
name: 'ImportTable',
|
||||
@@ -350,7 +350,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async performUpdateObject(item) {
|
||||
const updateUrl = getUpdateObjURL(this.url, item.id)
|
||||
const updateUrl = `${this.url}${item.id}/`
|
||||
return this.$axios.put(
|
||||
updateUrl,
|
||||
item,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog
|
||||
:selected-rows="selectedRows"
|
||||
:export-options="iExportOptions"
|
||||
:import-options="iImportOptions"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<ActionsGroup :is-fa="true" :actions="rightSideActions" :url="tableUrl" class="right-side-actions right-side-item" />
|
||||
<ImExportDialog :selected-rows="selectedRows" :url="tableUrl" v-bind="$attrs" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +9,6 @@
|
||||
import ActionsGroup from '@/components/ActionsGroup'
|
||||
import ImExportDialog from './ImExportDialog'
|
||||
import { cleanActions } from './utils'
|
||||
import { assignIfNot } from '@/utils/common'
|
||||
|
||||
const defaultTrue = { type: Boolean, default: true }
|
||||
|
||||
@@ -30,32 +24,24 @@ export default {
|
||||
default: ''
|
||||
},
|
||||
hasExport: defaultTrue,
|
||||
exportOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleExportClick: {
|
||||
handleExport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
this.$eventBus.$emit('showExportDialog', { selectedRows, url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasImport: defaultTrue,
|
||||
importOptions: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
handleImportClick: {
|
||||
handleImport: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows, url: this.tableUrl, name: this.name })
|
||||
this.$eventBus.$emit('showImportDialog', { selectedRows, url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasColumnSetting: defaultTrue,
|
||||
handleTableSettingClick: {
|
||||
handleColumnConfig: {
|
||||
type: Function,
|
||||
default: function({ selectedRows }) {
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl, row: selectedRows, name: this.name })
|
||||
this.$eventBus.$emit('showColumnSettingPopover', { url: this.tableUrl })
|
||||
}
|
||||
},
|
||||
hasRefresh: defaultTrue,
|
||||
@@ -75,12 +61,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
defaultRightSideActions: [
|
||||
{ name: 'actionColumnSetting', fa: 'fa-cog', tip: this.$t('common.CustomCol'), has: this.hasColumnSetting, callback: this.handleTableSettingClick.bind(this) },
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExportClick.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImportClick.bind(this) },
|
||||
{ name: 'actionColumnSetting', fa: 'fa-cog', tip: this.$t('common.CustomCol'), has: this.hasColumnSetting, callback: this.handleColumnConfig.bind(this) },
|
||||
{ name: 'actionExport', fa: 'fa-download', tip: this.$t('common.Export'), has: this.hasExport, callback: this.handleExport.bind(this) },
|
||||
{ name: 'actionImport', fa: 'fa-upload', tip: this.$t('common.Import'), has: this.hasImport, callback: this.handleImport.bind(this) },
|
||||
{ name: 'actionRefresh', fa: 'fa-refresh', tip: this.$t('common.Refresh'), has: this.hasRefresh, callback: this.handleRefresh }
|
||||
],
|
||||
dialogExportVisible: false
|
||||
dialogExportVisible: false,
|
||||
exportValue: 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -94,19 +81,18 @@ export default {
|
||||
},
|
||||
hasSelectedRows() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
iImportOptions() {
|
||||
return assignIfNot(this.importOptions, { url: this.tableUrl })
|
||||
},
|
||||
iExportOptions() {
|
||||
const options = assignIfNot(this.exportOptions, { url: this.tableUrl })
|
||||
return options
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTagSearch(val) {
|
||||
this.searchTable(val)
|
||||
},
|
||||
// handleExport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showExportDialog', { selectedRows })
|
||||
// },
|
||||
// handleImport({ selectedRows }) {
|
||||
// this.$eventBus.$emit('showImportDialog', { selectedRows })
|
||||
// },
|
||||
handleRefresh() {
|
||||
this.reloadTable()
|
||||
}
|
||||
|
||||
@@ -113,6 +113,15 @@ export default {
|
||||
& >>> .el-table__header thead > tr > th {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/*& >>> .el-table--striped .el-table__body tr.el-table__row--striped td {*/
|
||||
/*background: white;*/
|
||||
/*}*/
|
||||
|
||||
/*& >>> .el-table th, .el-table tr {*/
|
||||
/*background-color: red;*/
|
||||
/*!*background-color: #FAFAFA;*!*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
//修改颜色
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:title="$t('common.MFAVerify')"
|
||||
:width="'50'"
|
||||
:show-confirm="false"
|
||||
:show-cancel="false"
|
||||
:visible.sync="visible"
|
||||
:destroy-on-close="true"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAToken" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="verifyMFA">
|
||||
{{ this.$t('common.Confirm') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
|
||||
export default {
|
||||
name: 'MFAVerifyDialog',
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
MFAToken: '',
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (!val) {
|
||||
this.$emit('MFAVerifyCancel', true)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$axios.get('/api/v1/authentication/otp/verify/', { disableFlashErrorMsg: true }).then(() => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
}).catch(err => {
|
||||
this.$log.debug('Verify otp code error: ', err)
|
||||
this.visible = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
verifyMFA() {
|
||||
if (this.MFAToken.length !== 6) {
|
||||
return this.$message.error(this.$tc('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAToken
|
||||
}
|
||||
).then(res => {
|
||||
this.$emit('MFAVerifyDone', true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -4,16 +4,13 @@
|
||||
<span class="header-title">{{ title }}</span>
|
||||
<span class="pull-right right-side">
|
||||
<slot name="header-right">
|
||||
<el-tag :type="rightSideLabel.type || 'success'" effect="dark" size="mini">
|
||||
{{ rightSideLabel.title }}
|
||||
</el-tag>
|
||||
<el-tag :type="rightSideLabel.type || 'success'" effect="dark" size="mini">{{ rightSideLabel.title }}</el-tag>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
<slot>
|
||||
<h1 class="no-margins">
|
||||
<span v-if="body.disabled" class="disabled-link">{{ body.count }}</span>
|
||||
<router-link v-else :to="body.route">
|
||||
<router-link :to="body.route">
|
||||
<span>{{ body.count }}</span>
|
||||
</router-link>
|
||||
</h1>
|
||||
@@ -77,8 +74,4 @@ export default {
|
||||
.no-margins {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.disabled-link {
|
||||
color: #428bca;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,12 +8,9 @@ import BaseFormatter from './base'
|
||||
|
||||
const defaultPerformDelete = function({ row, col }) {
|
||||
const id = row.id
|
||||
const url = new URL(this.url, location.origin)
|
||||
url.pathname += `${id}/`
|
||||
const deleteUrl = url.href
|
||||
return this.$axios.delete(deleteUrl)
|
||||
const url = `${this.url}${id}/`
|
||||
return this.$axios.delete(url)
|
||||
}
|
||||
|
||||
const defaultUpdateCallback = function({ row, col }) {
|
||||
const id = row.id
|
||||
let route = { params: { id: id }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip v-if="formatterArgs.hasTips" placement="bottom" effect="dark">
|
||||
<div slot="content" v-html="tips" />
|
||||
<div slot="content">{{ tipStatus }}<br>{{ tipTime }}</div>
|
||||
<i :class="'fa ' + iconClass" />
|
||||
</el-tooltip>
|
||||
<i v-else :class="'fa ' + iconClass" />
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<script>
|
||||
import BaseFormatter from './base'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
export default {
|
||||
name: 'ChoicesFormatter',
|
||||
extends: BaseFormatter,
|
||||
@@ -22,12 +23,21 @@ export default {
|
||||
true: 'fa-check text-primary',
|
||||
false: 'fa-times text-danger'
|
||||
},
|
||||
getIconKey({ row, cellValue }) {
|
||||
return cellValue
|
||||
typeChange(val) {
|
||||
return !!val
|
||||
},
|
||||
hasTips: false,
|
||||
getTips: ({ row, cellValue }) => {
|
||||
return cellValue
|
||||
tipStatus(val, vm) {
|
||||
if (!val) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
if (val.status === 0) {
|
||||
return vm.$t('assets.Unreachable')
|
||||
} else if (val.status === 1) {
|
||||
return vm.$t('assets.Reachable')
|
||||
} else if (val.status === 2) {
|
||||
return vm.$t('assets.Unknown')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,11 +50,18 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
iconClass() {
|
||||
const key = this.formatterArgs.getIconKey({ row: this.row, cellValue: this.cellValue })
|
||||
const key = this.formatterArgs.typeChange(this.cellValue)
|
||||
return this.formatterArgs.iconChoices[key]
|
||||
},
|
||||
tips() {
|
||||
return this.formatterArgs.getTips({ cellValue: this.cellValue, row: this.row })
|
||||
tipStatus() {
|
||||
const vm = this
|
||||
return this.formatterArgs.tipStatus(this.cellValue, vm)
|
||||
},
|
||||
tipTime() {
|
||||
if (!this.cellValue) {
|
||||
return ''
|
||||
}
|
||||
return toSafeLocalDateStr(this.cellValue.datetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
defaultOnDelete(col, row, cellValue, reload) {
|
||||
const url = col.deleteUrl + cellValue
|
||||
this.$axios.delete(url).then(res => {
|
||||
this.$message.success(this.$tc('common.deleteSuccessMsg'))
|
||||
this.$message.success(this.$t('common.deleteSuccessMsg'))
|
||||
reload()
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.deleteErrorMsg') + ' ' + error)
|
||||
|
||||
@@ -22,6 +22,5 @@ export { default as QuickActions } from './QuickActions'
|
||||
export { default as Switcher } from './FormFields/Swicher'
|
||||
export { default as SummaryCard } from './SummaryCard'
|
||||
export { default as UploadField } from './FormFields/UploadField'
|
||||
export { default as AccountListTable } from './AccountListTable/index'
|
||||
export { default as AssetUserTable } from './AssetUserTable'
|
||||
export { default as AssetRelationCard } from './AssetRelationCard'
|
||||
export { default as MFAVerifyDialog } from './MFAVerifyDialog'
|
||||
|
||||
@@ -83,13 +83,11 @@
|
||||
"DBInfo": "数据库信息"
|
||||
},
|
||||
"assets": {
|
||||
"AssociateAssets": "关联资产",
|
||||
"AssociateNodes": "关联节点",
|
||||
"Action": "动作",
|
||||
"ActiveSelected": "激活所选",
|
||||
"AdminUser": "特权用户",
|
||||
"AdminUser": "管理用户",
|
||||
"AdminUserDetail": "管理用户详情",
|
||||
"AdminUserListHelpMessage": "<b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, 如 root 或 拥有 `NOPASSWD: ALL` sudo 权限的用户。 JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。",
|
||||
"AdminUserListHelpMessage": "管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。\n",
|
||||
"Asset": "资产",
|
||||
"HardwareInfo": "硬件信息",
|
||||
"AssetDetail": "资产详情",
|
||||
@@ -100,11 +98,11 @@
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
"SshPort": "SSH 端口",
|
||||
"AssetNumber": "资产编号",
|
||||
"AssetUserList": "账号列表",
|
||||
"AssetUserList": "资产用户列表",
|
||||
"Assets": "资产",
|
||||
"Auth": "认证",
|
||||
"AccountList": "账号列表",
|
||||
"AutoGenerateKey": "自动生成",
|
||||
"AutoGenerateKey": "自动生成密钥",
|
||||
"AutoPush": "自动推送",
|
||||
"BasePlatform": "基础平台",
|
||||
"Basic": "基本",
|
||||
@@ -117,7 +115,6 @@
|
||||
"CommandFilterRules": "命令过滤器规则",
|
||||
"Comment": "备注",
|
||||
"Cpu": "CPU",
|
||||
"CommonUser": "普通用户",
|
||||
"CreatedBy": "创建者",
|
||||
"Database": "数据库",
|
||||
"DateJoined": "创建日期",
|
||||
@@ -159,7 +156,6 @@
|
||||
"PriorityHelpMessage": "1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为默认登录用户",
|
||||
"Protocol": "协议",
|
||||
"Protocols": "协议组",
|
||||
"LoginOption": "登录选项",
|
||||
"PublicIp": "公网IP",
|
||||
"Push": "推送",
|
||||
"PushSystemUserNow": "推送系统用户",
|
||||
@@ -182,8 +178,7 @@
|
||||
"PasswordHelpMessage": "密码或密钥密码",
|
||||
"SystemUser": "系统用户",
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserListHelpMessage": "<b>系统用户</b> 是JumpServer 登录资产时使用的账号,如 root `ssh root@host`,而不是使用该用户名登录资产(ssh admin@host)`;<br><b>特权用户</b> 是资产已存在的, 并且拥有 高级权限 的系统用户, JumpServer 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等;</br><b>普通用户</b> 可以在资产上预先存在,也可以由 特权用户 来自动创建。",
|
||||
"DynamicUsername": "动态用户名",
|
||||
"SystemUserListHelpMessage": "系统用户是 JumpServer 跳转登录资产时使用的用户,可以理解为登录资产用户,如 web,sa,dba(`ssh web@some-host`),而不是使用某个用户的用户名跳转登录服务器(`ssh xiaoming@some-host`); 简单来说是用户使用自己的用户名登录 JumpServer,JumpServer 使用系统用户登录资产。 系统用户创建时,如果选择了自动推送,JumpServer 会使用 Ansible 自动推送系统用户到资产中,如果资产(交换机)不支持 Ansible,请手动填写账号密码。\n",
|
||||
"SystemUsers": "系统用户",
|
||||
"Test": "测试",
|
||||
"TestAssetsConnective": "测试资产可连接性",
|
||||
@@ -191,7 +186,7 @@
|
||||
"Type": "类型",
|
||||
"UnselectedAssets": "未选择资产或所选择的资产不支持SSH协议连接",
|
||||
"UnselectedNodes": "未选择节点",
|
||||
"UpdateAssetUserToken": "更新账号认证信息",
|
||||
"UpdateAssetUserToken": "更新资产用户认证信息",
|
||||
"Username": "用户名",
|
||||
"UsernameHelpMessage": "用户名是动态的,登录资产时使用当前用户的用户名登录",
|
||||
"Value": "值",
|
||||
@@ -202,7 +197,6 @@
|
||||
"sshKeyFingerprint": "SSH 指纹",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
@@ -226,8 +220,6 @@
|
||||
"ReLogin": "重新登录"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "验证 MFA",
|
||||
"ViewSecret": "查看密码",
|
||||
"ConnectWebSocketError": "连接 WebSocket 失败",
|
||||
"Action": "动作",
|
||||
"RequestTickets": "申请工单",
|
||||
@@ -646,7 +638,6 @@
|
||||
"SystemUserDetail": "系统用户详情",
|
||||
"SystemUserList": "系统用户",
|
||||
"SystemUserUpdate": "更新系统用户",
|
||||
"AssetUserList": "资产用户",
|
||||
"TaskDetail": "任务详情",
|
||||
"TaskList": "任务列表",
|
||||
"TaskMonitor": "任务监控",
|
||||
@@ -672,10 +663,6 @@
|
||||
"SiteMessageList": "站内信"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "设置为默认存储",
|
||||
"SetToDefault": "设为默认",
|
||||
"SetSuccess": "设置成功",
|
||||
"SetFailed": "设置失败",
|
||||
"StorageConfiguration": "存储配置",
|
||||
"accountKey": "账户密钥",
|
||||
"accountName": "账户名称",
|
||||
@@ -1120,8 +1107,6 @@
|
||||
"Cloud": {
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"QingyunPrivatecloud": "青云私有云",
|
||||
"HuaweiPrivatecloud": "华为私有云",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
@@ -1149,7 +1134,7 @@
|
||||
"Name": "名称",
|
||||
"Account":"账户",
|
||||
"Node": "节点",
|
||||
"AdminUser":"特权用户",
|
||||
"AdminUser":"管理用户",
|
||||
"Periodic":"执行周期",
|
||||
"PeriodicPerform":"定时执行",
|
||||
"RegularlyPerform": "定期执行",
|
||||
@@ -1211,7 +1196,7 @@
|
||||
"users_amount": "用户数量",
|
||||
"groups_amount": "用户组数量",
|
||||
"assets_amount": "资产数量",
|
||||
"admin_users_amount": "特权用户数量",
|
||||
"admin_users_amount": "管理用户数量",
|
||||
"system_users_amount": "系统用户数量",
|
||||
"applications_amount": "应用数量",
|
||||
"asset_perms_amount": "资产授权数量",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"": "",
|
||||
"accounts": {
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftAssetToViewAssetAccount": "Asset account list, please click on the assets on the left to view",
|
||||
"PleaseClickLeftApplicationToViewApplicationAccount": "Application account list, please click on the application on the left to view",
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
"PleaseClickLeftAssetToViewGatheredUser": "Gathered user list, please click on the assets on the left to view"
|
||||
},
|
||||
"acl": {
|
||||
"name": "Name",
|
||||
@@ -82,20 +82,16 @@
|
||||
"DBInfo": "Database Info"
|
||||
},
|
||||
"assets": {
|
||||
"AssociateAssets": "Associate assets",
|
||||
"AssociateNodes": "Associate nodes",
|
||||
"Action": "Action",
|
||||
"ActiveSelected": "Active selected",
|
||||
"AdminUser": "Admin user",
|
||||
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"DynamicUsername": "Dynamic username",
|
||||
"AdminUserListHelpMessage": "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, JumpServer users of the system using the user to `push system user`, `get assets hardware information`, etc.\n",
|
||||
"Asset": "Asset",
|
||||
"HardwareInfo": "Hardware info",
|
||||
"Hardware": "Hardware",
|
||||
"AccountList": "Account list",
|
||||
"LoginOption": "Login option",
|
||||
"AssetDetail": "Asset detail",
|
||||
"AssetList": "Asset list",
|
||||
"AssetListHelpMessage": "The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node\n",
|
||||
@@ -119,7 +115,6 @@
|
||||
"CommandFilterRules": "Command filter rules",
|
||||
"Comment": "Comment",
|
||||
"Cpu": "Cpu",
|
||||
"CommonUser": "Common user",
|
||||
"CreatedBy": "Created by",
|
||||
"Database": "Database",
|
||||
"DateJoined": "Date joined",
|
||||
@@ -178,11 +173,11 @@
|
||||
"Rules": "Rules",
|
||||
"SFTPHelpMessage": "SFTP root dir, tmp, home or custom",
|
||||
"SerialNumber": "Serial number",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami, /bin/ifconfig",
|
||||
"SudoHelpMessage": "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig",
|
||||
"PasswordHelpMessage": "Password or private key password",
|
||||
"SystemUser": "System user",
|
||||
"SystemUserDetail": "System user detail",
|
||||
"SystemUserListHelpMessage": "<b>System user</b> is the account JumpServer used to log into the asset, such as using root `ssh root@host`, rather than the current user username(ssh admin@host)`;<br><b>Admin user</b> is the account that already exists on an asset, and have privileged permissions, JumpServer using this create common system user, and gather hardware Etc;</br><b>Common user</b> can pre-exist assets or created automatically by the admin user.",
|
||||
"SystemUserListHelpMessage": "System user is JumpServer jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); In simple terms, users log into JumpServer using their own username, and JumpServer uses system users to log into assets. When system users are created, if you choose auto push JumpServer to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.\n",
|
||||
"SystemUsers": "System users",
|
||||
"Test": "Test",
|
||||
"TestAssetsConnective": "Test assets connective",
|
||||
@@ -224,8 +219,6 @@
|
||||
"ReLogin": "Re-Login"
|
||||
},
|
||||
"common": {
|
||||
"MFAVerify": "Verify MFA",
|
||||
"ViewSecret": "View secret",
|
||||
"ConnectWebSocketError": "Connect Websocket failed",
|
||||
"Nothing": "Nothing",
|
||||
"Action": "Action",
|
||||
@@ -257,7 +250,7 @@
|
||||
"Confirm": "Confirm",
|
||||
"Create": "Create",
|
||||
"CreatedBy": "Created by",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set, give priority to Regularly perform",
|
||||
"CrontabHelpTips": "eg: Every Sunday 03:05 run <5 3 * * 0> <br>Tips:Using 5 digits linux crontab expressions<min hour day month week> (<a href='https://tool.lu/crontab/' target='_blank'>Online tools</a>) <br>Note:If both Regularly perform and Cycle perform are set,give priority to Regularly perform",
|
||||
"DateEnd": "End date",
|
||||
"Resource": "Resource",
|
||||
"DateLast24Hours": "Last 24 hours",
|
||||
@@ -299,7 +292,7 @@
|
||||
"RemoveSuccessMsg": "Remove success",
|
||||
"Reset": "Reset",
|
||||
"Search": "Search",
|
||||
"MFAErrorMsg": "MFA Error, please check",
|
||||
"MFAErrorMsg": "MFA Error,please check",
|
||||
"InputEmailAddress": "Please enter your email address",
|
||||
"Select": "Select",
|
||||
"SelectFile": "Select file",
|
||||
@@ -668,10 +661,6 @@
|
||||
"SiteMessageList": "Site message"
|
||||
},
|
||||
"sessions": {
|
||||
"SetToDefaultStorage": "Set to default storage",
|
||||
"SetToDefault": "Set to default",
|
||||
"SetSuccess": "Set success",
|
||||
"SetFailed": "Set failed",
|
||||
"StorageConfiguration": "Storage configuration",
|
||||
"accountKey": "Account key",
|
||||
"accountName": "Account name",
|
||||
@@ -736,7 +725,7 @@
|
||||
"sessionMonitor": "Session Monitor",
|
||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
||||
"helpText": {
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (, ) to split (eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)",
|
||||
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||
"esIndex":"Es provides the default index: jumpserver",
|
||||
"esDocType": "Es provides the default document type: command",
|
||||
"s3Endpoint": "S3: http://s3.{REGION_NAME}.amazonaws.com<br>S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn<br>Example: http://s3.cn-north-1.amazonaws.com.cn",
|
||||
@@ -803,7 +792,7 @@
|
||||
"ApiKeyList": "The API key is used to sign the request header. The header of each request is different. Please refer to the usage documentation",
|
||||
"authLdapSearchFilter": "Choice may be (cn|uid|sAMAccountName)=%(user)s)",
|
||||
"authLdapSearchOu": "Use | split User OUs",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username, name, email is jumpserver attr",
|
||||
"authLdapUserAttrMap": "User attr map present how to map LDAP user attr to jumpserver, username,name,email is jumpserver attr",
|
||||
"emailCustomUserCreatedBody": "Tips:When creating a user, send the content of the email",
|
||||
"emailCustomUserCreatedHonorific": "Tips: When creating a user, send the honorific of the email (eg:Hello)",
|
||||
"emailCustomUserCreatedSignature": "Tips: Email signature (eg:jumpserver)",
|
||||
@@ -1112,8 +1101,6 @@
|
||||
"Cloud": {
|
||||
"Aliyun": "Ali Cloud",
|
||||
"Qcloud": "Tencent Cloud",
|
||||
"QingyunPrivatecloud": "Qingyun Private Cloud",
|
||||
"HuaweiPrivatecloud": "Huawei Private Cloud",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
"HuaweiCloud": "Huawei Cloud",
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
</template>
|
||||
<script>
|
||||
import AutoDataForm from '@/components/AutoDataForm'
|
||||
import { getUpdateObjURL } from '@/utils/common'
|
||||
export default {
|
||||
name: 'GenericCreateUpdateForm',
|
||||
components: {
|
||||
@@ -130,7 +129,7 @@ export default {
|
||||
const params = this.$route.params
|
||||
let url = this.url
|
||||
if (params.id) {
|
||||
url = getUpdateObjURL(url, params.id)
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ListTable ref="ListTable" v-bind="$attrs" v-on="$listeners" />
|
||||
<ListTable ref="ListTable" v-bind="iAttrs" v-on="$listeners" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -11,15 +11,16 @@ export default {
|
||||
ListTable
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot'])
|
||||
},
|
||||
created() {
|
||||
const headerActions = this.$attrs['header-actions'] || {}
|
||||
if (headerActions.canCreate === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.canCreate', false)
|
||||
}
|
||||
if (headerActions.hasImport === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.hasImport', false)
|
||||
...mapGetters(['currentOrgIsRoot']),
|
||||
iAttrs() {
|
||||
const attrs = _.cloneDeep(this.$attrs)
|
||||
const canCreate = _.get(attrs, 'header-actions.canCreate', null)
|
||||
this.$log.debug('Can create: ', canCreate)
|
||||
if (canCreate === null && this.currentOrgIsRoot) {
|
||||
_.set(attrs, 'header-actions.canCreate', false)
|
||||
}
|
||||
this.$log.debug('List table Attrs: ', attrs)
|
||||
return attrs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<template>
|
||||
<Page v-bind="$attrs">
|
||||
<Page>
|
||||
<el-alert v-if="helpMessage" type="success"> {{ helpMessage }} </el-alert>
|
||||
<TreeTable
|
||||
ref="TreeTable"
|
||||
v-bind="$attrs"
|
||||
:table-config="tableConfig"
|
||||
:header-actions="iHeaderActions"
|
||||
:tree-setting="treeSetting"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<template #table>
|
||||
@@ -24,16 +27,23 @@ export default {
|
||||
components: {
|
||||
Page, TreeTable
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrgIsRoot'])
|
||||
},
|
||||
created() {
|
||||
const headerActions = this.$attrs['header-actions'] || {}
|
||||
if (headerActions.canCreate === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.canCreate', false)
|
||||
props: {
|
||||
...TreeTable.props,
|
||||
helpMessage: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
if (headerActions.hasImport === undefined && this.currentOrgIsRoot) {
|
||||
_.set(this.$attrs, 'header-actions.hasImport', false)
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentOrg']),
|
||||
iHeaderActions() {
|
||||
const attrs = _.cloneDeep(this.headerActions)
|
||||
const canCreate = _.get(attrs, 'canCreate', null)
|
||||
// this.$log.debug('Current org: ', this.currentOrg)
|
||||
if (canCreate === null && this.currentOrg && this.currentOrg.is_root) {
|
||||
_.set(attrs, 'canCreate', false)
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<el-option
|
||||
v-for="item in userAdminOrgList"
|
||||
:key="item.id"
|
||||
:selected="item.id === currentOrg.id"
|
||||
selected="item.id == currentOrg.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
@@ -48,8 +48,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
needShow() {
|
||||
const hasValidLicense = this.$store.getters.hasValidLicense
|
||||
return !this.isCollapse && this.inAdminPage && hasValidLicense
|
||||
const otherOrgs = this.userAdminOrgList.filter(org => {
|
||||
return !org.is_root && !org.is_default
|
||||
})
|
||||
return !this.isCollapse && otherOrgs.length > 0 && this.inAdminPage
|
||||
},
|
||||
changeOrg(orgId) {
|
||||
orgUtil.changeOrg(orgId)
|
||||
|
||||
@@ -7,7 +7,6 @@ export { default as GenericDetailPage } from './GenericDetailPage'
|
||||
export { default as TabPage } from './TabPage'
|
||||
export { default as Footer } from './Footer'
|
||||
export { default as GenericListPage } from './GenericListPage'
|
||||
export { default as GenericListTable } from './GenericListTable'
|
||||
export { default as GenericCreateUpdatePage } from './GenericCreateUpdatePage'
|
||||
export { default as GenericCreateUpdateForm } from './GenericCreateUpdateForm'
|
||||
export { default as GenericUpdateFormDialog } from './GenericUpdateFormDialog'
|
||||
|
||||
@@ -101,6 +101,41 @@ export default [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'admin-users',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('route.AdminUserList') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'AdminUserList',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserList'),
|
||||
meta: { title: i18n.t('route.AdminUserList'), activeMenu: '/assets/admin-users' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'AdminUserCreate',
|
||||
meta: { title: i18n.t('route.AdminUserCreate'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserCreateUpdate.vue'), // Parent router-view
|
||||
name: 'AdminUserUpdate',
|
||||
meta: { title: i18n.t('route.AdminUserUpdate'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/assets/AdminUser/AdminUserDetail/index.vue'), // Parent router-view
|
||||
name: 'AdminUserDetail',
|
||||
meta: { title: i18n.t('route.AdminUserDetail'), activeMenu: '/assets/admin-users' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'system-users',
|
||||
component: empty,
|
||||
@@ -110,20 +145,20 @@ export default [
|
||||
{
|
||||
path: '',
|
||||
name: 'SystemUserList',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserList/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserList.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserList'), activeMenu: '/assets/system-users' }
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
name: 'SystemUserCreate',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserCreate'), activeMenu: '/assets/system-users' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'SystemUserUpdate',
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate/index.vue'),
|
||||
component: () => import('@/views/assets/SystemUser/SystemUserCreateUpdate.vue'),
|
||||
meta: { title: i18n.t('route.SystemUserUpdate'), activeMenu: '/assets/system-users' },
|
||||
hidden: true
|
||||
},
|
||||
|
||||
@@ -20,9 +20,7 @@ const getDefaultState = () => {
|
||||
orgs: [],
|
||||
perms: 0b00000000,
|
||||
MFAVerifyAt: null,
|
||||
isSuperAdmin: false,
|
||||
hasAdminPerm: false,
|
||||
hasAuditPerm: false
|
||||
isSuperAdmin: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +53,12 @@ const mutations = {
|
||||
},
|
||||
SET_ROLES(state, roles) {
|
||||
state.roles = roles
|
||||
// rolec.PERM_ADMIN &
|
||||
},
|
||||
SET_SYS_ROLE(state, role) {
|
||||
state.sysRole = role
|
||||
},
|
||||
SET_PERMS(state, perms) {
|
||||
state.perms = perms
|
||||
state.hasAdmin = (perms & rolec.PERM_ADMIN) === rolec.PERM_ADMIN
|
||||
state.hasAudit = (perms & rolec.PERM_AUDIT) === rolec.PERM_AUDIT
|
||||
},
|
||||
SET_CURRENT_ORG(state, org) {
|
||||
saveCurrentOrgToCookie(org)
|
||||
|
||||
@@ -214,22 +214,11 @@ export function newURL(url) {
|
||||
if (url.indexOf('//') > -1) {
|
||||
obj = new URL(url)
|
||||
} else {
|
||||
obj = new URL(url, location.origin)
|
||||
obj = new URL(url, 'http://localhost')
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
export function getUpdateObjURL(url, objId) {
|
||||
const urlObj = new URL(url, location.origin)
|
||||
let pathname = urlObj.pathname
|
||||
if (!pathname.endsWith('/')) {
|
||||
pathname += '/'
|
||||
}
|
||||
pathname += `${objId}/`
|
||||
urlObj.pathname = pathname
|
||||
return urlObj.href
|
||||
}
|
||||
|
||||
export const assignIfNot = _.partialRight(_.assignInWith, customizer)
|
||||
|
||||
const scheme = document.location.protocol
|
||||
|
||||
@@ -17,16 +17,31 @@
|
||||
<GenericListTable
|
||||
ref="LeftTable"
|
||||
class="asset-table"
|
||||
v-bind="leftTable"
|
||||
:header-actions="leftTable.headerActions"
|
||||
:table-config="leftTable.tableConfig"
|
||||
@row-click="leftTable.tableConfig.rowClick"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="iShowTree?11:13">
|
||||
<AccountListTable
|
||||
<AssetUserTable
|
||||
v-if="!isInit"
|
||||
ref="RightTable"
|
||||
class="asset-user-table"
|
||||
v-bind="rightTable"
|
||||
:url="rightTable.url"
|
||||
:search-exclude="rightTable.searchExclude"
|
||||
:extra-query="rightTable.extraQuery"
|
||||
:has-left-actions="rightTable.hasLeftActions"
|
||||
:table-config="rightTable.tableConfig"
|
||||
:has-clone="false"
|
||||
:has-import="false"
|
||||
/>
|
||||
<div v-else class="noDataR">
|
||||
<div class="hintWrap">
|
||||
<div>
|
||||
{{ $t('accounts.PleaseClickLeftAssetToViewAssetAccount') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Page>
|
||||
@@ -36,13 +51,13 @@
|
||||
import Page from '@/layout/components/Page'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||
import { AccountListTable } from '@/components'
|
||||
import { AssetUserTable } from '@/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetAccountList',
|
||||
components: {
|
||||
AutoDataZTree, GenericListTable, Page, AccountListTable
|
||||
AutoDataZTree, GenericListTable, Page, AssetUserTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
@@ -101,8 +116,8 @@ export default {
|
||||
}
|
||||
},
|
||||
rowClick: function(row, column, event) {
|
||||
vm.rightTable.url = `/api/v1/assets/accounts/?asset=${row.id}`
|
||||
vm.rightTable.extraQuery.asset = row.id
|
||||
vm.rightTable.url = `/api/v1/assets/asset-users/?asset_id=${row.id}`
|
||||
vm.rightTable.extraQuery.asset_id = row.id
|
||||
vm.clickedRow = row
|
||||
vm.isInit = false
|
||||
}
|
||||
@@ -119,11 +134,33 @@ export default {
|
||||
}
|
||||
},
|
||||
rightTable: {
|
||||
url: `/api/v1/assets/accounts/`,
|
||||
name: 'AssetAccountListTable',
|
||||
url: `/api/v1/assets/asset-users/?hostname=ShowFirstAssetRelated`,
|
||||
extraQuery: {
|
||||
latest: 1
|
||||
},
|
||||
tableConfig: {
|
||||
columns: ['name', 'username', 'version', 'backend_display', 'date_created', 'org_name', 'actions'],
|
||||
columnsShow: {
|
||||
min: ['username', 'actions'],
|
||||
default: ['name', 'username', 'version', 'backend_display', 'date_created', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: null,
|
||||
showOverflowTooltip: true,
|
||||
sortable: false
|
||||
}
|
||||
},
|
||||
tableAttrs: {
|
||||
stripe: true, // 斑马纹表格
|
||||
border: false, // 表格边框
|
||||
fit: true, // 宽度自适应,
|
||||
tooltipEffect: 'dark',
|
||||
rowClassName({ row, rowIndex }) {
|
||||
return 'row-background-color'
|
||||
}
|
||||
}
|
||||
},
|
||||
hasLeftActions: false,
|
||||
searchExclude: ['hostname', 'id', 'ip']
|
||||
}
|
||||
|
||||
@@ -20,10 +20,6 @@ export default {
|
||||
'name', 'username', 'assets_amount', 'nodes_amount', 'password_strategy_display',
|
||||
'periodic_display', 'run_times', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'password_strategy_display', 'periodic_display', 'run_times', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
|
||||
@@ -34,7 +34,7 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.PeriodicPerform'),
|
||||
value: this.object.is_periodic ? (this.$t('xpack.Cloud.True')) : (this.$t('xpack.Cloud.False'))
|
||||
value: this.object.is_periodic ? (this.$t('xpack.GatherUser.True')) : (this.$t('xpack.GatherUser.False'))
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.Cloud.Periodic'),
|
||||
|
||||
@@ -13,17 +13,10 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/acls/login-asset-acls/',
|
||||
columns: [
|
||||
'name', 'user_username_group', 'hostname_group', 'ip_group', 'name_group',
|
||||
'protocol_group', 'systemuser_username_group', 'reviewers', 'priority',
|
||||
'is_active', 'comment', 'actions'
|
||||
],
|
||||
columns: ['name', 'user_username_group', 'hostname_group', 'ip_group', 'name_group', 'protocol_group', 'systemuser_username_group', 'reviewers', 'priority', 'is_active', 'comment', 'actions'],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'user_username_group', 'hostname_group', 'ip_group', 'reviewers',
|
||||
'priority', 'is_active', 'comment', 'actions'
|
||||
]
|
||||
default: ['name', 'user_username_group', 'hostname_group', 'ip_group', 'reviewers', 'priority', 'is_active', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
user_username_group: {
|
||||
|
||||
65
src/views/assets/AdminUser/AdminUserCreateUpdate.vue
Normal file
65
src/views/assets/AdminUser/AdminUserCreateUpdate.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
<template>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import { UploadKey } from '@/components'
|
||||
export default {
|
||||
name: 'AdminUserCreateUpdate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initial: {
|
||||
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'username', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Name')
|
||||
}
|
||||
},
|
||||
username: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Username')
|
||||
}
|
||||
},
|
||||
update_password: {
|
||||
label: this.$t('users.UpdatePassword'),
|
||||
type: 'checkbox',
|
||||
hidden: (formValue) => {
|
||||
if (formValue.update_password) {
|
||||
return true
|
||||
}
|
||||
return !this.$route.params.id
|
||||
}
|
||||
},
|
||||
password: {
|
||||
helpText: this.$t('common.passwordOrPassphrase'),
|
||||
hidden: (formValue) => {
|
||||
if (!this.$route.params.id) {
|
||||
return false
|
||||
}
|
||||
return !formValue.update_password
|
||||
}
|
||||
},
|
||||
private_key: {
|
||||
component: UploadKey
|
||||
}
|
||||
},
|
||||
url: '/api/v1/assets/admin-users/'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
60
src/views/assets/AdminUser/AdminUserDetail/AccountList.vue
Normal file
60
src/views/assets/AdminUser/AdminUserDetail/AccountList.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<AssetUserTable :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AssetUserTable } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AssetUserTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assetUserUrl: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=admin_user&latest=1`,
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/assets/admin-users/${this.object.id}/connective/`
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
126
src/views/assets/AdminUser/AdminUserDetail/AssetList.vue
Normal file
126
src/views/assets/AdminUser/AdminUserDetail/AssetList.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
<RelationCard ref="RelationCard" type="info" style="margin-top: 15px" v-bind="nodeRelationConfig" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QuickActions from '@/components/QuickActions/index'
|
||||
import ListTable from '@/components/ListTable'
|
||||
import RelationCard from '@/components/RelationCard'
|
||||
import { ChoicesFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetList',
|
||||
components: {
|
||||
QuickActions,
|
||||
ListTable,
|
||||
RelationCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/assets/?admin_user__id=${this.object.id}`,
|
||||
columns: [
|
||||
'hostname', 'ip', 'admin_user_display', 'connectivity'
|
||||
],
|
||||
columnsMeta: {
|
||||
admin_user_display: {
|
||||
label: this.$t('assets.AdminUser')
|
||||
},
|
||||
connectivity: {
|
||||
label: this.$t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
0: 'fa-times text-danger',
|
||||
1: 'fa-check text-primary',
|
||||
2: 'fa-circle text-warning'
|
||||
},
|
||||
typeChange: function(val) {
|
||||
return val.status
|
||||
},
|
||||
hasTips: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasBulkDelete: false,
|
||||
hasImport: false,
|
||||
hasExport: true,
|
||||
hasCreate: false,
|
||||
hasSearch: true,
|
||||
hasMoreActions: false
|
||||
},
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('assets.Test')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.get(
|
||||
`/api/v1/assets/admin-users/${this.object.id}/connective/`
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
}
|
||||
)
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
],
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
title: this.$t('assets.ReplaceNodeAssetsAdminUser'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value, value: item.id }
|
||||
}
|
||||
},
|
||||
performAdd: (items) => {
|
||||
const data = []
|
||||
const relationUrl = `/api/v1/assets/admin-users/${this.object.id}/nodes/`
|
||||
items.map(v => {
|
||||
data.push(v.value)
|
||||
})
|
||||
return this.$axios.patch(relationUrl, { nodes: data }).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
},
|
||||
onAddSuccess: () => {
|
||||
this.$refs.RelationCard.$refs.select2.clearSelected()
|
||||
this.$refs.ListTable.reloadTable()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
60
src/views/assets/AdminUser/AdminUserDetail/Detail.vue
Normal file
60
src/views/assets/AdminUser/AdminUserDetail/Detail.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="14">
|
||||
<DetailCard :items="detailCardItems" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
DetailCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
detailCardItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('assets.Name'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Username'),
|
||||
value: this.object.username
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.sshKeyFingerprint'),
|
||||
value: this.object.ssh_key_fingerprint
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.date_joined'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.CreatedBy'),
|
||||
value: this.object.created_by
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
50
src/views/assets/AdminUser/AdminUserDetail/index.vue
Normal file
50
src/views/assets/AdminUser/AdminUserDetail/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="TaskDetail" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="TaskDetail" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import Detail from './Detail.vue'
|
||||
import AccountList from './AccountList.vue'
|
||||
import AssetList from './AssetList.vue'
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
Detail,
|
||||
AssetList,
|
||||
AccountList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
TaskDetail: {},
|
||||
config: {
|
||||
activeMenu: 'Detail',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('assets.AdminUserDetail'),
|
||||
name: 'Detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AssetList'),
|
||||
name: 'AssetList'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AccountList'),
|
||||
name: 'AccountList'
|
||||
}
|
||||
],
|
||||
hasRightSide: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
45
src/views/assets/AdminUser/AdminUserList.vue
Normal file
45
src/views/assets/AdminUser/AdminUserList.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
columns: [
|
||||
'name', 'username', 'assets_amount',
|
||||
'created_by', 'date_created', 'date_updated', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'assets_amount', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
assets_amount: {
|
||||
width: '80px'
|
||||
}
|
||||
}
|
||||
},
|
||||
updateRoute: 'AdminUserUpdate',
|
||||
headerActions: {
|
||||
createRoute: 'AdminUserCreate'
|
||||
},
|
||||
helpMessage: this.$t('assets.AdminUserListHelpMessage')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -63,7 +63,7 @@ export default {
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: '/api/v1/assets/system-users/?type=admin',
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
transformOption: (item) => {
|
||||
return { label: `${item.name}(${item.username})`, value: item.id }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16">
|
||||
<AccountListTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
<AssetUserTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<QuickActions type="primary" :actions="quickActions" />
|
||||
@@ -13,12 +13,12 @@
|
||||
|
||||
<script>
|
||||
import QuickActions from '@/components/QuickActions'
|
||||
import { AccountListTable } from '@/components'
|
||||
import { AssetUserTable } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'Detail',
|
||||
components: {
|
||||
AccountListTable,
|
||||
AssetUserTable,
|
||||
QuickActions
|
||||
},
|
||||
props: {
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assetUserUrl: `/api/v1/assets/accounts/?asset=${this.object.id}`,
|
||||
assetUserUrl: `/api/v1/assets/asset-users/?asset_id=${this.object.id}&latest=1`,
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/tasks/?asset=${this.object.id}`,
|
||||
`/api/v1/assets/asset-users/tasks/?asset_id=${this.object.id}&latest=1`,
|
||||
{ action: 'test' }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
@@ -91,6 +91,24 @@ export default {
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
// {
|
||||
// title: this.$t('assets.PushSystemUserNow'),
|
||||
// attrs: {
|
||||
// type: 'primary',
|
||||
// label: this.$t('assets.Push')
|
||||
// },
|
||||
// callbacks: {
|
||||
// click: function() {
|
||||
// this.$axios.post(
|
||||
// `api/v1/assets/system-users/${this.object.id}/tasks/`,
|
||||
// { action: 'push' }
|
||||
// ).then(res => {
|
||||
// window.open(`/ops/celery/task/${res.task}/log/`, '', 'width=900,height=600')
|
||||
// }
|
||||
// )
|
||||
// }.bind(this)
|
||||
// }
|
||||
// }
|
||||
],
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import Detail from './Detail.vue'
|
||||
import Account from './Account.vue'
|
||||
import AssetUserList from './AssetUserList.vue'
|
||||
export default {
|
||||
name: 'AssetListDetail',
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TabPage,
|
||||
Detail,
|
||||
Account
|
||||
AssetUserList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -29,8 +29,8 @@ export default {
|
||||
name: 'Detail'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AccountList'),
|
||||
name: 'Account'
|
||||
title: this.$t('assets.AssetUserList'),
|
||||
name: 'AssetUserList'
|
||||
}
|
||||
],
|
||||
hasRightSide: true,
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage/index'
|
||||
import { DetailFormatter, ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { DetailFormatter, ActionsFormatter, ChoicesFormatter } from '@/components/TableFormatters'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import TreeTable from '@/components/TreeTable'
|
||||
@@ -75,7 +75,6 @@ import { GenericUpdateFormDialog } from '@/layout/components'
|
||||
import rules from '@/components/DataForm/rules'
|
||||
import Protocols from '@/views/assets/Asset/components/Protocols/index'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { connectivityMeta } from '@/components/AccountListTable/const'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -102,10 +101,12 @@ export default {
|
||||
hasTree: true,
|
||||
columns: [
|
||||
'hostname', 'ip', 'public_ip', 'admin_user_display',
|
||||
'protocols', 'platform', 'hardware_info', 'model',
|
||||
'protocols',
|
||||
'platform', 'hardware_info', 'model',
|
||||
'cpu_model', 'cpu_cores', 'cpu_count', 'cpu_vcpus',
|
||||
'disk_info', 'disk_total', 'memory', 'os', 'os_arch',
|
||||
'os_version', 'number', 'vendor', 'sn',
|
||||
'disk_info', 'disk_total', 'memory',
|
||||
'os', 'os_arch', 'os_version',
|
||||
'number', 'vendor', 'sn',
|
||||
'connectivity',
|
||||
'created_by', 'date_created', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
@@ -142,7 +143,26 @@ export default {
|
||||
comment: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
connectivity: {
|
||||
label: this.$t('assets.Reachable'),
|
||||
formatter: ChoicesFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
0: 'fa-times text-danger',
|
||||
1: 'fa-check text-primary',
|
||||
2: 'fa-circle text-warning'
|
||||
},
|
||||
typeChange: function(val) {
|
||||
if (!val) {
|
||||
return 2
|
||||
}
|
||||
return val.status
|
||||
},
|
||||
hasTips: true
|
||||
},
|
||||
width: '90px',
|
||||
align: 'center'
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
@@ -166,7 +186,6 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasImport: !this.$store.getters.currentOrgIsRoot,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AssetCreate',
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
:get-next-route="getNextRoute"
|
||||
:clean-form-value="cleanFormValue"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -102,12 +101,6 @@ export default {
|
||||
}
|
||||
}
|
||||
return route
|
||||
},
|
||||
cleanFormValue(values) {
|
||||
if (!values.update_password) {
|
||||
delete values['password']
|
||||
}
|
||||
return values
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserDatabase',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { Select2, UploadKey } from '@/components'
|
||||
|
||||
function getFields() {
|
||||
const login_mode = {
|
||||
helpText: i18n.t('assets.LoginModeHelpMessage'),
|
||||
on: {
|
||||
input: ([value], updateForm) => {
|
||||
if (value === 'manual') {
|
||||
@@ -24,14 +26,12 @@ function getFields() {
|
||||
},
|
||||
rules: [Object.assign({}, Required)],
|
||||
hidden: (form) => {
|
||||
if (['vnc'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.username_same_with_user) {
|
||||
if (['mysql', 'postgresql', 'mariadb', 'oracle'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = true
|
||||
} else if (['vnc'].includes(form.protocol)) {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else {
|
||||
this.fieldsMeta.username.rules[0].required = true
|
||||
this.fieldsMeta.username.rules[0].required = !(form.login_mode === 'manual' || form.username_same_with_user)
|
||||
}
|
||||
if (form.username_same_with_user) {
|
||||
this.fieldsMeta.username.el.disabled = true
|
||||
@@ -54,7 +54,6 @@ function getFields() {
|
||||
|
||||
const username_same_with_user = {
|
||||
type: 'switch',
|
||||
label: this.$t('assets.DynamicUsername'),
|
||||
helpText: this.$t('assets.UsernameHelpMessage'),
|
||||
el: {
|
||||
disabled: false
|
||||
@@ -78,10 +77,6 @@ function getFields() {
|
||||
if (form.protocol === 'k8s') {
|
||||
return true
|
||||
}
|
||||
if (form.type === 'admin') {
|
||||
form.auto_generate_key = false
|
||||
return true
|
||||
}
|
||||
|
||||
if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.auto_generate_key.el.disabled = true
|
||||
@@ -119,7 +114,7 @@ function getFields() {
|
||||
disabled: false
|
||||
},
|
||||
hidden: form => {
|
||||
if (form.login_mode === 'manual' || form.type === 'admin') {
|
||||
if (form.login_mode === 'manual') {
|
||||
this.fieldsMeta.auto_push.el.disabled = true
|
||||
} else {
|
||||
this.fieldsMeta.auto_push.el.disabled = false
|
||||
@@ -167,9 +162,6 @@ function getFields() {
|
||||
helpText: this.$t('assets.GroupsHelpMessage')
|
||||
}
|
||||
|
||||
const type = {
|
||||
}
|
||||
|
||||
return {
|
||||
login_mode: login_mode,
|
||||
username: username,
|
||||
@@ -181,8 +173,7 @@ function getFields() {
|
||||
auto_push: auto_push,
|
||||
update_password: update_password,
|
||||
password: password,
|
||||
system_groups: system_groups,
|
||||
type: type
|
||||
system_groups: system_groups
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -13,7 +12,7 @@ import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserSSH',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
return {
|
||||
@@ -21,9 +20,9 @@ export default {
|
||||
protocol: this.$route.query.protocol
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol']],
|
||||
[this.$t('common.Basic'), ['name', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['token']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
token: {
|
||||
@@ -1,19 +1,13 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserRDP',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -24,14 +18,15 @@ export default {
|
||||
username_same_with_user: false,
|
||||
auto_generate_key: false,
|
||||
auto_push: false,
|
||||
sftp_root: 'tmp',
|
||||
sudo: '/bin/whoami',
|
||||
shell: '/bin/bash'
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'update_password', 'password', 'ad_domain']],
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('assets.AutoPush'), ['auto_push', 'system_groups']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Auth'), ['update_password', 'password', 'ad_domain']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
@@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
import getFields from '../fields'
|
||||
import getFields from './fields'
|
||||
|
||||
export default {
|
||||
name: 'CommonUserSSH',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -30,11 +24,11 @@ export default {
|
||||
shell: '/bin/bash'
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'auto_generate_key', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('assets.AutoPush'), ['auto_push', 'sudo', 'shell', 'home', 'system_groups']],
|
||||
[this.$t('common.Auth'), ['auto_generate_key', 'update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('common.Other'), ['priority', 'sftp_root', 'comment']]
|
||||
[this.$t('common.Other'), ['sftp_root', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
@@ -1,19 +1,15 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import getFields from '@/views/assets/SystemUser/SystemUserCreate/fields'
|
||||
|
||||
// const asciiProtocols = ['ssh', 'telnet', 'mysql']
|
||||
|
||||
export default {
|
||||
name: 'CommonUserVNC',
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
@@ -24,9 +20,9 @@ export default {
|
||||
username_same_with_user: false
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'update_password', 'password']],
|
||||
[this.$t('common.Other'), ['priority', 'comment']]
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'username_same_with_user', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['update_password', 'password']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
login_mode: fields.login_mode,
|
||||
@@ -1,26 +1,27 @@
|
||||
<template>
|
||||
<component :is="systemUserProtocolComponent" :title="title" />
|
||||
<component :is="activePage" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SSH from './ssh'
|
||||
import RDP from './rdp'
|
||||
import VncOrTelnet from './vncAndTelnet'
|
||||
import Database from './database'
|
||||
import K8S from './k8s'
|
||||
import SSH from './SystemUserCreate/ssh'
|
||||
import RDP from './SystemUserCreate/rdp'
|
||||
import VncAndTelnet from './SystemUserCreate/vncAndTelnet'
|
||||
import DATABASE from './SystemUserCreate/database'
|
||||
import K8S from './SystemUserCreate/k8s'
|
||||
|
||||
export default {
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { SSH, RDP, VncOrTelnet, Database },
|
||||
components: { SSH, RDP, VncAndTelnet, DATABASE },
|
||||
data() {
|
||||
return {
|
||||
title: this.$t('route.SystemUserCreate') + ' - ' + this.$t('assets.CommonUser')
|
||||
|
||||
}
|
||||
},
|
||||
method: {
|
||||
|
||||
},
|
||||
computed: {
|
||||
systemUserProtocolComponent() {
|
||||
activePage() {
|
||||
const query = this.$route.query
|
||||
const protocol = query.protocol
|
||||
switch (protocol) {
|
||||
@@ -30,12 +31,12 @@ export default {
|
||||
return RDP
|
||||
case 'vnc':
|
||||
case 'telnet':
|
||||
return VncOrTelnet
|
||||
return VncAndTelnet
|
||||
case 'mysql':
|
||||
case 'oracle':
|
||||
case 'postgresql':
|
||||
case 'mariadb':
|
||||
return Database
|
||||
return DATABASE
|
||||
case 'k8s':
|
||||
return K8S
|
||||
default:
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<component :is="systemUserProtocolComponent" :title="title" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SSH from './ssh'
|
||||
|
||||
export default {
|
||||
name: 'AdminUserCreate',
|
||||
data() {
|
||||
return {
|
||||
title: this.$t('route.SystemUserCreate') + ' - ' + this.$t('assets.AdminUser')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
systemUserProtocolComponent() {
|
||||
return SSH
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,97 +0,0 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage
|
||||
:fields="fields"
|
||||
:initial="initial"
|
||||
:fields-meta="fieldsMeta"
|
||||
:url="url"
|
||||
:clean-form-value="cleanFormValue"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import getFields from '../fields'
|
||||
import { UploadKey } from '@/components'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'AdminUserSSH',
|
||||
components: { GenericCreateUpdatePage },
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
return {
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
initial: {
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'type']],
|
||||
[this.$t('common.Auth'), ['update_password', 'password', 'private_key']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('common.Other'), ['priority', 'sftp_root', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Name')
|
||||
}
|
||||
},
|
||||
type: {
|
||||
hidden() {
|
||||
return true
|
||||
}
|
||||
},
|
||||
username: {
|
||||
el: {
|
||||
placeholder: this.$t('common.Username')
|
||||
},
|
||||
rules: [Required]
|
||||
},
|
||||
protocol: {
|
||||
el: {
|
||||
style: 'width:100%',
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
update_password: {
|
||||
label: this.$t('users.UpdatePassword'),
|
||||
type: 'checkbox',
|
||||
hidden: (formValue) => {
|
||||
if (formValue.update_password) {
|
||||
return true
|
||||
}
|
||||
return !this.$route.params.id
|
||||
}
|
||||
},
|
||||
password: {
|
||||
helpText: this.$t('common.passwordOrPassphrase'),
|
||||
hidden: (formValue) => {
|
||||
if (!this.$route.params.id) {
|
||||
return false
|
||||
}
|
||||
return !formValue.update_password
|
||||
}
|
||||
},
|
||||
private_key: {
|
||||
component: UploadKey
|
||||
},
|
||||
sftp_root: {
|
||||
rules: [Required],
|
||||
helpText: this.$t('assets.SFTPHelpMessage')
|
||||
},
|
||||
cmd_filters: fields.cmd_filters
|
||||
},
|
||||
cleanFormValue: (values) => {
|
||||
values['type'] = 'admin'
|
||||
return values
|
||||
}
|
||||
}
|
||||
},
|
||||
method: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<component :is="systemUserTypeComponent" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommonUser from './CommonUser/index'
|
||||
import AdminUser from './AdminUser/index'
|
||||
|
||||
export default {
|
||||
name: 'SystemUserCreateUpdate',
|
||||
components: { CommonUser, AdminUser },
|
||||
computed: {
|
||||
systemUserTypeComponent() {
|
||||
const query = this.$route.query
|
||||
const type = query.type || 'common'
|
||||
if (type === 'admin') {
|
||||
return AdminUser
|
||||
} else {
|
||||
return CommonUser
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,20 +1,22 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="20">
|
||||
<AccountListTable ref="ListTable" :url="accountUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
<el-col :span="4" />
|
||||
</el-row>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<AssetUserTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
|
||||
</el-col>
|
||||
<el-col :span="8" />
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { AccountListTable } from '@/components'
|
||||
import { AssetUserTable } from '@/components'
|
||||
|
||||
export default {
|
||||
name: 'AccountList',
|
||||
components: {
|
||||
AccountListTable
|
||||
AssetUserTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -24,7 +26,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
accountUrl: `/api/v1/assets/accounts/?systemuser=${this.object.id}`
|
||||
assetUserUrl: `/api/v1/assets/asset-users/?prefer_id=${this.object.id}&prefer=system_user&latest=1`
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -18,8 +18,7 @@ import QuickActions from '@/components/QuickActions/index'
|
||||
import RelationCard from '@/components/RelationCard'
|
||||
import AssetRelationCard from '@/components/AssetRelationCard'
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { connectivityMeta } from '@/components/AccountListTable/const'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
name: 'AssetList',
|
||||
@@ -40,23 +39,21 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/system-users-assets-relations/?systemuser=${this.object.id}`,
|
||||
columns: ['asset_display', 'connectivity', 'actions'],
|
||||
columnsMeta: {
|
||||
asset_display: {
|
||||
label: this.$t('assets.Hostname'),
|
||||
showOverflowTooltip: true,
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
getRoute({ row }) {
|
||||
return {
|
||||
name: 'AssetDetail',
|
||||
params: { id: row.asset }
|
||||
}
|
||||
}
|
||||
}
|
||||
columns: [
|
||||
{
|
||||
prop: 'asset_display',
|
||||
label: this.$t('assets.Hostname')
|
||||
},
|
||||
connectivity: connectivityMeta,
|
||||
actions: {
|
||||
{
|
||||
prop: 'systemuser_display',
|
||||
label: this.$t('assets.SystemUsers')
|
||||
},
|
||||
{
|
||||
prop: 'id',
|
||||
label: this.$t('common.Action'),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false, // can set function(row, value)
|
||||
hasDelete: false, // can set function(row, value)
|
||||
@@ -91,7 +88,7 @@ export default {
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
headerActions: {
|
||||
hasBulkDelete: false,
|
||||
@@ -116,6 +113,38 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-info',
|
||||
title: this.$t('perms.addNodeToThisPermission'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value, value: item.id }
|
||||
}
|
||||
},
|
||||
hasObjectsId: [],
|
||||
hasObjects: [],
|
||||
performAdd: (items) => {
|
||||
const relationUrl = `/api/v1/assets/system-users-nodes-relations/`
|
||||
const objectId = this.object.id
|
||||
const data = items.map(v => {
|
||||
return {
|
||||
systemuser: objectId,
|
||||
node: v.value
|
||||
}
|
||||
})
|
||||
if (data.length === 0) {
|
||||
return this.$message.error(this.$t('assets.UnselectedNodes'))
|
||||
}
|
||||
return this.$axios.post(relationUrl, data)
|
||||
},
|
||||
performDelete: (item) => {
|
||||
const itemId = item.value
|
||||
const objectId = this.object.id
|
||||
const relationUrl = `/api/v1/assets/system-users-nodes-relations/?systemuser=${objectId}&node=${itemId}`
|
||||
return this.$axios.delete(relationUrl)
|
||||
}
|
||||
},
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('assets.TestAssetsConnective'),
|
||||
@@ -153,52 +182,23 @@ export default {
|
||||
}
|
||||
}
|
||||
],
|
||||
nodeRelationConfig: {
|
||||
icon: 'fa-link',
|
||||
title: this.$t('assets.AssociateNodes'),
|
||||
objectsAjax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value, value: item.id }
|
||||
}
|
||||
},
|
||||
hasObjectsId: [],
|
||||
hasObjects: [],
|
||||
performAdd: (items) => {
|
||||
const relationUrl = `/api/v1/assets/system-users-nodes-relations/`
|
||||
const objectId = this.object.id
|
||||
const data = items.map(v => {
|
||||
return {
|
||||
systemuser: objectId,
|
||||
node: v.value
|
||||
}
|
||||
})
|
||||
if (data.length === 0) {
|
||||
return this.$message.error(this.$t('assets.UnselectedNodes'))
|
||||
}
|
||||
return this.$axios.post(relationUrl, data)
|
||||
},
|
||||
performDelete: (item) => {
|
||||
const itemId = item.value
|
||||
const objectId = this.object.id
|
||||
const relationUrl = `/api/v1/assets/system-users-nodes-relations/?systemuser=${objectId}&node=${itemId}`
|
||||
return this.$axios.delete(relationUrl)
|
||||
}
|
||||
},
|
||||
assetRelationConfig: {
|
||||
icon: 'fa-link',
|
||||
title: this.$t('assets.AssociateAssets'),
|
||||
icon: 'fa-edit',
|
||||
title: this.$t('xpack.ChangeAuthPlan.AddAsset'),
|
||||
disabled: this.$store.getters.currentOrgIsRoot,
|
||||
performAdd: (items, that) => {
|
||||
const relationUrl = `/api/v1/assets/system-users-assets-relations/`
|
||||
const data = items.map((i) => {
|
||||
return {
|
||||
'asset': i,
|
||||
'systemuser': this.object.id
|
||||
}
|
||||
})
|
||||
const data = [
|
||||
|
||||
]
|
||||
items.map(v =>
|
||||
data.push({
|
||||
asset: v,
|
||||
systemuser: this.object.id
|
||||
})
|
||||
)
|
||||
if (data.length === 0) {
|
||||
return this.$message.error(this.$tc('assets.UnselectedAssets'))
|
||||
return this.$message.error(this.$t('assets.UnselectedAssets'))
|
||||
}
|
||||
return this.$axios.post(relationUrl, data)
|
||||
},
|
||||
|
||||
@@ -44,7 +44,7 @@ export default {
|
||||
attrs: {
|
||||
label: this.$t('assets.AutoPush'),
|
||||
model: this.object.auto_push,
|
||||
disabled: ['rdp', 'ssh'].indexOf(this.object.protocol) === -1 || this.object.type === 'admin'
|
||||
disabled: ['rdp', 'ssh'].indexOf(this.object.protocol) === -1
|
||||
},
|
||||
callbacks: {
|
||||
change: function(val) {
|
||||
@@ -111,25 +111,17 @@ export default {
|
||||
key: this.$t('assets.Name'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Type'),
|
||||
value: this.object.type_display
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Username'),
|
||||
value: this.object.username
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.sshKeyFingerprint'),
|
||||
value: this.object['ssh_key_fingerprint']
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Protocol'),
|
||||
value: this.object.protocol
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.LoginModel'),
|
||||
value: this.object['login_mode_display']
|
||||
value: this.object.login_mode_display
|
||||
},
|
||||
{
|
||||
key: 'Sudo',
|
||||
|
||||
@@ -47,10 +47,7 @@ export default {
|
||||
this.$router.push({
|
||||
name: routeName,
|
||||
params: { id: id },
|
||||
query: {
|
||||
type: vm.systemUser.type,
|
||||
protocol: vm.systemUser.protocol
|
||||
}
|
||||
query: { protocol: vm.systemUser.protocol }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert v-if="helpMessage" type="success">
|
||||
<span v-html="helpMessage" />
|
||||
</el-alert>
|
||||
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
|
||||
</div>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { GenericListPage } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListTable
|
||||
GenericListPage
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/system-users/?type=common',
|
||||
url: '/api/v1/assets/system-users/',
|
||||
columns: [
|
||||
'name', 'username', 'username_same_with_user', 'protocol', 'login_mode',
|
||||
'assets_amount', 'priority',
|
||||
@@ -55,8 +50,10 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
headerActions: {
|
||||
hasMoreActions: false,
|
||||
hasCreate: false,
|
||||
createRoute: 'SystemUserCreate',
|
||||
moreCreates: {
|
||||
@@ -71,6 +68,12 @@ export default {
|
||||
group: this.$t('assets.HostProtocol'),
|
||||
has: true
|
||||
},
|
||||
{
|
||||
title: 'Telnet',
|
||||
name: 'Telnet',
|
||||
type: 'primary',
|
||||
has: true
|
||||
},
|
||||
{
|
||||
title: 'RDP',
|
||||
name: 'RDP',
|
||||
@@ -83,12 +86,6 @@ export default {
|
||||
type: 'primary',
|
||||
has: true
|
||||
},
|
||||
{
|
||||
title: 'Telnet',
|
||||
name: 'Telnet',
|
||||
type: 'primary',
|
||||
has: true
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
title: 'MySQL',
|
||||
@@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert v-if="helpMessage" type="success">
|
||||
<span v-html="helpMessage" />
|
||||
</el-alert>
|
||||
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" :help-message="helpMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/admin-users/',
|
||||
columns: [
|
||||
'name', 'username', 'assets_amount',
|
||||
'created_by', 'date_created', 'date_updated', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'username', 'assets_amount', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
username: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
assets_amount: {
|
||||
width: '80px'
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
onUpdate: ({ row }) => {
|
||||
vm.$router.push({
|
||||
name: 'SystemUserUpdate',
|
||||
params: { id: row.id },
|
||||
query: { protocol: row.protocol, type: 'admin' }
|
||||
})
|
||||
},
|
||||
onClone: ({ row }) => {
|
||||
vm.$router.push({
|
||||
name: 'SystemUserCreate',
|
||||
query: { protocol: row.protocol, type: 'admin', clone_from: row.id }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'SystemUserCreate',
|
||||
query: {
|
||||
type: 'admin'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
helpMessage: this.$t('assets.AdminUserListHelpMessage')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" />
|
||||
</keep-alive>
|
||||
</TabPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TabPage } from '@/layout/components'
|
||||
import CommonUserList from './CommonUserList'
|
||||
import AdminUserList from './AdminUserList'
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
TabPage,
|
||||
CommonUserList,
|
||||
AdminUserList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
activeMenu: 'CommonUserList',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('assets.CommonUser'),
|
||||
name: 'CommonUserList'
|
||||
},
|
||||
{
|
||||
title: this.$t('assets.AdminUser'),
|
||||
name: 'AdminUserList'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -34,8 +34,7 @@ export default {
|
||||
body: {
|
||||
route: `/users/users`,
|
||||
count: this.counter.total_count_users,
|
||||
comment: 'All users',
|
||||
disabled: !this.$store.state.users.hasAdmin
|
||||
comment: 'All users'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -47,8 +46,7 @@ export default {
|
||||
body: {
|
||||
route: `/assets/assets`,
|
||||
count: this.counter.total_count_assets,
|
||||
comment: 'All assets',
|
||||
disabled: !this.$store.state.users.hasAdmin
|
||||
comment: 'All assets'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div id="terminal" ref="terminal" class="xterm" />
|
||||
<div id="terminal" ref="terminal" class="xterm" style="height: 100%;width: 100%;background-color:#676a6c" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -21,6 +21,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.initTermAndWs()
|
||||
@@ -51,9 +52,6 @@ export default {
|
||||
this.xterm.loadAddon(fitAddon)
|
||||
this.xterm.open(terminalContainer)
|
||||
fitAddon.fit()
|
||||
window.onresize = function() {
|
||||
fitAddon.fit()
|
||||
}
|
||||
this.xterm.scrollToBottom()
|
||||
this.enableWS()
|
||||
},
|
||||
@@ -88,10 +86,4 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#terminal {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #1f1b1b;
|
||||
padding: 10px
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,9 +91,6 @@ export default {
|
||||
// const form = await this.$refs.createUpdatePage.$refs.createUpdateForm.getFormValue()
|
||||
url: this.$route.query.category === 'remote_app' ? `/api/v1/assets/system-users/?protocol=rdp` : `/api/v1/assets/system-users/?protocol=${this.$route.query.type}`,
|
||||
transformOption: (item) => {
|
||||
if (this.$route.query.type === 'k8s') {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,13 @@ export default {
|
||||
|
||||
initial: { type: commandType, doc_type: 'command' },
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'type', 'meta', 'is_default', 'comment']]
|
||||
[this.$t('common.Basic'), ['name', 'type', 'meta', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
type: {
|
||||
type: 'select',
|
||||
disabled: true
|
||||
},
|
||||
is_default: {
|
||||
helpText: this.$t('sessions.SetToDefaultStorage')
|
||||
},
|
||||
meta: {
|
||||
fields: ['HOSTS', 'INDEX', 'IGNORE_VERIFY_CERTS'],
|
||||
fieldsMeta: {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'type']],
|
||||
[storageTypeMeta.title, ['meta']],
|
||||
[this.$t('common.Other'), ['is_default', 'comment']]
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
type: {
|
||||
@@ -47,9 +47,6 @@ export default {
|
||||
},
|
||||
meta: {
|
||||
fields: storageTypeMeta.meta
|
||||
},
|
||||
is_default: {
|
||||
helpText: this.$t('sessions.SetToDefaultStorage')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<ListTable ref="ListTable" :table-config="commandTableConfig" :header-actions="commandActions" />
|
||||
<ListTable :table-config="commandTableConfig" :header-actions="commandActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { SetToDefaultCommandStorage, TestCommandStorage } from '@/api/sessions'
|
||||
import { BooleanFormatter } from '@/components/TableFormatters'
|
||||
import { TestCommandStorage } from '@/api/sessions'
|
||||
export default {
|
||||
name: 'CommandStorage',
|
||||
components: {
|
||||
@@ -18,12 +17,11 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
commandActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasRefresh: true,
|
||||
hasRefresh: false,
|
||||
hasMoreActions: false,
|
||||
moreCreates: {
|
||||
callback: (item) => {
|
||||
@@ -40,7 +38,7 @@ export default {
|
||||
commandTableConfig: {
|
||||
title: 'command',
|
||||
url: '/api/v1/terminal/command-storages/',
|
||||
columns: ['name', 'type', 'comment', 'is_default', 'actions'],
|
||||
columns: ['name', 'type', 'comment', 'actions'],
|
||||
columnsMeta: {
|
||||
comment: {
|
||||
sortable: 'custom'
|
||||
@@ -55,17 +53,6 @@ export default {
|
||||
return row.type
|
||||
}
|
||||
},
|
||||
is_default: {
|
||||
formatter: BooleanFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
true: 'fa-check text-primary',
|
||||
false: ''
|
||||
}
|
||||
},
|
||||
align: 'center',
|
||||
width: '100px'
|
||||
},
|
||||
actions: {
|
||||
prop: 'id',
|
||||
formatterArgs: {
|
||||
@@ -78,7 +65,6 @@ export default {
|
||||
canDelete: function({ row }) {
|
||||
return (row.name !== 'default' && row.name !== 'null')
|
||||
},
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'test',
|
||||
@@ -86,26 +72,13 @@ export default {
|
||||
type: 'primary',
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
TestCommandStorage(row.id).then(data => {
|
||||
if (!data['is_valid']) {
|
||||
if (!data.is_valid) {
|
||||
this.$message.error(data.msg)
|
||||
} else {
|
||||
this.$message.success(data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'set_to_default',
|
||||
title: this.$t('sessions.SetToDefault'),
|
||||
type: 'primary',
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
SetToDefaultCommandStorage(row.id).then(data => {
|
||||
vm.$refs.ListTable.reloadTable()
|
||||
this.$message.success(this.$t('sessions.SetSuccess'))
|
||||
}).catch(() => {
|
||||
this.$message.error(this.$t('sessions.SetFailed'))
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<template>
|
||||
<ListTable ref="ListTable" :table-config="replayTableConfig" :header-actions="replayActions" />
|
||||
<ListTable :table-config="replayTableConfig" :header-actions="replayActions" />
|
||||
</template>
|
||||
<script>
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { TestReplayStorage, SetToDefaultReplayStorage } from '@/api/sessions'
|
||||
import { BooleanFormatter } from '@/components/TableFormatters'
|
||||
import { TestReplayStorage } from '@/api/sessions'
|
||||
export default {
|
||||
name: 'ReplayStorage',
|
||||
components: {
|
||||
ListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
replayActions: {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasRefresh: true,
|
||||
hasRefresh: false,
|
||||
hasMoreActions: false,
|
||||
moreCreates: {
|
||||
callback: (item) => {
|
||||
@@ -48,7 +46,7 @@ export default {
|
||||
},
|
||||
replayTableConfig: {
|
||||
url: '/api/v1/terminal/replay-storages/',
|
||||
columns: ['name', 'type', 'comment', 'is_default', 'actions'],
|
||||
columns: ['name', 'type', 'comment', 'actions'],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: function(row) {
|
||||
@@ -60,17 +58,6 @@ export default {
|
||||
return row.type
|
||||
}
|
||||
},
|
||||
is_default: {
|
||||
formatter: BooleanFormatter,
|
||||
formatterArgs: {
|
||||
iconChoices: {
|
||||
true: 'fa-check text-primary',
|
||||
false: ''
|
||||
}
|
||||
},
|
||||
align: 'center',
|
||||
width: '100px'
|
||||
},
|
||||
comment: {
|
||||
sortable: 'custom'
|
||||
},
|
||||
@@ -86,7 +73,6 @@ export default {
|
||||
canDelete: function({ row }) {
|
||||
return (row.name !== 'default' && row.name !== 'null')
|
||||
},
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'test',
|
||||
@@ -94,26 +80,13 @@ export default {
|
||||
type: 'primary',
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
TestReplayStorage(row.id).then(data => {
|
||||
if (!data['is_valid']) {
|
||||
if (!data.is_valid) {
|
||||
this.$message.error(data.msg)
|
||||
} else {
|
||||
this.$message.success(data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'set_to_default',
|
||||
title: this.$t('sessions.SetToDefault'),
|
||||
type: 'primary',
|
||||
callback: function({ row, col, cellValue, reload }) {
|
||||
SetToDefaultReplayStorage(row.id).then(data => {
|
||||
vm.$refs.ListTable.reloadTable()
|
||||
this.$message.success(this.$t('sessions.SetSuccess'))
|
||||
}).catch(() => {
|
||||
this.$message.error(this.$t('sessions.SetFailed'))
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
<template>
|
||||
<Dialog
|
||||
ref="myDialog"
|
||||
:destroy-on-close="true"
|
||||
width="770px"
|
||||
height="700px"
|
||||
v-bind="$attrs"
|
||||
@confirm="submit"
|
||||
@cancel="cancel"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<Dialog ref="myDialog" :destroy-on-close="true" width="770px" height="700px" v-bind="$attrs" @confirm="submit" v-on="$listeners">
|
||||
<krryPaging ref="pageTransfer" v-bind="pagingTransfer" class="transfer" />
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -84,6 +75,8 @@ export default {
|
||||
submit() {
|
||||
const selectedUsersId = this.$refs.pageTransfer.getSelectedData()
|
||||
this.$emit('submit', selectedUsersId)
|
||||
|
||||
console.log('Selected user ids: ', selectedUsersId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
title="修改消息接受人"
|
||||
:selected-users="dialogSelectedUsers"
|
||||
@submit="onDialogSelectSubmit"
|
||||
@cancel="dialogVisible=false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script type="text/jsx">
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, azure, azure_international, vmware, nutanix, qingcloud_private, huaweicloud_private } from '../const'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, azure, azure_international, vmware, nutanix } from '../const'
|
||||
import { BooleanFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
@@ -113,14 +113,6 @@ export default {
|
||||
{
|
||||
name: nutanix,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[nutanix].title
|
||||
},
|
||||
{
|
||||
name: qingcloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qingcloud_private].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud_private].title
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ export const azure = 'azure'
|
||||
export const azure_international = 'azure_international'
|
||||
export const vmware = 'vmware'
|
||||
export const nutanix = 'nutanix'
|
||||
export const qingcloud_private = 'qingcloud_private'
|
||||
export const huaweicloud_private = 'huaweicloud_private'
|
||||
|
||||
export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
[aliyun]: {
|
||||
@@ -57,15 +55,5 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
name: nutanix,
|
||||
title: 'Nutanix',
|
||||
attrs: ['access_key_id', 'access_key_secret', 'api_endpoint']
|
||||
},
|
||||
[qingcloud_private]: {
|
||||
name: qingcloud_private,
|
||||
title: i18n.t('xpack.Cloud.QingyunPrivatecloud'),
|
||||
attrs: ['access_key_id', 'access_key_secret', 'api_endpoint']
|
||||
},
|
||||
[huaweicloud_private]: {
|
||||
name: huaweicloud_private,
|
||||
title: i18n.t('xpack.Cloud.HuaweiPrivatecloud'),
|
||||
attrs: ['sc_username', 'sc_password', 'domain_name', 'oc_username', 'oc_password', 'api_endpoint']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericTreeListPage ref="TreeTablePage" :tree-setting="treeSetting">
|
||||
<template #table>
|
||||
<AssetUserTable ref="table" v-bind="assetUserConfig" />
|
||||
</template>
|
||||
</GenericTreeListPage>
|
||||
<Dialog width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4">
|
||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="MFAInput" />
|
||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
|
||||
import { AssetUserTable } from '@/components'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { setUrlParam } from '@/utils/common'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VaultList',
|
||||
components: {
|
||||
GenericTreeListPage,
|
||||
AssetUserTable,
|
||||
Dialog
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
errorMsg: '',
|
||||
exportOption: '1',
|
||||
meta: {},
|
||||
MfaExpired: 0,
|
||||
showMFADialog: false,
|
||||
MFAInput: '',
|
||||
selectedRows: '',
|
||||
assetUserConfig: {
|
||||
hasLeftActions: true,
|
||||
hasCreate: true,
|
||||
hasClone: false,
|
||||
url: '/api/v1/assets/accounts/'
|
||||
// handleImport: function({ selectedRows }) {
|
||||
// this.selectedRows = selectedRows
|
||||
// this.dialogStatus = 'import'
|
||||
// if (!this.needMFAVerify) {
|
||||
// this.showMFADialog = false
|
||||
// this.showImportDialog = true
|
||||
// } else {
|
||||
// this.showMFADialog = true
|
||||
// }
|
||||
// }.bind(this),
|
||||
// handleExport: function({ selectedRows }) {
|
||||
// this.selectedRows = selectedRows
|
||||
// this.dialogStatus = 'export'
|
||||
// if (!this.needMFAVerify) {
|
||||
// this.showMFADialog = false
|
||||
// this.showExportDialog = true
|
||||
// } else {
|
||||
// this.showMFADialog = true
|
||||
// }
|
||||
// }.bind(this)
|
||||
},
|
||||
treeSetting: {
|
||||
showMenu: false,
|
||||
showRefresh: false,
|
||||
showAssets: true,
|
||||
url: '/api/v1/assets/accounts/',
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
||||
callback: {
|
||||
onSelected: function(event, treeNode) {
|
||||
let url = vm.assetUserConfig.url
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const nodeId = treeNode.meta.node.id
|
||||
url = setUrlParam(url, 'asset_id', '')
|
||||
url = setUrlParam(url, 'node_id', nodeId)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.asset.id
|
||||
url = setUrlParam(url, 'node_id', '')
|
||||
url = setUrlParam(url, 'asset_id', assetId)
|
||||
}
|
||||
setTimeout(() => {
|
||||
vm.assetUserConfig.url = url
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'MFA_TTl',
|
||||
'MFAVerifyAt',
|
||||
'publicSettings'
|
||||
]),
|
||||
needMFAVerify() {
|
||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||
return false
|
||||
}
|
||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||
const now = new Date()
|
||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||
},
|
||||
hasSelected() {
|
||||
return this.selectedRows.length > 0
|
||||
},
|
||||
...mapGetters([
|
||||
'MFAVerifyAt',
|
||||
'MFA_TTl'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
performUpdate(item) {
|
||||
this.$axios.put(
|
||||
`/api/v1/assets/accounts/`,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
this.catchError(error)
|
||||
})
|
||||
},
|
||||
performCreate(item) {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/accounts/`,
|
||||
item.file,
|
||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
||||
).then((data) => {
|
||||
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
|
||||
this.onSuccess(msg)
|
||||
}).catch(error => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg') + ' ' + error)
|
||||
this.catchError(error)
|
||||
})
|
||||
},
|
||||
catchError(error) {
|
||||
this.$refs.upload.clearFiles()
|
||||
if (error.response && error.response.status === 400) {
|
||||
const errorData = error.response.data
|
||||
const totalErrorMsg = []
|
||||
errorData.forEach((value, index) => {
|
||||
if (typeof value === 'string') {
|
||||
totalErrorMsg.push(`line ${index}. ${value}`)
|
||||
} else {
|
||||
const errorMsg = [`line ${index}. `]
|
||||
for (const [k, v] of Object.entries(value)) {
|
||||
if (v) {
|
||||
errorMsg.push(`${k}: ${v}`)
|
||||
}
|
||||
}
|
||||
if (errorMsg.length > 1) {
|
||||
totalErrorMsg.push(errorMsg.join(' '))
|
||||
}
|
||||
}
|
||||
})
|
||||
this.errorMsg = totalErrorMsg
|
||||
}
|
||||
},
|
||||
MFAConfirm() {
|
||||
if (this.MFAInput.length !== 6) {
|
||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||
}
|
||||
this.$axios.post(
|
||||
`/api/v1/authentication/otp/verify/`, {
|
||||
code: this.MFAInput
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
this.$store.dispatch('users/setMFAVerify')
|
||||
if (this.dialogStatus === 'import') {
|
||||
this.showMFADialog = false
|
||||
this.showImportDialog = true
|
||||
} else {
|
||||
this.showMFADialog = false
|
||||
this.showExportDialog = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user