Compare commits

..

22 Commits

Author SHA1 Message Date
“怀磊”
c093e7d621 fix: 修复资产授权创建时默认选中了点击的资产和节点问题
修改为指保留最后一次选中的资产或者节点
2022-02-17 18:20:47 +08:00
Jiangjie.Bai
b4183b421f Merge pull request #1316 from jumpserver/dev
v2.19.0-rc3
2022-02-16 16:42:58 +08:00
“怀磊”
9c6281cf02 fix: 修复资产账号导出条数不准确问题 2022-02-16 16:41:10 +08:00
feng626
e7bdf6276d fix: 工单流需要超级管理员配置 2022-02-16 14:50:45 +08:00
“怀磊”
fa860dbd85 fix: 修复命令记录页面筛选数据时tree自动刷新问题 2022-02-15 18:04:42 +08:00
Jiangjie.Bai
2bb4ca3ec0 fix: 修改LDAP SYNC相关参数设置 2022-02-15 17:58:20 +08:00
feng626
93a08d265b fix: 修复工单流只在当前组织管理员或系统管理员显示 2022-02-15 17:01:07 +08:00
Jiangjie.Bai
b7be7784c6 Merge pull request #1310 from jumpserver/dev
v2.19.0-rc2
2022-02-14 18:33:51 +08:00
“怀磊”
375c6bf44b fix: 修复搜索组件在页面刷新后搜索条件是布尔类型时页面不显示中文问题
1、优化获取url中搜索条件的算法
2022-02-14 18:23:03 +08:00
feng626
542e34208e feat: 工单添加redis类型 2022-02-14 17:46:07 +08:00
“怀磊”
f041b0de74 fix: 修复命令记录页面layout布局不能自适应问题 2022-02-14 16:16:59 +08:00
“怀磊”
7ff75280a5 fix: 修复search组件去重搜索不准确问题 2022-02-14 16:16:29 +08:00
ibuler
d76009de3a pref: 优化数据库协议翻译 2022-02-14 15:01:45 +08:00
“怀磊”
851f4508e2 fix: 资产账号替换密码框组件 2022-02-10 15:04:56 +08:00
feng626
a36e5196f8 perf: 工单流显示组织名称 2022-02-10 15:04:09 +08:00
Jiangjie.Bai
d22d16681e Merge pull request #1300 from jumpserver/dev
v2.19.0-rc1
2022-02-10 10:50:21 +08:00
“怀磊”
31c86fb281 feat: table搜索框支持多级自定义搜索 2022-02-09 18:47:24 +08:00
Michael Bai
f83739b496 fix: 修改第三方认证用户进行MFA认证配置项变量名 2022-02-09 10:34:09 +08:00
Michael Bai
1d9eddb11f feat: 增加系统设置(安全)控制第三方认证用户是否进行MFA认证 2022-02-08 17:49:21 +08:00
Michael Bai
da14abc83b feat: 命令过滤规则增加忽略大小写选项 2022-02-08 12:31:48 +08:00
“怀磊”
7b705dfc75 perf: 表单自动滚动到校验出错位置 2022-01-21 18:35:13 +08:00
“怀磊”
4516d83ce4 fix: 修复搜索框不能搜索false选项的问题 2022-01-20 15:05:35 +08:00
18 changed files with 135 additions and 51 deletions

View File

@@ -25,7 +25,7 @@
<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-form-item :label="this.$t('users.SSHKey')">
<el-input v-model="authInfo['private_key']" class="item-textarea" type="textarea" show-password />
</el-form-item>
</el-form>

View File

