mirror of
https://github.com/PanJiaChen/vue-element-admin.git
synced 2026-01-09 00:28:27 +08:00
fix(security): prevent stored XSS in role notifications
This commit is contained in:
parent
6858a9ad67
commit
502e1886c5
@ -2,7 +2,7 @@
|
|||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-button type="primary" @click="handleAddRole">New Role</el-button>
|
<el-button type="primary" @click="handleAddRole">New Role</el-button>
|
||||||
|
|
||||||
<el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
|
<el-table :data="rolesList" style="width: 100%; margin-top: 30px" border>
|
||||||
<el-table-column align="center" label="Role Key" width="220">
|
<el-table-column align="center" label="Role Key" width="220">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ scope.row.key }}
|
{{ scope.row.key }}
|
||||||
@ -20,13 +20,24 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="Operations">
|
<el-table-column align="center" label="Operations">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="primary" size="small" @click="handleEdit(scope)">Edit</el-button>
|
<el-button
|
||||||
<el-button type="danger" size="small" @click="handleDelete(scope)">Delete</el-button>
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleEdit(scope)"
|
||||||
|
>Edit</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="handleDelete(scope)"
|
||||||
|
>Delete</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
|
<el-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:title="dialogType === 'edit' ? 'Edit Role' : 'New Role'"
|
||||||
|
>
|
||||||
<el-form :model="role" label-width="80px" label-position="left">
|
<el-form :model="role" label-width="80px" label-position="left">
|
||||||
<el-form-item label="Name">
|
<el-form-item label="Name">
|
||||||
<el-input v-model="role.name" placeholder="Role Name" />
|
<el-input v-model="role.name" placeholder="Role Name" />
|
||||||
@ -34,7 +45,7 @@
|
|||||||
<el-form-item label="Desc">
|
<el-form-item label="Desc">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="role.description"
|
v-model="role.description"
|
||||||
:autosize="{ minRows: 2, maxRows: 4}"
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="Role Description"
|
placeholder="Role Description"
|
||||||
/>
|
/>
|
||||||
@ -51,8 +62,11 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div style="text-align:right;">
|
<div style="text-align: right">
|
||||||
<el-button type="danger" @click="dialogVisible=false">Cancel</el-button>
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
@click="dialogVisible = false"
|
||||||
|
>Cancel</el-button>
|
||||||
<el-button type="primary" @click="confirmRole">Confirm</el-button>
|
<el-button type="primary" @click="confirmRole">Confirm</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -62,7 +76,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { deepClone } from '@/utils'
|
import { deepClone } from '@/utils'
|
||||||
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
|
import {
|
||||||
|
getRoutes,
|
||||||
|
getRoles,
|
||||||
|
addRole,
|
||||||
|
deleteRole,
|
||||||
|
updateRole
|
||||||
|
} from '@/api/role'
|
||||||
|
|
||||||
const defaultRole = {
|
const defaultRole = {
|
||||||
key: '',
|
key: '',
|
||||||
@ -113,9 +133,14 @@ export default {
|
|||||||
|
|
||||||
for (let route of routes) {
|
for (let route of routes) {
|
||||||
// skip some route
|
// skip some route
|
||||||
if (route.hidden) { continue }
|
if (route.hidden) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
|
const onlyOneShowingChild = this.onlyOneShowingChild(
|
||||||
|
route.children,
|
||||||
|
route
|
||||||
|
)
|
||||||
|
|
||||||
if (route.children && onlyOneShowingChild && !route.alwaysShow) {
|
if (route.children && onlyOneShowingChild && !route.alwaysShow) {
|
||||||
route = onlyOneShowingChild
|
route = onlyOneShowingChild
|
||||||
@ -124,7 +149,6 @@ export default {
|
|||||||
const data = {
|
const data = {
|
||||||
path: path.resolve(basePath, route.path),
|
path: path.resolve(basePath, route.path),
|
||||||
title: route.meta && route.meta.title
|
title: route.meta && route.meta.title
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recursive child routes
|
// recursive child routes
|
||||||
@ -137,7 +161,7 @@ export default {
|
|||||||
},
|
},
|
||||||
generateArr(routes) {
|
generateArr(routes) {
|
||||||
let data = []
|
let data = []
|
||||||
routes.forEach(route => {
|
routes.forEach((route) => {
|
||||||
data.push(route)
|
data.push(route)
|
||||||
if (route.children) {
|
if (route.children) {
|
||||||
const temp = this.generateArr(route.children)
|
const temp = this.generateArr(route.children)
|
||||||
@ -182,7 +206,9 @@ export default {
|
|||||||
message: 'Delete succed!'
|
message: 'Delete succed!'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(err => { console.error(err) })
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
generateTree(routes, basePath = '/', checkedKeys) {
|
generateTree(routes, basePath = '/', checkedKeys) {
|
||||||
const res = []
|
const res = []
|
||||||
@ -192,20 +218,39 @@ export default {
|
|||||||
|
|
||||||
// recursive child routes
|
// recursive child routes
|
||||||
if (route.children) {
|
if (route.children) {
|
||||||
route.children = this.generateTree(route.children, routePath, checkedKeys)
|
route.children = this.generateTree(
|
||||||
|
route.children,
|
||||||
|
routePath,
|
||||||
|
checkedKeys
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
|
if (
|
||||||
|
checkedKeys.includes(routePath) ||
|
||||||
|
(route.children && route.children.length >= 1)
|
||||||
|
) {
|
||||||
res.push(route)
|
res.push(route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
|
escapeHTML(str = '') {
|
||||||
|
return str
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''')
|
||||||
|
},
|
||||||
async confirmRole() {
|
async confirmRole() {
|
||||||
const isEdit = this.dialogType === 'edit'
|
const isEdit = this.dialogType === 'edit'
|
||||||
|
|
||||||
const checkedKeys = this.$refs.tree.getCheckedKeys()
|
const checkedKeys = this.$refs.tree.getCheckedKeys()
|
||||||
this.role.routes = this.generateTree(deepClone(this.serviceRoutes), '/', checkedKeys)
|
this.role.routes = this.generateTree(
|
||||||
|
deepClone(this.serviceRoutes),
|
||||||
|
'/',
|
||||||
|
checkedKeys
|
||||||
|
)
|
||||||
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
await updateRole(this.role.key, this.role)
|
await updateRole(this.role.key, this.role)
|
||||||
@ -228,8 +273,8 @@ export default {
|
|||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
message: `
|
message: `
|
||||||
<div>Role Key: ${key}</div>
|
<div>Role Key: ${key}</div>
|
||||||
<div>Role Name: ${name}</div>
|
<div>Role Name: ${this.escapeHTML(name)}</div>
|
||||||
<div>Description: ${description}</div>
|
<div>Description: ${this.escapeHTML(description)}</div>
|
||||||
`,
|
`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
@ -237,7 +282,7 @@ export default {
|
|||||||
// reference: src/view/layout/components/Sidebar/SidebarItem.vue
|
// reference: src/view/layout/components/Sidebar/SidebarItem.vue
|
||||||
onlyOneShowingChild(children = [], parent) {
|
onlyOneShowingChild(children = [], parent) {
|
||||||
let onlyOneChild = null
|
let onlyOneChild = null
|
||||||
const showingChildren = children.filter(item => !item.hidden)
|
const showingChildren = children.filter((item) => !item.hidden)
|
||||||
|
|
||||||
// When there is only one child route, the child route is displayed by default
|
// When there is only one child route, the child route is displayed by default
|
||||||
if (showingChildren.length === 1) {
|
if (showingChildren.length === 1) {
|
||||||
@ -248,7 +293,7 @@ export default {
|
|||||||
|
|
||||||
// Show parent if there are no child route to display
|
// Show parent if there are no child route to display
|
||||||
if (showingChildren.length === 0) {
|
if (showingChildren.length === 0) {
|
||||||
onlyOneChild = { ... parent, path: '', noShowingChildren: true }
|
onlyOneChild = { ...parent, path: '', noShowingChildren: true }
|
||||||
return onlyOneChild
|
return onlyOneChild
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user