perf: update encrypt support gm

This commit is contained in:
ibuler
2026-03-31 16:11:08 +08:00
parent 74d50146e3
commit 30f50f67c3
16 changed files with 328 additions and 188 deletions

View File

@@ -59,6 +59,7 @@
"npm": "^7.8.0",
"nprogress": "0.2.0",
"path-to-regexp": "3.3.0",
"sm-crypto": "^0.4.0",
"sortablejs": "^1.15.6",
"v-sanitize": "^0.0.13",
"vue": "2.7.16",

View File

@@ -2,7 +2,7 @@
<AutoDataForm
v-if="!loading"
ref="AutoDataForm"
:class="addTemplate? '': 'account-add'"
:class="addTemplate ? '' : 'account-add'"
:submit-btn-text="submitBtnText"
v-bind="$data"
@submit="confirm"
@@ -11,7 +11,7 @@
<script>
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
export default {
@@ -59,16 +59,27 @@ export default {
]
},
url: '/api/v1/accounts/accounts/',
form: Object.assign({ 'on_invalid': 'error' }, this.account || {}),
form: Object.assign({ on_invalid: 'error' }, this.account || {}),
encryptedFields: ['secret'],
fields: [
[this.$t('Basic'), ['name', 'username', 'privileged', 'su_from', 'su_from_username', 'template']],
[
this.$t('Basic'),
['name', 'username', 'privileged', 'su_from', 'su_from_username', 'template']
],
[this.$t('Asset'), ['nodes', 'assets']],
[this.$t('Secret'), [
'secret_type', 'password', 'ssh_key', 'token',
'access_key', 'passphrase', 'api_key',
'secret_reset'
]],
[
this.$t('Secret'),
[
'secret_type',
'password',
'ssh_key',
'token',
'access_key',
'passphrase',
'api_key',
'secret_reset'
]
],
[this.$t('Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
],
fieldsMeta: accountFieldsMeta(this),
@@ -170,7 +181,7 @@ export default {
}
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.account-add {
::v-deep .el-form-item {
//margin-bottom: 5px;

View File

@@ -11,7 +11,7 @@
<script>
import { GenericUpdateFormDialog } from '@/layout/components'
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
export default {
name: 'AccountBulkUpdateDialog',
@@ -25,7 +25,7 @@ export default {
},
selectedRows: {
type: Array,
default: () => ([])
default: () => []
}
},
data() {
@@ -35,7 +35,7 @@ export default {
hasSaveContinue: false,
fields: [],
fieldsMeta: accountFieldsMeta(this),
cleanOtherFormValue: (formValue) => {
cleanOtherFormValue: formValue => {
for (const value of formValue) {
Object.keys(value).forEach((item, index, arr) => {
if (['ssh_key', 'token', 'access_key', 'api_key', 'password'].includes(item)) {
@@ -82,6 +82,4 @@ export default {
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -23,7 +23,7 @@
<script>
import Dialog from '@/components/Dialog/index.vue'
import { accountFieldsMeta } from '@/components/Apps/AccountCreateUpdateForm/const'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
export default {
@@ -46,8 +46,14 @@ export default {
const accountMeta = accountFieldsMeta(this)
return {
fields: [
'name', 'secret_type', 'password', 'ssh_key', 'token',
'access_key', 'passphrase', 'api_key'
'name',
'secret_type',
'password',
'ssh_key',
'token',
'access_key',
'passphrase',
'api_key'
],
fieldsMeta: {
...accountMeta,
@@ -80,18 +86,19 @@ export default {
const data = {
secret: encryptPassword(form[secretType])
}
this.$axios.patch(
`/api/v1/accounts/accounts/${this.account.id}/`,
data,
{ disableFlashErrorMsg: true }
).then(res => {
this.$message.success(this.$tc('UpdateSuccessMsg'))
this.iVisible = false
}).catch(err => {
const errMsg = Object.values(err.response.data).join(', ')
this.$message.error(this.$tc('UpdateErrorMsg') + ' ' + errMsg)
this.iVisible = false
})
this.$axios
.patch(`/api/v1/accounts/accounts/${this.account.id}/`, data, {
disableFlashErrorMsg: true
})
.then(res => {
this.$message.success(this.$tc('UpdateSuccessMsg'))
this.iVisible = false
})
.catch(err => {
const errMsg = Object.values(err.response.data).join(', ')
this.$message.error(this.$tc('UpdateErrorMsg') + ' ' + errMsg)
this.iVisible = false
})
},
handleCancel() {
this.$emit('update:visible', false)

View File

@@ -20,10 +20,12 @@
<el-form-item :label="secretTypeLabel">
<SecretViewerFormatter
:cell-value="secretInfo.secret"
:col="{ formatterArgs: {
name: account['name'],
secretType: secretType || ''
}}"
:col="{
formatterArgs: {
name: account['name'],
secretType: secretType || ''
}
}"
@input="onShowKeyCopyFormatterChange"
/>
</el-form-item>
@@ -36,12 +38,12 @@
<el-form-item :label="$tc('DateUpdated')">
<span>{{ account['date_updated'] | date }}</span>
</el-form-item>
<el-form-item v-if="showPasswordRecord" v-perms="'accounts.view_accountsecret'" :label="$tc('PasswordRecord')">
<el-link
:underline="false"
type="success"
@click="showHistoryDialog"
>
<el-form-item
v-if="showPasswordRecord"
v-perms="'accounts.view_accountsecret'"
:label="$tc('PasswordRecord')"
>
<el-link :underline="false" type="success" @click="showHistoryDialog">
<span style="padding-right: 30px">
{{ versions }}
</span>
@@ -61,7 +63,7 @@
import Dialog from '@/components/Dialog/index.vue'
import PasswordHistoryDialog from './PasswordHistoryDialog.vue'
import { SecretViewerFormatter } from '@/components/Table/TableFormatters'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import { mapGetters } from 'vuex'
export default {
@@ -90,7 +92,7 @@ export default {
},
title: {
type: String,
default: function() {
default: function () {
return this.$tc('Detail')
}
},
@@ -144,7 +146,8 @@ export default {
name: this.secretInfo.name,
secret: encryptPassword(this.modifiedSecret)
}
const url = this.type === 'account' ? `/api/v1/accounts/accounts` : `/api/v1/accounts/account-templates`
const url =
this.type === 'account' ? `/api/v1/accounts/accounts` : `/api/v1/accounts/account-templates`
this.$axios.patch(`${url}/${this.account.id}/`, params).then(() => {
this.$message.success(this.$tc('UpdateSuccessMsg'))
})
@@ -154,7 +157,7 @@ export default {
this.$message.warning(this.$tc('AccountSecretReadDisabled'))
return
}
return this.$axios.get(this.url).then((res) => {
return this.$axios.get(this.url).then(res => {
this.secretInfo = res
this.sshKeyFingerprint = res?.spec_info?.ssh_key_fingerprint || '-'
this.showSecret = true
@@ -180,7 +183,7 @@ export default {
}
.el-form-item {
border-bottom: 1px solid #EBEEF5;
border-bottom: 1px solid #ebeef5;
padding: 5px 0;
margin-bottom: 0;

View File

@@ -118,7 +118,7 @@
</template>
<script>
import Dialog from '@/components/Dialog/index.vue'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import { logout as redirectToLogout } from '@/utils/request'
export default {
@@ -180,7 +180,7 @@ export default {
this.inputPlaceholder = this.subTypeChoices.filter(item => item.name === val)[0]?.placeholder
this.smsWidth = val === 'sms' ? 6 : 0
},
performConfirm: _.debounce(function({ response, callback, cancel }) {
performConfirm: _.debounce(function ({ response, callback, cancel }) {
if (this.processing || this.visible) {
return
}

View File

@@ -11,10 +11,18 @@
<div class="row">
<el-progress :percentage="processedPercent" />
</div>
<DataTable v-if="tableGenDone" id="importTable" ref="dataTable" :config="tableConfig" class="importTable" />
<DataTable
v-if="tableGenDone"
id="importTable"
ref="dataTable"
:config="tableConfig"
class="importTable"
/>
<div class="row" style="padding-top: 20px">
<div class="btn-groups">
<el-button v-if="showCancel" size="small" @click="performCancel">{{ $t('Cancel') }}</el-button>
<el-button v-if="showCancel" size="small" @click="performCancel">{{
$t('Cancel')
}}</el-button>
<el-button
v-show="!disableImportBtn"
size="small"
@@ -45,7 +53,7 @@ import DataTable from '@/components/Table/DataTable/index.vue'
import { getUpdateObjURL } from '@/utils/common/index'
import { sleep } from '@/utils/common/time'
import { EditableInputFormatter } from '@/components/Table/TableFormatters'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
export default {
@@ -162,17 +170,17 @@ export default {
return this.importActions[this.importAction]
},
successData() {
return this.iTotalData.filter((item) => {
return this.iTotalData.filter(item => {
return item['@status'] === 'ok'
})
},
failedData() {
return this.iTotalData.filter((item) => {
return this.iTotalData.filter(item => {
return typeof item['@status'] === 'object' && item['@status'].name === 'error'
})
},
pendingData() {
return this.iTotalData.filter((item) => {
return this.iTotalData.filter(item => {
return item['@status'] === 'pending'
})
},
@@ -195,7 +203,7 @@ export default {
if (this.totalCount === 0) {
return 0
}
return Math.round(this.processedCount / this.totalCount * 100)
return Math.round((this.processedCount / this.totalCount) * 100)
},
elDataTable() {
return this.$refs['dataTable'].dataTable
@@ -218,7 +226,7 @@ export default {
} else if (val === 'error') {
this.tableConfig.totalData = this.failedData
} else {
this.tableConfig.totalData = this.iTotalData.filter((item) => {
this.tableConfig.totalData = this.iTotalData.filter(item => {
return item['@status'] === val
})
}
@@ -278,7 +286,8 @@ export default {
return columns
},
getEncryptFields() {
const fromProp = Array.isArray(this.encryptFields) && this.encryptFields.length ? this.encryptFields : null
const fromProp =
Array.isArray(this.encryptFields) && this.encryptFields.length ? this.encryptFields : null
return fromProp || ['password', 'secret', 'private_key']
},
generateTableData(tableTitles, tableData) {
@@ -415,11 +424,7 @@ export default {
},
async performUpdateObject(item) {
const updateUrl = getUpdateObjURL(this.url, item.id)
return this.$axios.patch(
updateUrl,
item,
{ disableFlashErrorMsg: true }
)
return this.$axios.patch(updateUrl, item, { disableFlashErrorMsg: true })
},
async defaultPerformUploadObject(item) {
let handler = this.performCreateObject
@@ -439,11 +444,7 @@ export default {
}
},
async performCreateObject(item) {
return this.$axios.post(
this.url,
item,
{ disableFlashErrorMsg: true }
)
return this.$axios.post(this.url, item, { disableFlashErrorMsg: true })
},
keepElementInViewport() {
const tableRef = document.getElementById('importTable')
@@ -455,11 +456,7 @@ export default {
const rect = parentTdRef.getBoundingClientRect()
let windowInnerHeight = window.innerHeight || document.documentElement.clientHeight
windowInnerHeight = windowInnerHeight * 0.97 - 150
const inViewport = (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= windowInnerHeight
)
const inViewport = rect.top >= 0 && rect.left >= 0 && rect.bottom <= windowInnerHeight
if (!inViewport) {
parentTdRef.scrollIntoView({ behavior: 'auto', block: 'start', inline: 'start' })
}
@@ -468,8 +465,7 @@ export default {
this.tableConfig.totalData.push(item)
},
handleClick(btn) {
const callback = btn.callback || function() {
}
const callback = btn.callback || function () {}
callback(btn)
}
}
@@ -477,10 +473,10 @@ export default {
</script>
<style lang="scss" scoped>
@import "~@/styles/variables";
@import '~@/styles/variables';
.summary-item {
padding: 0 10px
padding: 0 10px;
}
.summary-success {

View File

@@ -19,7 +19,7 @@
<script>
import AutoDataForm from '@/components/Form/AutoDataForm'
import { getUpdateObjURL } from '@/utils/common/index'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import deepmerge from 'deepmerge'
export default {
@@ -45,12 +45,12 @@ export default {
},
afterGetFormValue: {
type: Function,
default: (value) => value
default: value => value
},
// 提交前清理form的值
cleanFormValue: {
type: Function,
default: (value) => value
default: value => value
},
// 获取 meta
afterGetRemoteMeta: {
@@ -76,28 +76,28 @@ export default {
// 创建成功的msg
createSuccessMsg: {
type: String,
default: function() {
default: function () {
return this.$t('CreateSuccessMsg')
}
},
// 保存成功继续添加的msg
saveSuccessContinueMsg: {
type: String,
default: function() {
default: function () {
return this.$t('SaveSuccessContinueMsg')
}
},
// 更新成功的msg
updateSuccessMsg: {
type: String,
default: function() {
default: function () {
return this.$t('UpdateSuccessMsg')
}
},
// 创建成功的跳转路由
createSuccessNextRoute: {
type: Object,
default: function() {
default: function () {
const routeName = this.$route.name?.replace('Create', 'List')
return { name: routeName }
}
@@ -105,16 +105,15 @@ export default {
// 更新成功的跳转路由
updateSuccessNextRoute: {
type: Object,
default: function() {
default: function () {
const routeName = this.$route.name?.replace('Update', 'List')
return { name: routeName }
}
},
objectDetailRoute: {
type: Object,
default: function() {
const routeName = this.$route.name?.replace('Update', 'Detail')
.replace('Create', 'Detail')
default: function () {
const routeName = this.$route.name?.replace('Update', 'Detail').replace('Create', 'Detail')
return { name: routeName }
}
},
@@ -127,7 +126,7 @@ export default {
},
cloneNameSuffix: {
type: [String, Number],
default: function() {
default: function () {
return this.$t('Duplicate').toLowerCase()
}
},
@@ -139,7 +138,7 @@ export default {
// 获取创建和更新的url function
getUrl: {
type: Function,
default: function() {
default: function () {
const objectId = this.getUpdateId()
let url = this.url
if (objectId) {
@@ -180,12 +179,16 @@ export default {
msg = msg[0].toLowerCase() + msg.slice(1)
this.$message({
message: h('p', null, [
h('el-link', {
on: {
click: () => this.$router.push(detailRoute)
h(
'el-link',
{
on: {
click: () => this.$router.push(detailRoute)
},
style: { 'vertical-align': 'top', 'margin-right': '5px' }
},
style: { 'vertical-align': 'top', 'margin-right': '5px' }
}, msgLinkName),
msgLinkName
),
h('span', {}, msg)
]),
type: 'success'
@@ -200,9 +203,12 @@ export default {
default(res, method, vm, addContinue) {
const route = this.getNextRoute(res, method)
if (!(route.params && route.params.id)) {
route['params'] = deepmerge(route['params'] || {}, { 'id': res.id })
route['params'] = deepmerge(route['params'] || {}, { id: res.id })
}
route['query'] = deepmerge(route['query'], { 'order': this.extraQueryOrder, 'updated': new Date().getTime() })
route['query'] = deepmerge(route['query'], {
order: this.extraQueryOrder,
updated: new Date().getTime()
})
this.$emit('submitSuccess', res)
@@ -344,7 +350,7 @@ export default {
encryptFields(values) {
// 批量提交clean 后可能是个数组
if (values instanceof Array) {
return values.map((item) => this.encryptFields(item))
return values.map(item => this.encryptFields(item))
}
values = { ...values }
for (const field of this.encryptedFields) {
@@ -372,8 +378,8 @@ export default {
defaultOnSubmit(validValues, formName, addContinue) {
this.isSubmitting = true
this.performSubmit(validValues)
.then((res) => this.onPerformSuccess.bind(this)(res, this.method, this, addContinue))
.catch((error) => this.onPerformError(error, this.method, this))
.then(res => this.onPerformSuccess.bind(this)(res, this.method, this, addContinue))
.catch(error => this.onPerformError(error, this.method, this))
.finally(() => {
setTimeout(() => {
this.isSubmitting = false
@@ -383,7 +389,7 @@ export default {
},
async getCloneForm(cloneFrom) {
const [curUrl, query] = this.url.split('?')
const url = `${curUrl}${cloneFrom}/${query ? ('?' + query) : ''}`
const url = `${curUrl}${cloneFrom}/${query ? '?' + query : ''}`
try {
const object = await this.getObjectDetail(url)
let name = ''
@@ -438,5 +444,4 @@ export default {
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,6 +1,4 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
import { encryptPassword } from './session-encrypt'
/**
* @param {string} path
@@ -43,60 +41,6 @@ const options = {
}
}
const filter = new xss.FilterXSS(options)
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
import CryptoJS from 'crypto-js'
import { vueCookie as VueCookie } from '@/utils/storage'
export function fillKey(key) {
const KeyLength = 16
if (key.length > KeyLength) {
key = key.slice(0, KeyLength)
}
const filledKey = Buffer.alloc(KeyLength)
const keys = Buffer.from(key)
for (let i = 0; i < keys.length; i++) {
filledKey[i] = keys[i]
}
return filledKey
}
export function aesEncrypt(text, originKey) {
const key = CryptoJS.enc.Utf8.parse(fillKey(originKey))
return CryptoJS.AES.encrypt(text, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
}).toString()
}
export function rsaEncrypt(text, pubKey) {
const jsEncrypt = new JSEncrypt()
jsEncrypt.setPublicKey(pubKey)
return jsEncrypt.encrypt(text)
}
export function getCookie(name) {
return VueCookie.get(name)
}
export function encryptPassword(password) {
if (!password) {
return ''
}
let rsaPublicKeyText = getCookie('jms_public_key')
if (!rsaPublicKeyText) {
return password
}
const aesKey = (Math.random() + 1).toString(36).substring(2)
// public key 是 base64 存储的
rsaPublicKeyText = rsaPublicKeyText.replaceAll('"', '')
const rsaPublicKey = atob(rsaPublicKeyText)
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey)
const passwordCipher = aesEncrypt(String(password), aesKey)
return `${keyCipher}:${passwordCipher}`
}
window.aesEncrypt = aesEncrypt
window.fillKey = fillKey
export default filter
window.encryptPassword = encryptPassword

View File

@@ -0,0 +1,160 @@
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
import CryptoJS from 'crypto-js'
import { vueCookie as VueCookie } from '@/utils/storage'
import { sm2, sm4 } from 'sm-crypto'
export function getCookie(name) {
return VueCookie.get(name)
}
export function fillKey(key) {
const KeyLength = 16
if (key.length > KeyLength) {
key = key.slice(0, KeyLength)
}
const filledKey = Buffer.alloc(KeyLength)
const keys = Buffer.from(key)
for (let i = 0; i < keys.length; i++) {
filledKey[i] = keys[i]
}
return filledKey
}
function aesEncrypt(text, originKey) {
const key = CryptoJS.enc.Utf8.parse(fillKey(originKey))
return CryptoJS.AES.encrypt(text, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.ZeroPadding
}).toString()
}
function rsaEncrypt(text, pubKey) {
if (!text) {
return text
}
const jsEncrypt = new JSEncrypt()
jsEncrypt.setPublicKey(pubKey)
return jsEncrypt.encrypt(text)
}
function rsaDecrypt(cipher, pkey) {
const jsEncrypt = new JSEncrypt()
jsEncrypt.setPrivateKey(pkey)
return jsEncrypt.decrypt(cipher)
}
window.rsaEncrypt = rsaEncrypt
window.rsaDecrypt = rsaDecrypt
function hexToBytes(hex) {
if (!hex) return new Uint8Array([])
hex = hex.toString().trim().toLowerCase()
if (hex.startsWith('0x')) {
hex = hex.slice(2)
}
// 确保是偶数长度
const len = Math.floor(hex.length / 2)
const bytes = new Uint8Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16)
}
return bytes
}
function bytesToBase64(bytes) {
// Uint8Array -> base64标准 base64
let binary = ''
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i])
}
return btoa(binary)
}
function rsaEncryptPassword(password, rsaPublicKey) {
const aesKey = (Math.random() + 1).toString(36).substring(2)
// public key 是 base64 存储的
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey)
const passwordCipher = aesEncrypt(password, aesKey)
return `${keyCipher}:${passwordCipher}`
}
function ensureSm2PublicKey(sm2PublicKey) {
// sm2.min.js 的 doEncrypt 需要能被 decodePointHex 解析的公钥:
// 通常为非压缩点 hex格式 `04||x||y`(总长度 130
// 但后端生成/下发的公钥有时是 `x||y`(长度 128这里做归一化补齐 `04` 前缀。
if (typeof sm2PublicKey === 'string') {
sm2PublicKey = sm2PublicKey.replaceAll('"', '').trim()
if (sm2PublicKey.startsWith('0x')) {
sm2PublicKey = sm2PublicKey.slice(2)
}
// 后端下发的 SM2 公钥常见是 x||y128 hexsm-crypto 需要 04||x||y130 hex
if (sm2PublicKey.length === 128 && !sm2PublicKey.startsWith('04')) {
sm2PublicKey = '04' + sm2PublicKey
}
}
return sm2PublicKey
}
function gmEncryptPassword(password, sm2PublicKey) {
sm2PublicKey = ensureSm2PublicKey(sm2PublicKey)
// 只适配前端,不改后端:
// 直接生成 16 字符 key后端 padding_key 会保持原样,不再补齐)
const sm4KeyRaw = randomString(16)
const sm4KeyHex = Buffer.from(sm4KeyRaw).toString('hex')
let keyCipher = ''
try {
// 与后端 gmssl.sm2.CryptSM2 默认 decrypt 的 mode 对齐:
// gmssl 解析的格式是 C1C2C3mode=0前端这里输出也用 mode=0。
keyCipher = sm2.doEncrypt(sm4KeyRaw, sm2PublicKey, 0)
} catch (e) {
console.error('gmEncryptPassword sm2.doEncrypt failed:', e)
// 避免前端崩溃:失败时返回明文,由后端按原值流程处理(至少可继续登录/看报错)
return password
}
const passwordCipher = sm4.encrypt(password, sm4KeyHex)
// sm2/sm4 默认输出是 hex但后端 gm.py/session.py 需要 base64
// - sm2_decrypt: base64.b64decode
// - sm4 decrypt: base64.urlsafe_b64decode
const keyCipherB64 = bytesToBase64(hexToBytes(keyCipher))
const passwordCipherB64 = bytesToBase64(hexToBytes(passwordCipher))
return `${keyCipherB64}:${passwordCipherB64}`
}
export function encryptPassword(password) {
if (!password) {
console.log('password is empty')
return ''
}
let publicKeyText = getCookie('jms_public_key')
if (!publicKeyText) {
console.log('publicKeyText is empty')
return password
}
publicKeyText = publicKeyText.replaceAll('"', '')
publicKeyText = atob(publicKeyText)
let cipher = ''
let jmsGMSSL = getCookie('jms_gm_ssl')
if (publicKeyText.includes('PUBLIC KEY')) {
jmsGMSSL = '0'
}
if (jmsGMSSL === '1') {
cipher = gmEncryptPassword(password, publicKeyText)
} else {
cipher = rsaEncryptPassword(password, publicKeyText)
}
return cipher
}
export function randomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
const charactersLength = characters.length
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength))
}
return result
}

View File

@@ -5,7 +5,7 @@
<script>
import { GenericCreateUpdatePage } from '@/layout/components'
import getChangeSecretFields from '@/views/accounts/AccountBackup/fields'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import { periodicMeta } from '@/components/const'
export default {
@@ -20,7 +20,8 @@ export default {
url: '/api/v1/accounts/account-backup-plans/',
fields: [
[this.$t('Basic'), ['name', 'types']],
[this.$t('Backup'),
[
this.$t('Backup'),
[
'backup_type',
'is_password_divided_by_email',
@@ -91,6 +92,4 @@ export default {
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,14 +1,11 @@
<template>
<GenericCreateUpdatePage
v-bind="$data"
@getObjectDone="getObjectDone"
/>
<GenericCreateUpdatePage v-bind="$data" @getObjectDone="getObjectDone" />
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { templateFields, templateFieldsMeta } from './const.js'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
export default {
name: 'GatewayCreateUpdate',
@@ -27,7 +24,7 @@ export default {
return {
initial: {
secret_type: 'password',
push_params: { }
push_params: {}
},
url: '/api/v1/accounts/account-templates/',
hasDetailInMsg: false,

View File

@@ -4,7 +4,7 @@
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
import { getUpdateObjURL, setUrlParam } from '@/utils/common/index'
import { assetFieldsMeta } from '@/views/assets/const'
@@ -38,7 +38,7 @@ export default {
},
updateInitial: {
type: Function,
default: (initial) => {
default: initial => {
return initial
}
}
@@ -82,7 +82,7 @@ export default {
delete values['accounts']
} else {
const accounts = values?.accounts || []
values.accounts = accounts.map((item) => {
values.accounts = accounts.map(item => {
item['secret'] = encryptPassword(item['secret'])
return item
})
@@ -179,5 +179,4 @@ export default {
}
</script>
<style>
</style>
<style></style>

View File

@@ -1,5 +1,5 @@
import i18n from '@/i18n/i18n'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
export const gcp = 'gcp'
export const aliyun = 'aliyun'
@@ -55,8 +55,18 @@ export const publicHostProviders = [
export const publicDBProviders = [aliyun]
export const privateCloudProviders = [
vmware, qingcloud_private, huaweicloud_private, ctyun_private,
openstack, zstack, nutanix, fc, scp, apsara_stack, smartx, proxmox
vmware,
qingcloud_private,
huaweicloud_private,
ctyun_private,
openstack,
zstack,
nutanix,
fc,
scp,
apsara_stack,
smartx,
proxmox
]
export const ACCOUNT_PROVIDER_ATTRS_MAP = {

View File

@@ -11,7 +11,7 @@
import { GenericCreateUpdatePage } from '@/layout/components'
import { STORAGE_TYPE_META_MAP } from '@/views/sessions/const'
import { UploadSecret } from '@/components/Form/FormFields'
import { encryptPassword } from '@/utils/secure'
import { encryptPassword } from '@/utils/session-encrypt'
export default {
name: 'ReplayStorageUpdate',
@@ -51,19 +51,19 @@ export default {
fields: storageTypeMeta.meta,
fieldsMeta: {
SFTP_PASSWORD: {
hidden: (formValue) => formValue.STP_SECRET_TYPE !== 'password'
hidden: formValue => formValue.STP_SECRET_TYPE !== 'password'
},
STP_PRIVATE_KEY: {
component: UploadSecret,
hidden: (formValue) => formValue.STP_SECRET_TYPE !== 'ssh_key'
hidden: formValue => formValue.STP_SECRET_TYPE !== 'ssh_key'
},
STP_PASSPHRASE: {
hidden: (formValue) => formValue.STP_SECRET_TYPE !== 'ssh_key'
hidden: formValue => formValue.STP_SECRET_TYPE !== 'ssh_key'
}
}
},
is_default: {
hidden: (formValue) => formValue.type === 'sftp'
hidden: formValue => formValue.type === 'sftp'
},
comment: {
component: 'el-input',
@@ -102,6 +102,4 @@ export default {
}
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -8577,6 +8577,11 @@ js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.7.0, js-yaml@^3.9.
argparse "^1.0.7"
esprima "^4.0.0"
jsbn@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.npmmirror.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -12849,6 +12854,13 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
sm-crypto@^0.4.0:
version "0.4.0"
resolved "https://registry.npmmirror.com/sm-crypto/-/sm-crypto-0.4.0.tgz#13917f5b41e8428d764e9e6934840fdede6a4022"
integrity sha512-OexH2V1EqmhXuOIPGoCl55OjMF0wwPUM/zhUjT0Q6vHBeopSRvTNRy76/1eRoFs3VBKt39hdFnxwpFmooHYa2A==
dependencies:
jsbn "^1.1.0"
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"