@@ -16,26 +16,27 @@
<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" />
<UpdateToken v-model="authInfo.password" />
</el-form-item>
<el-form-item :label="this.$t('assets.SSHSecretKey')">
<UploadKey @input="getFile" />
</el-form-item>
<el-form-item :label="this.$t('assets.Passphrase')">
<el-input v-model="authInfo.passphrase" type="password" />
<UpdateToken v-model="authInfo.passphrase" />
</el-form-item>
</el-form>
</Dialog>
</template>
<script>
import Dialog from '@/components/Dialog'
import { UploadKey } from '@/components'
import { Dialog, UploadKey } from '@/components'
import { UpdateToken } from '@/components/FormFields'
export default {
name: 'UpdateSecretInfo',
components: {
Dialog,
UploadKey
UploadKey,
UpdateToken
},
props: {
account: {

View File

@@ -161,6 +161,7 @@ export default {
watch: {
url(iNew) {
this.$set(this.tableConfig, 'url', iNew)
this.$set(this.headerActions.exportOptions, 'url', iNew.replace('/accounts/', '/account-secrets/'))
}
},
mounted() {

View File

@@ -110,9 +110,11 @@ export default {
this.currentNode = treeNode
this.currentNodeId = treeNode.meta.data.id
query['node'] = this.currentNodeId
query['asset'] = ''
url = `${this.setting.url}${combinator}node_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
} else if (treeNode.meta.type === 'asset') {
query['asset'] = treeNode.meta.data.id
query['node'] = ''
url = `${this.setting.url}${combinator}asset_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
}
this.$router.push({ query })

View File

@@ -23,6 +23,7 @@
<script>
import ElFormRender from './components/el-form-renderer'
import { scrollToError } from '@/utils'
export default {
components: {
ElFormRender
@@ -76,6 +77,7 @@ export default {
this.$emit('submit', form.getFormValue(), form, addContinue)
} else {
this.$emit('invalid', valid)
scrollToError(form.$el)
return false
}
})

View File

@@ -86,7 +86,11 @@ export default {
if (key === '') {
key = 'search'
}
data[key] = value
if (key.startsWith('search')) {
data['search'] = (data.search ? data.search + ',' : '') + value
} else {
data[key] = value
}
}
return data
},
@@ -109,6 +113,19 @@ export default {
handler(val) {
if (val && val.length > 0) {
const routeFilter = this.checkInTableColumns()
const routerSearch = routeFilter.search || {}
const routerSearchArrs = routerSearch?.value?.split(',') || []
const routerSearchArrsLength = routerSearchArrs.length || 0
if (routerSearch && routerSearchArrsLength > 0) {
for (let i = 0; i < routerSearchArrsLength; i++) {
const cur = routerSearchArrs[i]
routeFilter[`search_${cur}`] = {
...routerSearch,
value: cur
}
}
delete routeFilter.search
}
const asFilterTags = _.cloneDeep(this.filterTags)
this.filterTags = {
...asFilterTags,
@@ -130,37 +147,43 @@ export default {
if (Object.keys(this.filterMaps).length > 0) {
return this.$emit('tagSearch', this.filterMaps)
}
}
, 400)
}, 400)
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
},
methods: {
// 判断url中的查询条件
// 获取url中的查询条件,判断是不是包含在当前查询条件里
checkInTableColumns() {
const routeQuery = this.getUrlQuery ? this.$route?.query : {}
const routeQueryKeys = Object.keys(routeQuery)
const routeQueryKeysLength = routeQueryKeys.length
const keys = {}
if (routeQueryKeys.length < 1) return keys
if (routeQueryKeys.length > 0) {
for (let i = 0; i < routeQueryKeys.length; i++) {
if (routeQueryKeysLength < 1) return keys
if (routeQueryKeysLength > 0) {
for (let i = 0; i < routeQueryKeysLength; i++) {
const key = routeQueryKeys[i]
const valueDecode = decodeURI(routeQuery[key])
let valueDecode = decodeURI(routeQuery[key])
const isSearch = key !== 'search'
for (let k = 0, len = this.options.length; k < len; k++) {
const cur = this.options[k]
const curOptions = this.options || []
for (let k = 0, len = curOptions.length; k < len; k++) {
const cur = curOptions[k]
if (cur?.type === 'boolean') {
valueDecode = !!valueDecode
}
if (key === cur.value || !isSearch) {
const curChildren = cur.children || []
keys[key] = {
...cur,
key,
label: isSearch ? cur.label : '',
value: valueDecode
}
if (isSearch && curChildren.length > 0) {
curChildren.forEach(item => {
for (const item of curChildren) {
if (valueDecode === item.value) {
keys[key].valueLabel = item.label
break
}
})
}
}
}
}
@@ -200,23 +223,28 @@ export default {
this.$nextTick(() => this.$refs.Cascade.handleClear())
},
handleTagClose(evt) {
this.checkUrlFilds(evt)
this.$delete(this.filterTags, evt)
this.checkUrlFilds(evt)
this.$emit('tagSearch', this.filterMaps)
return true
},
handleConfirm() {
if (this.filterValue === '') return
if (this.filterValue && !this.filterKey) {
this.filterKey = 'search'
this.filterKey = 'search' + '_' + this.filterValue
}
const tag = { key: this.filterKey, label: this.keyLabel, value: this.filterValue, valueLabel: this.valueLabel }
this.$set(this.filterTags, this.filterKey, tag)
this.$emit('tagSearch', this.filterMaps)
// 修改查询参数时改变url中保存的参数
if (this.getUrlQuery) {
let newQuery = _.cloneDeep(this.$route.query)
newQuery = { ...newQuery, [this.filterKey]: encodeURI(this.filterValue) }
if (this.filterKey.startsWith('search')) {
newQuery = { ...newQuery, search: encodeURI(this.filterMaps.search) }
} else {
newQuery = { ...newQuery, [this.filterKey]: encodeURI(this.filterValue) }
}
this.$router.replace({ query: newQuery })
}
@@ -249,7 +277,15 @@ export default {
},
// 删除查询条件时改变url
checkUrlFilds(evt) {
const newQuery = _.omit(this.$route.query, evt)
let newQuery = _.omit(this.$route.query, evt)
if (this.getUrlQuery && evt.startsWith('search')) {
if (newQuery.search) delete newQuery.search
const filterMapsSearch = this.filterMaps.search || ''
newQuery = {
...newQuery,
...(filterMapsSearch && { search: encodeURI(filterMapsSearch) })
}
}
this.$router.replace({ query: newQuery })
}
}

View File

@@ -90,8 +90,8 @@
"kubernetes":"Kubernetes",
"clusterHelpTextMessage": "例如https://172.16.8.8:8443",
"DBInfo": "数据库信息",
"RDBProtocol": "关系型数据库协议",
"NoSQLProtocol": "非关系数据库协议"
"RDBProtocol": "关系型数据库",
"NoSQLProtocol": "非关系数据库"
},
"assets": {
"AppList": "应用列表",
@@ -232,8 +232,8 @@
"ipDomain": "IP(域名)",
"HostProtocol": "主机协议",
"DatabaseProtocol": "数据库协议",
"RDBProtocol": "关系型数据库协议",
"NoSQLProtocol": "非关系数据库协议",
"RDBProtocol": "关系型数据库",
"NoSQLProtocol": "非关系数据库",
"OtherProtocol": "其它协议",
"PasswordOrToken": "密码 / 令牌"
},

View File

@@ -110,3 +110,24 @@ export function param2Obj(url) {
export function getDateTimeStamp(dateStr) {
return Date.parse(dateStr.replace(/-/gi, '/'))
}
/**
* 自动滚动到错误位置
* @param {*} el 目标元素
* @param {Object} 滚动参数 scrollOption={
* behavior: 'smooth',
* block: 'center'
* }
*/
export const scrollToError = (
el,
scrollOption = {
behavior: 'smooth',
block: 'center'
}
) => {
setTimeout(() => {
const isError = el.getElementsByClassName('is-error')
isError[0].scrollIntoView(scrollOption)
}, 0)
}

View File

@@ -17,7 +17,6 @@ export default {
GenericTreeListPage, AccountListTable
},
data() {
const vm = this
return {
isInit: true,
clickedRow: null,
@@ -30,24 +29,25 @@ export default {
url: '/api/v1/assets/accounts/',
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
callback: {
onSelected: function(event, treeNode) {
let url = '/api/v1/assets/accounts/'
if (treeNode.meta.type === 'node') {
const nodeId = treeNode.meta.data.id
url = setUrlParam(url, 'asset', '')
url = setUrlParam(url, 'node', nodeId)
} else if (treeNode.meta.type === 'asset') {
const assetId = treeNode.meta.data.id
url = setUrlParam(url, 'node', '')
url = setUrlParam(url, 'asset', assetId)
}
setTimeout(() => {
vm.accountsUrl = url
}, 100)
}
onSelected: (event, treeNode) => this.getAccountsUrl(event, treeNode)
}
}
}
},
methods: {
getAccountsUrl(event, treeNode) {
let url = '/api/v1/assets/accounts/'
if (treeNode.meta.type === 'node') {
const nodeId = treeNode.meta.data.id
url = setUrlParam(url, 'asset', '')
url = setUrlParam(url, 'node', nodeId)
} else if (treeNode.meta.type === 'asset') {
const assetId = treeNode.meta.data.id
url = setUrlParam(url, 'node', '')
url = setUrlParam(url, 'asset', assetId)
}
this.accountsUrl = url
}
}
}
</script>

View File

@@ -20,11 +20,17 @@ export default {
return {
tableConfig: {
url: `/api/v1/assets/cmd-filters/${this.object.id}/rules/`,
columns: ['type', 'content', 'action', 'priority', 'pattern', 'comment', 'actions'],
columns: ['type', 'content', 'ignore_case', 'action', 'priority', 'pattern', 'comment', 'actions'],
columnsMeta: {
type: {
width: '100px'
},
ignore_case: {
width: '100px',
formatterArgs: {
showFalse: false
}
},
priority: {
width: '70px'
},

View File

@@ -28,7 +28,7 @@ export default {
action: 0
},
fields: [
[this.$t('common.Basic'), ['filter', 'type', 'content', 'priority', 'action', 'reviewers', 'comment']]
[this.$t('common.Basic'), ['filter', 'type', 'content', 'ignore_case', 'priority', 'action', 'reviewers', 'comment']]
],
fieldsMeta: {
filter: {

View File

@@ -76,7 +76,6 @@ export default {
}
},
user: {
width: '140px',
showOverflowTooltip: true
},
asset: {
@@ -210,7 +209,6 @@ export default {
const queryStr = (url.indexOf('?') > -1 ? '&' : '?') + queryUtil.stringify(_query, '=', '&')
const treeUrl = url + queryStr
this.$set(this.treeSetting, 'treeUrl', treeUrl)
this.treeTable.forceRerenderTree()
},
handleDateChange(object) {
this.query = {

View File

@@ -84,7 +84,7 @@ export default {
// 这个页面不去提交auth这些
const removeFields = [
'AUTH_CAS', 'AUTH_OPENID', 'AUTH_WECOM', 'AUTH_DINGTALK',
'AUTH_FEISHU', 'AUTH_RADIUS', 'AUTH_SSO'
'AUTH_FEISHU', 'AUTH_RADIUS', 'AUTH_SSO', 'AUTH_SAML2'
]
for (const i of removeFields) {
delete data[i]

View File

@@ -105,11 +105,17 @@ export default {
settings: {
visible: false,
url: '/api/v1/settings/setting/?category=ldap',
fields: ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_INTERVAL', 'AUTH_LDAP_SYNC_CRONTAB'],
fields: ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_CRONTAB', 'AUTH_LDAP_SYNC_INTERVAL'],
fieldsMeta: {
AUTH_LDAP_SYNC_IS_PERIODIC: {
type: 'switch'
},
AUTH_LDAP_SYNC_CRONTAB: {
component: CronTab,
helpText: this.$t('xpack.HelpText.CrontabOfCreateUpdatePage')
},
AUTH_LDAP_SYNC_INTERVAL: {
helpText: this.$t('xpack.HelpText.IntervalOfCreateUpdatePage')
}
},
submitMethod: () => 'patch'

View File

@@ -41,6 +41,7 @@ export default {
[
'SECURITY_MFA_AUTH',
'SECURITY_MFA_IN_LOGIN_PAGE',
'SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY',
'SECURITY_LOGIN_CHALLENGE_ENABLED',
'SECURITY_LOGIN_CAPTCHA_ENABLED',
'SECURITY_PASSWORD_EXPIRATION_TIME',

View File

@@ -132,6 +132,10 @@ export default {
{
label: 'SQLServer',
value: 'sqlserver'
},
{
label: 'Redis',
value: 'redis'
}
]
},

View File

@@ -16,17 +16,23 @@ export default {
tableConfig: {
url: '/api/v1/tickets/flows/',
columns: [
'type_display', 'created_by',
'type_display', 'created_by', 'org_name',
'date_created', 'date_updated', 'actions'
],
columnsShow: {
min: ['actions'],
default: [
'type_display', 'created_by',
'type_display', 'created_by', 'org_name',
'date_created', 'date_updated', 'actions'
]
},
columnsMeta: {
org_name: {
formatter: function(row, col, cell) {
var currentOrg = vm.$store.getters.currentOrg
return currentOrg.is_root ? row.org_name : currentOrg.name
}
},
type_display: {
formatter: DetailFormatter,
formatterArgs: {

View File

@@ -50,7 +50,7 @@ export default {
icon: 'fa-gear',
name: 'TicketFlow',
hidden: () => {
return !(vm.$store.getters.currentUserIsSuperAdmin || vm.$store.getters.currentUserIsAdmin)
return !vm.$store.getters.currentUserIsSuperAdmin
}
}
]