Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/components/Form/FormFields/ClipboardPolicy.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<template>
<div class="clipboard-policy">
<el-checkbox
v-for="item in items"
:key="item.key"
:value="can[item.key] && policy[item.key]"
:disabled="!can[item.key]"
@change="val => onToggle(item.key, val)"
>{{ $t(item.label) }}</el-checkbox>
<div
v-for="item in items.filter(i => i.limit)"
v-show="can[item.key] && policy[item.key]"
:key="item.limit"
class="clipboard-policy-limit"
>
<span>{{ $t(item.limitLabel) }}</span>
<el-input-number
v-model="policy[item.limit]"
:min="0"
size="mini"
controls-position="right"
@change="emitChange"
/>
<el-tooltip :content="$t('ZeroMeansUnlimited')" placement="top">
<i class="fa fa-question-circle-o" />
</el-tooltip>
</div>
</div>
</template>

<script>
// Each clipboard item is gated by a clipboard action, by data direction:
// server -> client (copy out): file_download / text_copy ↔ copy
// client -> server (paste in): file_upload / text_paste ↔ paste
const ITEMS = [
{ key: 'file_upload', label: 'FileUpload', action: 'paste' },
{ key: 'file_download', label: 'FileDownload', action: 'copy' },
{ key: 'text_copy', label: 'TextCopy', action: 'copy', limit: 'text_copy_max_length', limitLabel: 'TextCopyMaxLength' },
{ key: 'text_paste', label: 'TextPaste', action: 'paste', limit: 'text_paste_max_length', limitLabel: 'TextPasteMaxLength' }
]

export const defaultClipboardPolicy = () => ({
file_upload: true, file_download: true, text_copy: true, text_paste: true,
text_copy_max_length: 0, text_paste_max_length: 0
})

const actionValues = actions => (Array.isArray(actions) ? actions.map(i => (i && i.value ? i.value : i)) : [])

// Persist only what is enforceable given the granted actions (backend ANDs too).
export const applyActionsToClipboardPolicy = (value, actions) => {
const policy = { ...defaultClipboardPolicy(), ...(value || {}) }

Check warning on line 51 in src/components/Form/FormFields/ClipboardPolicy.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The empty object is useless.

See more on https://sonarcloud.io/project/issues?id=jumpserver_lina&issues=AZ7tThwBK19_7kdmNAKW&open=AZ7tThwBK19_7kdmNAKW&pullRequest=5409
const acts = actionValues(actions)
ITEMS.forEach(i => { if (!acts.includes(i.action)) policy[i.key] = false })
return policy
}

export default {
name: 'ClipboardPolicy',
props: {
value: { type: Object, default: () => ({}) },
actions: { type: Array, default: () => [] }
},
data() {
return { items: ITEMS, policy: { ...defaultClipboardPolicy(), ...this.value } }
},
computed: {
can() {
const acts = actionValues(this.actions)
return ITEMS.reduce((m, i) => ({ ...m, [i.key]: acts.includes(i.action) }), {})
}
},
watch: {
value(v) { this.policy = { ...defaultClipboardPolicy(), ...v } }
},
mounted() { this.emitChange() },
methods: {
onToggle(key, val) { this.policy[key] = val; this.emitChange() },
emitChange() { this.$emit('input', { ...this.policy }) }
}
}
</script>

