提交 5457a369 编写于 作者: H haifeng414

Merge remote-tracking branch 'origin/dev' into dev

package io.metersphere.commons.constants;
public class SessionConstants {
public static final String ATTR_USER = "user";
}
......@@ -34,7 +34,7 @@ public class WorkspaceController {
@GetMapping("delete/{workspaceId}")
@RequiresRoles(RoleConstants.ORG_ADMIN)
public void saveWorkspace(@PathVariable String workspaceId) {
public void deleteWorkspace(@PathVariable String workspaceId) {
workspaceService.checkOwner(workspaceId);
workspaceService.deleteWorkspace(workspaceId);
}
......
package io.metersphere.dto;
import io.metersphere.base.domain.Role;
import io.metersphere.base.domain.UserRole;
import java.util.ArrayList;
import java.util.List;
......@@ -22,6 +23,10 @@ public class UserDTO {
private String lastSourceId;
private List<Role> roles = new ArrayList<>();
private List<UserRole> userRoles = new ArrayList<>();
public String getLastSourceId() {
return lastSourceId;
}
......@@ -30,8 +35,6 @@ public class UserDTO {
this.lastSourceId = lastSourceId;
}
private List<Role> roles = new ArrayList<>();
public List<Role> getRoles() {
return roles;
}
......@@ -97,4 +100,12 @@ public class UserDTO {
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
public List<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(List<UserRole> userRoles) {
this.userRoles = userRoles;
}
}
......@@ -5,7 +5,7 @@ import io.metersphere.base.domain.Role;
import io.metersphere.dto.UserDTO;
import io.metersphere.service.UserService;
import io.metersphere.user.SessionUser;
import org.apache.shiro.SecurityUtils;
import io.metersphere.user.SessionUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
......@@ -68,7 +68,7 @@ public class ShiroDBRealm extends AuthorizingRealm {
// TODO 密码验证
SessionUser sessionUser = SessionUser.fromUser(user);
SecurityUtils.getSubject().getSession().setAttribute("user", sessionUser);
SessionUtils.putUser(sessionUser);
return new SimpleAuthenticationInfo(userId, password, getName());
}
......
package io.metersphere.service;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.ProjectExample;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.user.SessionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -16,12 +20,22 @@ public class ProjectService {
private ProjectMapper projectMapper;
public Project addProject(Project project) {
if (StringUtils.isBlank(project.getName())) {
MSException.throwException("Project name cannot be null");
}
ProjectExample example = new ProjectExample();
example.createCriteria()
.andWorkspaceIdEqualTo(SessionUtils.getCurrentWorkspaceId())
.andNameEqualTo(project.getName());
if (projectMapper.countByExample(example) > 0) {
MSException.throwException("The project name already exists");
}
project.setId(UUID.randomUUID().toString());
long createTime = System.currentTimeMillis();
project.setCreateTime(createTime);
project.setUpdateTime(createTime);
// todo set workspace id
// project.setWorkspaceId();
// set workspace id
project.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
projectMapper.insertSelective(project);
return project;
}
......
......@@ -89,6 +89,8 @@ public class UserService {
if (CollectionUtils.isEmpty(userRoleList)) {
return userDTO;
}
// 设置 user_role
userDTO.setUserRoles(userRoleList);
List<String> roleIds = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
......
package io.metersphere.service;
import io.metersphere.base.domain.UserRole;
import io.metersphere.base.domain.Workspace;
import io.metersphere.base.domain.WorkspaceExample;
import io.metersphere.base.mapper.WorkspaceMapper;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.user.SessionUser;
import io.metersphere.user.SessionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -10,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
......@@ -21,12 +27,18 @@ public class WorkspaceService {
if (StringUtils.isBlank(workspace.getName())) {
MSException.throwException("Workspace name cannot be null.");
}
// TODO 组织ID 暂无
if (StringUtils.isBlank(workspace.getOrganizationId())) {
workspace.setOrganizationId("root");
}
// set organization id
workspace.setOrganizationId(SessionUtils.getCurrentOrganizationId());
long currentTime = System.currentTimeMillis();
if (StringUtils.isBlank(workspace.getId())) {
WorkspaceExample example = new WorkspaceExample();
example.createCriteria()
.andOrganizationIdEqualTo(SessionUtils.getCurrentOrganizationId())
.andNameEqualTo(workspace.getName());
if (workspaceMapper.countByExample(example) > 0) {
MSException.throwException("The workspace name already exists");
}
workspace.setId(UUID.randomUUID().toString()); // 设置ID
workspace.setCreateTime(currentTime);
workspace.setUpdateTime(currentTime); // 首次 update time
......@@ -47,7 +59,18 @@ public class WorkspaceService {
}
public void checkOwner(String workspaceId) {
// TODO 验证当前用户是否拥有当前此空间权限
SessionUser user = SessionUtils.getUser();
List<String> orgIds = user.getUserRoles().stream()
.filter(ur -> RoleConstants.ORG_ADMIN.equals(ur.getRoleId()))
.map(UserRole::getSourceId)
.collect(Collectors.toList());
WorkspaceExample example = new WorkspaceExample();
example.createCriteria()
.andOrganizationIdIn(orgIds)
.andIdEqualTo(workspaceId);
if (workspaceMapper.countByExample(example) == 0) {
MSException.throwException("The current workspace does not belong to the current user");
}
}
}
package io.metersphere.user;
import io.metersphere.dto.UserDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import java.io.Serializable;
import static io.metersphere.commons.constants.RoleConstants.*;
public class SessionUser extends UserDTO implements Serializable {
private static final long serialVersionUID = -7149638440406959033L;
private String workspaceId;
private String organizationId;
public String getWorkspaceId() {
return workspaceId;
}
public String getOrganizationId() {
return organizationId;
}
public static SessionUser fromUser(UserDTO user) {
SessionUser sessionUser = new SessionUser();
BeanUtils.copyProperties(user, sessionUser);
String lastSourceId = sessionUser.getLastSourceId();
user.getUserRoles().forEach(ur -> {
if (StringUtils.equals(ur.getSourceId(), lastSourceId)) {
if (StringUtils.equals(ur.getRoleId(), ORG_ADMIN)) {
sessionUser.organizationId = lastSourceId;
return;
}
if (StringUtils.equalsAny(ur.getRoleId(), TEST_MANAGER, TEST_USER, TEST_VIEWER)) {
sessionUser.workspaceId = lastSourceId;
}
}
});
return sessionUser;
}
......
......@@ -4,15 +4,32 @@ import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import java.util.Optional;
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
public class SessionUtils {
public static SessionUser getUser() {
try {
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
return (SessionUser) session.getAttribute("user");
return (SessionUser) session.getAttribute(ATTR_USER);
} catch (Exception e) {
return null;
}
}
//
public static void putUser(SessionUser sessionUser) {
SecurityUtils.getSubject().getSession().setAttribute(ATTR_USER, sessionUser);
}
public static String getCurrentWorkspaceId() {
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getWorkspaceId();
}
public static String getCurrentOrganizationId() {
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getOrganizationId();
}
}
......@@ -16,7 +16,7 @@
</div>
<div class="form">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="邮箱" autocomplete="off" maxlength="100"
<el-input v-model="form.username" placeholder="邮箱" autofocus autocomplete="off" maxlength="100"
show-word-limit/>
</el-form-item>
<el-form-item prop="password">
......@@ -109,8 +109,10 @@
this.$post("signin", this.form, (response) => {
// 登录信息保存 cookie
Cookies.set(TokenKey, response.data);
// 保存上次角色
localStorage.setItem("lastSourceId", JSON.parse(Cookies.get(TokenKey)).lastSourceId);
let rolesArray = response.data.roles;
let roles = rolesArray.map(r => r.id);
// 保存角色
localStorage.setItem("roles", roles);
window.location.href = "/"
});
} else {
......
......@@ -2,7 +2,6 @@
<el-col v-if="auth">
<el-row id="header-top" type="flex" justify="space-between" align="middle">
<a class="logo"/>
<ms-switch-user/>
<ms-user/>
</el-row>
<el-row id="header-bottom" type="flex" justify="space-between" align="middle">
......@@ -11,7 +10,7 @@
</el-col>
<el-col :span="4">
<el-row type="flex" justify="center" align="middle">
<router-link to="/createTest">
<router-link to="/createTest" v-permission="['test_user','test_manager']">
<el-button type="primary" size="small">创建测试</el-button>
</router-link>
</el-row>
......@@ -30,7 +29,6 @@
import MsSetting from "./components/HeaderSetting";
import MsView from "./components/router/View";
import MsUser from "./components/HeaderUser";
import MsSwitchUser from "./components/HeaderSwitchUser";
import MsWebSocket from "./components/websocket/WebSocket";
export default {
......@@ -51,7 +49,7 @@
window.location.href = "/login"
});
},
components: {MsWebSocket, MsUser, MsMenus, MsSetting, MsView, MsSwitchUser},
components: {MsWebSocket, MsUser, MsMenus, MsSetting, MsView},
methods: {
}
}
......
......@@ -2,13 +2,13 @@
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router
menu-trigger="click">
<el-menu-item index="1"><a href="/" style="text-decoration: none;">{{ $t("i18n.home") }}</a></el-menu-item>
<el-submenu index="2" popper-class="submenu">
<el-submenu index="2" popper-class="submenu" v-permission="['org_admin']">
<template slot="title">工作空间</template>
<el-menu-item index="2-1">工作空间1</el-menu-item>
<el-menu-item index="2-2">工作空间2</el-menu-item>
<el-menu-item index="2-3">显示全部</el-menu-item>
</el-submenu>
<el-submenu index="3" popper-class="submenu">
<el-submenu index="3" popper-class="submenu" v-permission="['test_manager']">
<template slot="title">项目</template>
<el-menu-item index="3-1">项目1</el-menu-item>
<el-menu-item index="3-2">项目2</el-menu-item>
......@@ -21,7 +21,7 @@
<el-button type="text">创建项目</el-button>
</el-menu-item>
</el-submenu>
<el-submenu index="4" popper-class="submenu">
<el-submenu index="4" popper-class="submenu" v-permission="['test_manager', 'test_user']">
<template slot="title">测试</template>
<recent-test-plan/>
<el-divider/>
......@@ -33,7 +33,7 @@
<el-button type="text">创建测试</el-button>
</el-menu-item>
</el-submenu>
<el-submenu index="5" popper-class="submenu">
<el-submenu index="5" popper-class="submenu" v-permission="['test_manager', 'test_user', 'test_viewer']">
<template slot="title">报告</template>
<el-menu-item index="5-1">报告1</el-menu-item>
<el-menu-item index="5-2">报告2</el-menu-item>
......
<template>
<el-row>
<el-button type="text" @click="open">切换角色-{{ currentUserRole }}</el-button>
<el-dialog title="角色列表" :visible.sync="createVisible" width="30%">
<el-tree :data="userRoleList"
@node-click="handleNodeClick"
:props="defaultProps"
></el-tree>
<div style="text-align: center; margin-top: 20px;">
<el-button type="info" size="mini" class="ms-button" @click="closeDialog">取消</el-button>
<el-button type="primary" size="mini" style="margin-left: 50px;" @click="changeSubmit">保存</el-button>
</div>
</el-dialog>
</el-row>
</template>
<script>
import Cookies from "js-cookie"
import {TokenKey} from "../../common/constants"
export default {
name: "MsSwitchUser",
computed: {
currentUserRole() {
return this.userInfo.lastSourceId;
}
},
created() {
this.getUserRoleList();
this.getUserInfo();
},
data() {
return {
createVisible: false,
defaultProps: {
children: 'children',
label: 'switchInfo'
},
switchInfo: '',
userRoleList: [],
selectNode:[],
userInfo: {},
userId: JSON.parse(Cookies.get(TokenKey)).id
}
},
methods: {
open() {
this.createVisible = true;
},
getUserRoleList() {
this.$get('user/rolelist/' + this.userId).then(response => {
let roleList = response.data.data;
let newRoleList = [];
roleList.forEach(item => {
// item.current = item.id === this.userInfo.lastSourceId;
item.current = item.roleId;
if (item.current) {
if (item.name) {
item.switchInfo = item.name + " [" + item.desc + "]";
} else {
item.switchInfo = "MeterSphere[系统管理员]";
}
}
if (!item.parentId) {
item.hasChild = false;
item.children = [];
newRoleList.push(item);
} else {
newRoleList.forEach(userRole => {
if (userRole.id === item.parentId) {
userRole.children.push(item);
userRole.hasChild = true;
}
})
}
})
this.userRoleList = newRoleList;
})
},
closeDialog() {
this.createVisible = false;
},
switchRole(selectNode) {
if (!selectNode.switchable) {
return;
}
this.$post("user/switch/source/" + selectNode.roleId).then(() => {
this.getUserInfo();
// localStorage.setItem("lastSourceId", "bbbbb");
this.closeDialog();
});
localStorage.setItem("lastSourceId", selectNode.roleId);
window.location.reload();
},
changeSubmit() {
this.switchRole(this.selectNode);
},
handleNodeClick(data) {
this.selectNode = data;
window.console.log(data)
},
getUserInfo() {
this.$get("/user/info/" + this.userId).then(response => {
this.userInfo = response.data.data;
})
}
}
}
</script>
<style scoped>
</style>
<template>
<div class="project-container">
<div class="main-content">
<el-card>
<el-card v-loading="result.loading">
<div slot="header">
<el-row type="flex" justify="space-between" align="middle">
<span class="title">
......@@ -14,7 +14,7 @@
</span>
</el-row>
</div>
<el-table :data="items" style="width: 100%" v-loading="loading">
<el-table :data="items" style="width: 100%">
<el-table-column prop="name" label="名称"/>
<el-table-column prop="description" label="描述"/>
<el-table-column>
......@@ -70,7 +70,7 @@
data() {
return {
createVisible: false,
loading: false,
result: {},
btnTips: "添加项目",
condition: "",
items: [],
......@@ -87,7 +87,7 @@
}
},
mounted() {
// this.list();
this.list();
},
destroyed() {
this.createVisible = false;
......@@ -104,14 +104,12 @@
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.loading = true;
let saveType = "add";
if (this.form.id) {
saveType = "update"
}
this.$post("/project/" + saveType, this.form, () => {
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.loading = false;
this.list();
Message.success('保存成功');
});
......@@ -136,8 +134,10 @@
},
list() {
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.$post(url, {}, (response) => {
this.items = response.data;
this.result = this.$post(url, {}, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
})
},
handleSizeChange(size) {
......
<template>
<div v-loading="loading">
<div v-loading="result.loading">
<el-card>
<div slot="header">
......@@ -87,7 +87,7 @@
deletePath: '/organization/delete/',
createPath: '/organization/add',
updatePath: '/organization/update',
loading: false,
result: {},
createVisible: false,
updateVisible: false,
multipleSelection: [],
......@@ -133,7 +133,7 @@
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$get(this.deletePath + row.id,() => {
this.result = this.$get(this.deletePath + row.id,() => {
this.$message({
type: 'success',
message: '删除成功!'
......@@ -150,7 +150,7 @@
createOrganization(createOrganizationForm) {
this.$refs[createOrganizationForm].validate( valide => {
if (valide) {
this.$post(this.createPath, this.form,() => {
this.result = this.$post(this.createPath, this.form,() => {
this.$message({
type: 'success',
message: '添加成功!'
......@@ -166,14 +166,13 @@
updateOrganization(udpateOrganizationForm) {
this.$refs[udpateOrganizationForm].validate(valide => {
if (valide) {
this.$post(this.updatePath, this.form,() => {
this.result = this.$post(this.updatePath, this.form,() => {
this.$message({
type: 'success',
message: '修改成功!'
});
this.updateVisible = false;
this.initTableData();
self.loading = false;
});
} else {
return false;
......@@ -181,7 +180,7 @@
})
},
initTableData() {
this.$post(this.buildPagePath(this.queryPath),{},response => {
this.result = this.$post(this.buildPagePath(this.queryPath),{},response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
......
<template>
<el-menu menu-trigger="click" :default-active="$route.path" router>
<el-submenu index="1">
<el-submenu index="1" v-permission="['admin']">
<template slot="title">
<font-awesome-icon class="icon account" :icon="['far', 'address-card']" size="lg"/>
<span>账号</span>
</template>
<el-menu-item index="/setting/user">用户</el-menu-item>
<el-menu-item index="/setting/testresourcepool">测试资源池</el-menu-item>
<el-menu-item index="/setting/testresourcepool" v-permission="['test']">测试资源池</el-menu-item>
<el-menu-item index="/setting/organization">组织</el-menu-item>
<el-menu-item index="/setting/workspace">工作空间</el-menu-item>
<el-menu-item>API Keys</el-menu-item>
</el-submenu>
<el-submenu index=2>
<el-submenu index=2 v-permission="['admin','org_admin']">
<template slot="title">
<font-awesome-icon class="icon workspace" :icon="['far', 'clone']" size="lg"/>
<span>工作空间</span>
......
<template>
<div v-loading="loading">
<div v-loading="result.loading">
<el-card>
<div slot="header">
......@@ -115,7 +115,7 @@
deletePath: '/user/delete/',
createPath: '/user/add',
updatePath: '/user/update',
loading: false,
result: {},
createVisible: false,
updateVisible: false,
multipleSelection: [],
......@@ -180,7 +180,7 @@
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$get(this.deletePath + row.id, () => {
this.result = this.$get(this.deletePath + row.id, () => {
this.$message({
type: 'success',
message: '删除成功!'
......@@ -197,7 +197,7 @@
createUser(createUserForm) {
this.$refs[createUserForm].validate(valide => {
if (valide) {
this.$post(this.createPath, this.form, () => {
this.result = this.$post(this.createPath, this.form, () => {
this.$message({
type: 'success',
message: '添加成功!'
......@@ -213,14 +213,13 @@
updateUser(updateUserForm) {
this.$refs[updateUserForm].validate(valide => {
if (valide) {
this.$post(this.updatePath, this.form,() => {
this.result = this.$post(this.updatePath, this.form,() => {
this.$message({
type: 'success',
message: '修改成功!'
});
this.updateVisible = false;
this.initTableData();
self.loading = false;
});
} else {
return false;
......@@ -228,7 +227,7 @@
})
},
initTableData() {
this.$post(this.buildPagePath(this.queryPath),{},response => {
this.result = this.$post(this.buildPagePath(this.queryPath),{},response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
......
......@@ -13,7 +13,7 @@
</span>
</el-row>
</div>
<el-table :data="items" style="width: 100%" v-loading="loading">
<el-table :data="items" style="width: 100%">
<el-table-column prop="name" label="名称"/>
<el-table-column prop="description" label="描述"/>
<el-table-column>
......@@ -76,14 +76,12 @@
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.loading = true;
let saveType = 'add';
if (this.form.id) {
saveType = 'update'
}
this.$post("/workspace/" + saveType, this.form, () => {
this.createVisible = false;
this.loading = false;
this.list();
Message.success('保存成功');
});
......@@ -94,7 +92,6 @@
},
edit(row) {
this.createVisible = true;
this.loading = true;
this.form = row;
// let self = this;
......
......@@ -7,10 +7,13 @@ const whiteList = ['/login']; // no redirect whitelist
export const permission = {
inserted(el, binding) {
const { value } = binding;
const roles = localStorage.getItem("lastSourceId");
const rolesString = localStorage.getItem("roles");
const roles = rolesString.split(',');
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value;
const hasPermission = permissionRoles.includes(roles);
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
});
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册