<style lang="scss" scoped>
.clipboard-policy {
padding: 5px 0;

.clipboard-policy-limit {
margin-top: 8px;
display: flex;
align-items: center;
gap: 8px;
color: #606266;
font-size: 13px;

i { color: #8492a6; cursor: help; }
::v-deep .el-input-number--mini { width: 120px; }
}
}
</style>
11 changes: 10 additions & 1 deletion src/i18n/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,14 @@
"Confirm": "Confirm",
"Ignore": "Ignore",
"Please enter a reason": "Please enter a reason",
"PleaseEnterReason": "Please enter a reason | Please enter reasons"
"PleaseEnterReason": "Please enter a reason | Please enter reasons",
"Clipboard": "Clipboard",
"FileUpload": "File upload",
"FileDownload": "File download",
"TextCopy": "Text copy",
"TextPaste": "Text paste",
"TextCopyMaxLength": "Text copy character limit",
"TextPasteMaxLength": "Text paste character limit",
"ZeroMeansUnlimited": "0 means unlimited",
"ClipboardPolicyTip": "Controls clipboard file upload/download and text copy/paste for this permission in RDP sessions. Requires the copy/paste actions to take effect."
}
11 changes: 10 additions & 1 deletion src/i18n/langs/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,14 @@
"Other": "其他",
"Details": "详情",
"Confirm": "确认",
"Ignore": "忽略"
"Ignore": "忽略",
"Clipboard": "剪贴板",
"FileUpload": "文件上行",
"FileDownload": "文件下行",
"TextCopy": "字符串复制",
"TextPaste": "字符串粘贴",
"TextCopyMaxLength": "字符串复制字符数限制",
"TextPasteMaxLength": "字符串粘贴字符数限制",
"ZeroMeansUnlimited": "0 表示不限制",
"ClipboardPolicyTip": "控制该授权在 RDP 会话剪贴板中的文件上行/下行与字符串复制/粘贴权限,需配合动作中的复制、粘贴权限生效"
}
11 changes: 10 additions & 1 deletion src/i18n/langs/zh_hant.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,14 @@
"notAlphanumericUnderscore": "仅支持字母、数字和下划线。",
"notParenthesis": "不能包含括号。",
"InvalidJson": "JSON 格式错误.",
"requiredHasUserNameMapped": "用户名属性是必需的。"
"requiredHasUserNameMapped": "用户名属性是必需的。",
"Clipboard": "剪貼簿",
"FileUpload": "檔案上行",
"FileDownload": "檔案下行",
"TextCopy": "字串複製",
"TextPaste": "字串貼上",
"TextCopyMaxLength": "字串複製字元數限制",
"TextPasteMaxLength": "字串貼上字元數限制",
"ZeroMeansUnlimited": "0 表示不限制",
"ClipboardPolicyTip": "控制該授權在 RDP 工作階段剪貼簿中的檔案上行/下行與字串複製/貼上權限,需搭配動作中的複製、貼上權限生效"
}
19 changes: 18 additions & 1 deletion src/views/perms/AssetPermission/AssetPermissionCreateUpdate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import AssetSelect from '@/components/Apps/AssetSelect'
import AccountFormatter from './components/AccountFormatter'
import { AllAccount } from '../const'
import ProtocolsSelect from '@/components/Form/FormFields/AllOrSpec.vue'
import ClipboardPolicy, { applyActionsToClipboardPolicy, defaultClipboardPolicy } from '@/components/Form/FormFields/ClipboardPolicy.vue'

export default {
name: 'AccountFormatter',
Expand All @@ -33,7 +34,8 @@ export default {
initial: {
nodes: nodesInitial,
assets: assetsInitial,
accounts: [AllAccount]
accounts: [AllAccount],
clipboard_policy: defaultClipboardPolicy()
},
fields: [
[this.$t('Basic'), ['name']],
Expand All @@ -43,6 +45,7 @@ export default {
[this.$t('Account'), ['accounts']],
[this.$t('Protocol'), ['protocols']],
[this.$t('Action'), ['actions']],
[this.$t('Clipboard'), ['clipboard_policy']],
[this.$t('Other'), ['is_active', 'date_start', 'date_expired', 'comment']]
],
url: '/api/v1/perms/asset-permissions/',
Expand Down Expand Up @@ -124,6 +127,19 @@ export default {
label: this.$t('Action'),
helpText: this.$t('ActionsTips')
},
clipboard_policy: {
label: this.$t('Clipboard'),
component: ClipboardPolicy,
helpText: this.$t('ClipboardPolicyTip'),
el: {
actions: []
},
hidden: (formValue) => {
// Feed the live actions into the component so each clipboard
// sub-permission is gated by its matching action.
this.fieldsMeta.clipboard_policy.el.actions = formValue.actions || []
}
},
date_start: {},
date_expired: {},
comment: {},
Expand All @@ -135,6 +151,7 @@ export default {
if (!Array.isArray(value.accounts)) {
value.accounts = value.accounts ? value.accounts.split(',') : []
}
value.clipboard_policy = applyActionsToClipboardPolicy(value.clipboard_policy, value.actions)
return value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ export default {
)
}
},
{
key: this.$t('Clipboard'),
value: this.object.clipboard_policy,
formatter: (row, p = {}) => {
const items = [
['FileUpload', p.file_upload], ['FileDownload', p.file_download],
['TextCopy', p.text_copy, p.text_copy_max_length], ['TextPaste', p.text_paste, p.text_paste_max_length]
]
const labels = items.filter(i => i[1]).map(i => this.$t(i[0]) + (i[2] > 0 ? `(${i[2]})` : ''))
return labels.length ? labels.join('、') : '-'
}
},
'date_start', 'date_expired', 'date_created', 'created_by', 'comment'
]
}
Expand Down