mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-2312 check object permission (#980)
* CB-2312 check object permission * CB-2312 anonymous project access Co-authored-by: yagudin10 <yagudin10@yandex.ru>
This commit is contained in:
co-authored by
yagudin10
parent
905eace2e8
commit
15599bc29b
@@ -79,4 +79,8 @@ public class VirtualProjectImpl extends BaseProjectImpl {
|
||||
public void ensureOpen() {
|
||||
|
||||
}
|
||||
@NotNull
|
||||
public RMProject getRmProject() {
|
||||
return this.project;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -32,6 +32,7 @@ import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.registry.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.registry.EclipseWorkspaceImpl;
|
||||
import io.cloudbeaver.VirtualProjectImpl;
|
||||
@@ -123,12 +124,9 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMController getResourceController(@NotNull SMCredentialsProvider credentialsProvider) {
|
||||
return createResourceController(credentialsProvider);
|
||||
}
|
||||
public RMController getResourceController(@NotNull SMCredentialsProvider credentialsProvider, @NotNull SMController smController) {
|
||||
return LocalResourceController.builder(credentialsProvider, smController).build();
|
||||
|
||||
protected @NotNull RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider) {
|
||||
return LocalResourceController.builder(credentialsProvider).build();
|
||||
}
|
||||
|
||||
protected Map<String, Object> getServerConfigProps(Map<String, Object> configProps) {
|
||||
|
||||
+4
-1
@@ -51,7 +51,10 @@ public interface WebApplication extends DBPApplication {
|
||||
|
||||
SMAdminController getAdminSecurityController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
RMController getResourceController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
RMController getResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull SMController smController
|
||||
);
|
||||
|
||||
String getServerURL();
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
@Override
|
||||
public DBNResourceManagerProject[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null) {
|
||||
List<DBPProject> projectList = getParentNode().getModel().getModelProjects();
|
||||
List<? extends DBPProject> projectList = getParentNode().getModel().getModelProjects();
|
||||
SMSession session = null;
|
||||
for (DBPProject project : projectList) {
|
||||
session = getParentNode().getModel().getModelAuthContext().getSpaceSession(monitor, project, false);
|
||||
|
||||
@@ -4,9 +4,13 @@ import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RMUtils {
|
||||
|
||||
@@ -33,4 +37,19 @@ public class RMUtils {
|
||||
return getUserProjectsPath().resolve(project.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> parseProjectPermissions(Set<String> permissions) {
|
||||
return permissions.stream()
|
||||
.map(RMProjectPermission::fromPermission).filter(Objects::nonNull)
|
||||
.flatMap(permission -> permission.getAllPermissions().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static RMProject createAnonymousProject() {
|
||||
RMProject project = new RMProject("anonymous");
|
||||
project.setId("anonymous");
|
||||
project.setType(RMProject.Type.USER);
|
||||
project.setProjectPermissions(RMProjectPermission.CONNECTIONS_EDIT.getAllPermissions());
|
||||
return project;
|
||||
}
|
||||
}
|
||||
|
||||
+95
-37
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.VirtualProjectImpl;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
@@ -32,6 +33,8 @@ import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.registry.*;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -63,6 +66,7 @@ public class LocalResourceController implements RMController {
|
||||
private final Path userProjectsPath;
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private final SMController smController;
|
||||
|
||||
private final Map<String, VirtualProjectImpl> projectRegistries = new LinkedHashMap<>();
|
||||
|
||||
@@ -70,11 +74,14 @@ public class LocalResourceController implements RMController {
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
Path rootPath,
|
||||
Path userProjectsPath,
|
||||
Path sharedProjectsPath) {
|
||||
Path sharedProjectsPath,
|
||||
SMController smController
|
||||
) {
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.rootPath = rootPath;
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
this.sharedProjectsPath = sharedProjectsPath;
|
||||
this.smController = smController;
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
}
|
||||
@@ -94,7 +101,7 @@ public class LocalResourceController implements RMController {
|
||||
VirtualProjectImpl project = projectRegistries.get(projectId);
|
||||
if (project == null) {
|
||||
SessionContextImpl sessionContext = new SessionContextImpl(null);
|
||||
RMProject rmProject = makeProjectFromId(projectId);
|
||||
RMProject rmProject = makeProjectFromId(projectId, false);
|
||||
project = new VirtualProjectImpl(
|
||||
rmProject,
|
||||
sessionContext);
|
||||
@@ -107,39 +114,80 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull
|
||||
@Override
|
||||
public RMProject[] listAccessibleProjects() throws DBException {
|
||||
try {
|
||||
List<RMProject> projects;
|
||||
if (Files.exists(sharedProjectsPath)) {
|
||||
projects = Files.list(sharedProjectsPath)
|
||||
.map((Path path) -> makeProjectFromPath(path, RMProject.Type.SHARED, true))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
projects = new ArrayList<>();
|
||||
}
|
||||
RMProject globalProject = makeProjectFromPath(getGlobalProjectPath(), RMProject.Type.GLOBAL, true);
|
||||
if (globalProject != null) {
|
||||
projects.add(globalProject);
|
||||
}
|
||||
RMProject userProject = makeProjectFromPath(getPrivateProjectPath(), RMProject.Type.USER, false);
|
||||
if (userProject != null) {
|
||||
projects.add(0, userProject);
|
||||
}
|
||||
return projects.toArray(new RMProject[0]);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error reading projects", e);
|
||||
List<RMProject> projects;
|
||||
//TODO refactor after implement current user api in sm
|
||||
var activeUserCreds = credentialsProvider.getActiveUserCredentials();
|
||||
if (Files.exists(sharedProjectsPath) || activeUserCreds != null && activeUserCreds.getUserId() != null) {
|
||||
var accessibleSharedProjects = smController.getAllAvailableObjectsPermissions(
|
||||
activeUserCreds.getUserId(),
|
||||
SMObjects.PROJECT
|
||||
);
|
||||
|
||||
projects = accessibleSharedProjects
|
||||
.stream()
|
||||
.map(projectPermission -> makeProjectFromPath(
|
||||
sharedProjectsPath.resolve(projectPermission.getObjectId()),
|
||||
projectPermission.getPermissions().stream().map(RMProjectPermission::fromPermission).collect(Collectors.toSet()),
|
||||
RMProject.Type.SHARED, true)
|
||||
)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
projects = new ArrayList<>();
|
||||
}
|
||||
|
||||
//FIXME: remove legacy global project support
|
||||
//admin has all edit access
|
||||
//user has only read access
|
||||
var globalProjectPermissions = getProjectPermissions(globalProjectName, RMProject.Type.GLOBAL);
|
||||
|
||||
RMProject globalProject = makeProjectFromPath(getGlobalProjectPath(), globalProjectPermissions, RMProject.Type.GLOBAL, true);
|
||||
if (globalProject != null) {
|
||||
projects.add(globalProject);
|
||||
}
|
||||
|
||||
//user has full access to his private project
|
||||
var userProjectPermission = getProjectPermissions(null, RMProject.Type.USER);
|
||||
RMProject userProject = makeProjectFromPath(getPrivateProjectPath(), userProjectPermission, RMProject.Type.USER, false);
|
||||
if (userProject != null) {
|
||||
projects.add(0, userProject);
|
||||
}
|
||||
return projects.toArray(new RMProject[0]);
|
||||
}
|
||||
|
||||
private Set<RMProjectPermission> getProjectPermissions(@Nullable String projectId, RMProject.Type projectType) throws DBException {
|
||||
var activeUserCreds = credentialsProvider.getActiveUserCredentials();
|
||||
|
||||
switch (projectType) {
|
||||
case GLOBAL:
|
||||
return credentialsProvider.hasPermission(DBWConstants.PERMISSION_ADMIN)
|
||||
? Set.of(RMProjectPermission.RESOURCE_EDIT, RMProjectPermission.CONNECTIONS_EDIT)
|
||||
: Set.of(RMProjectPermission.RESOURCE_VIEW, RMProjectPermission.CONNECTIONS_VIEW);
|
||||
case SHARED:
|
||||
if(projectId == null) {
|
||||
throw new DBException("Project id required");
|
||||
}
|
||||
return smController.getObjectPermissions(activeUserCreds.getUserId(), projectId, SMObjects.PROJECT)
|
||||
.getPermissions()
|
||||
.stream()
|
||||
.map(RMProjectPermission::fromPermission)
|
||||
.collect(Collectors.toSet());
|
||||
case USER:
|
||||
return Set.of(RMProjectPermission.RESOURCE_EDIT, RMProjectPermission.CONNECTIONS_EDIT);
|
||||
default:
|
||||
throw new DBException("Unknown project type:" + projectType);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMProject[] listSharedProjects() throws DBException {
|
||||
public RMProject[] listAllSharedProjects() throws DBException {
|
||||
try {
|
||||
if (!Files.exists(sharedProjectsPath)) {
|
||||
return new RMProject[0];
|
||||
}
|
||||
return Files.list(sharedProjectsPath)
|
||||
.map((Path path) -> makeProjectFromPath(path, RMProject.Type.SHARED, false))
|
||||
.map((Path path) -> makeProjectFromPath(path, Set.of(), RMProject.Type.SHARED, false))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(RMProject[]::new);
|
||||
} catch (IOException e) {
|
||||
@@ -158,8 +206,7 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
validateResourcePath(name);
|
||||
RMProject project;
|
||||
project = makeProjectFromPath(sharedProjectsPath.resolve(name),
|
||||
RMProject.Type.SHARED, false);
|
||||
project = makeProjectFromPath(sharedProjectsPath.resolve(name), Set.of(), RMProject.Type.SHARED, false);
|
||||
if (project == null) {
|
||||
throw new DBException("Project '" + name + "' already exists");
|
||||
}
|
||||
@@ -173,7 +220,7 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull String projectId) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId);
|
||||
RMProject project = makeProjectFromId(projectId, false);
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
throw new DBException("Project '" + project.getName() + "' doesn't exists");
|
||||
@@ -187,7 +234,7 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId);
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
if (readResources) {
|
||||
project.setChildren(
|
||||
listResources(projectId, null, null, true, false, true)
|
||||
@@ -341,7 +388,7 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
makeProjectFromId(projectId),
|
||||
makeProjectFromId(projectId, false),
|
||||
rmResourcePath
|
||||
)
|
||||
);
|
||||
@@ -431,13 +478,17 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
|
||||
private RMProject makeProjectFromId(String projectId) throws DBException {
|
||||
private RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
var projectName = parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
return makeProjectFromPath(projectPath, projectName.getType(), false);
|
||||
Set<RMProjectPermission> permissions = Set.of();
|
||||
if(loadPermissions && credentialsProvider.getActiveUserCredentials() != null) {
|
||||
permissions = getProjectPermissions(projectId, projectName.getType());
|
||||
}
|
||||
return makeProjectFromPath(projectPath, permissions, projectName.getType(), false);
|
||||
}
|
||||
|
||||
private RMProject makeProjectFromPath(Path path, RMProject.Type type, boolean checkExistence) {
|
||||
private RMProject makeProjectFromPath(Path path, Set<RMProjectPermission> permissions, RMProject.Type type, boolean checkExistence) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -450,11 +501,16 @@ public class LocalResourceController implements RMController {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<String> allProjectPermissions = permissions.stream()
|
||||
.flatMap(rmProjectPermission -> rmProjectPermission.getAllPermissions().stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
RMProject project = new RMProject();
|
||||
String projectName = path.getFileName().toString();
|
||||
project.setName(projectName);
|
||||
project.setId(type.getPrefix() + "_" + projectName);
|
||||
project.setType(type);
|
||||
project.setProjectPermissions(allProjectPermissions);
|
||||
if (Files.exists(path)) {
|
||||
try {
|
||||
project.setCreateTime(
|
||||
@@ -550,19 +606,21 @@ public class LocalResourceController implements RMController {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public static Builder builder(SMCredentialsProvider credentialsProvider) {
|
||||
return new Builder(credentialsProvider);
|
||||
public static Builder builder(SMCredentialsProvider credentialsProvider, SMController smController) {
|
||||
return new Builder(credentialsProvider, smController);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final SMCredentialsProvider credentialsProvider;
|
||||
private final SMController smController;
|
||||
|
||||
private Path rootPath;
|
||||
private Path userProjectsPath;
|
||||
private Path sharedProjectsPath;
|
||||
|
||||
private Builder(SMCredentialsProvider credentialsProvider) {
|
||||
private Builder(SMCredentialsProvider credentialsProvider, SMController smController) {
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.smController = smController;
|
||||
this.rootPath = RMUtils.getRootPath();
|
||||
this.userProjectsPath = RMUtils.getUserProjectsPath();
|
||||
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
|
||||
@@ -584,7 +642,7 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
public LocalResourceController build() {
|
||||
return new LocalResourceController(credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath);
|
||||
return new LocalResourceController(credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-28
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.service.DBWSessionHandler;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
@@ -109,13 +110,13 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
|
||||
private final Map<String, Function<Object, Object>> attributeDisposers = new HashMap<>();
|
||||
|
||||
// Map of auth tokens. Key is authentication provdier
|
||||
// Map of auth tokens. Key is authentication provider
|
||||
private final List<WebAuthInfo> authTokens = new ArrayList<>();
|
||||
|
||||
private DBNModel navigatorModel;
|
||||
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
|
||||
private VirtualProjectImpl defaultProject;
|
||||
private final List<DBPProject> sessionProjects = new ArrayList<>();
|
||||
private final List<VirtualProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private final SessionContextImpl sessionAuthContext;
|
||||
private final WebApplication application;
|
||||
private final Map<String, DBWSessionHandler> sessionHandlers;
|
||||
@@ -295,20 +296,20 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
|
||||
if (!this.sessionProjects.isEmpty()) {
|
||||
for (DBPProject project : sessionProjects) {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (VirtualProjectImpl project : accessibleProjects) {
|
||||
if (project.equals(DBWorkbench.getPlatform().getWorkspace().getActiveProject())) {
|
||||
continue;
|
||||
}
|
||||
((BaseProjectImpl) project).dispose();
|
||||
project.dispose();
|
||||
}
|
||||
this.defaultProject = null;
|
||||
this.sessionProjects.clear();
|
||||
this.accessibleProjects.clear();
|
||||
}
|
||||
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), this.sessionProjects);
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), this.accessibleProjects);
|
||||
this.navigatorModel.setModelAuthContext(sessionAuthContext);
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
@@ -325,19 +326,14 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
private void loadProjects() {
|
||||
WebUser user = userContext.getUser();
|
||||
try {
|
||||
RMController controller = application.getResourceController(this);
|
||||
RMController controller = application.getResourceController(this, getSecurityController());
|
||||
RMProject[] rmProjects = controller.listAccessibleProjects();
|
||||
for (RMProject project : rmProjects) {
|
||||
VirtualProjectImpl sessionProject = application.createProjectImpl(project, getSessionAuthContext(), this);
|
||||
if (!project.isShared() || (user == null && project.getType().equals(RMProject.Type.GLOBAL))) {
|
||||
this.defaultProject = sessionProject;
|
||||
}
|
||||
DBPDataSourceRegistry dataSourceRegistry = sessionProject.getDataSourceRegistry();
|
||||
((DataSourceRegistry) dataSourceRegistry).setAuthCredentialsProvider(this);
|
||||
addSessionProject(sessionProject);
|
||||
if (user == null && sessionProject.equals(defaultProject)) {
|
||||
sessionProject.setInMemory(true);
|
||||
}
|
||||
createVirtualProject(project);
|
||||
}
|
||||
if (user == null) {
|
||||
createVirtualProject(RMUtils.createAnonymousProject());
|
||||
this.defaultProject.setInMemory(true);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
@@ -345,11 +341,21 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
}
|
||||
}
|
||||
|
||||
private void createVirtualProject(RMProject project) {
|
||||
VirtualProjectImpl sessionProject = application.createProjectImpl(project, getSessionAuthContext(), this);
|
||||
DBPDataSourceRegistry dataSourceRegistry = sessionProject.getDataSourceRegistry();
|
||||
((DataSourceRegistry) dataSourceRegistry).setAuthCredentialsProvider(this);
|
||||
addSessionProject(sessionProject);
|
||||
if (!project.isShared()) {
|
||||
this.defaultProject = sessionProject;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshConnections() {
|
||||
|
||||
// Add all provided datasources to the session
|
||||
List<WebConnectionInfo> connList = new ArrayList<>();
|
||||
for (DBPProject project : sessionProjects) {
|
||||
for (DBPProject project : accessibleProjects) {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
|
||||
for (DBPDataSourceContainer ds : registry.getDataSources()) {
|
||||
@@ -930,11 +936,11 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
return userContext.getActiveUserCredentials();
|
||||
}
|
||||
|
||||
public DBPProject getProjectById(@Nullable String projectId) {
|
||||
public VirtualProjectImpl getProjectById(@Nullable String projectId) {
|
||||
if (projectId == null) {
|
||||
return defaultProject;
|
||||
}
|
||||
for (DBPProject project : sessionProjects) {
|
||||
for (VirtualProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
}
|
||||
@@ -942,13 +948,13 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<DBPProject> getSessionProjects() {
|
||||
return sessionProjects;
|
||||
public List<VirtualProjectImpl> getAccessibleProjects() {
|
||||
return accessibleProjects;
|
||||
}
|
||||
|
||||
public void addSessionProject(DBPProject project) {
|
||||
synchronized (sessionProjects) {
|
||||
sessionProjects.add(project);
|
||||
public void addSessionProject(VirtualProjectImpl project) {
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
@@ -956,8 +962,8 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
|
||||
}
|
||||
|
||||
public void deleteSessionProject(DBPProject project) {
|
||||
synchronized (sessionProjects) {
|
||||
sessionProjects.remove(project);
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
|
||||
+3
-3
@@ -77,11 +77,11 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
if (isNonAnonymousUserAuthorized && isSessionChanged && !Objects.equals(getUserId(), authPermissions.getUserId())) {
|
||||
throw new DBCException("Another user is already logged in");
|
||||
}
|
||||
this.smCredentials = new SMCredentials(smAuthInfo.getSmAuthToken(), authPermissions.getUserId());
|
||||
this.smCredentials = new SMCredentials(smAuthInfo.getSmAuthToken(), authPermissions.getUserId(), authPermissions.getPermissions());
|
||||
this.userPermissions = authPermissions.getPermissions();
|
||||
this.securityController = application.getSecurityController(this);
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
this.rmController = application.getResourceController(this);
|
||||
this.rmController = application.getResourceController(this, this.securityController);
|
||||
if (isSessionChanged) {
|
||||
this.smSessionId = smAuthInfo.getAuthPermissions().getSessionId();
|
||||
setUser(authPermissions.getUserId() == null ? null : new WebUser(securityController.getUserById(authPermissions.getUserId())));
|
||||
@@ -98,7 +98,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.user = null;
|
||||
this.securityController = application.getSecurityController(this);
|
||||
this.adminSecurityController = null;
|
||||
this.rmController = application.getResourceController(this);
|
||||
this.rmController = application.getResourceController(this, this.securityController);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.cloudbeaver;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(value = {ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WebObjectId {
|
||||
}
|
||||
+12
-11
@@ -14,18 +14,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.rm;
|
||||
|
||||
package io.cloudbeaver;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* RM constants
|
||||
* Object association annotation
|
||||
*/
|
||||
public interface RMConstants {
|
||||
|
||||
String PERMISSION_PROJECT_VIEW = "project-view";
|
||||
String PERMISSION_PROJECT_EDIT = "project-edit";
|
||||
String PERMISSION_PROJECT_ADMIN = "project-admin";
|
||||
|
||||
// RM admin can create/delete projects. It also can assign project permissions.
|
||||
String PERMISSION_RM_ADMIN = "rm-admin";
|
||||
|
||||
@Target(value = {ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WebProjectAction {
|
||||
String[] requireProjectPermissions() default {};
|
||||
}
|
||||
+42
-4
@@ -29,6 +29,7 @@ import io.cloudbeaver.server.graphql.GraphQLEndpoint;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -38,10 +39,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -153,6 +151,10 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
if (webAction != null) {
|
||||
checkActionPermissions(method, webAction);
|
||||
}
|
||||
WebProjectAction projectAction = method.getAnnotation(WebProjectAction.class);
|
||||
if(projectAction != null) {
|
||||
checkObjectActionPermissions(method, projectAction, args);
|
||||
}
|
||||
beforeWebActionCall(webAction, method, args);
|
||||
try {
|
||||
return method.invoke(impl, args);
|
||||
@@ -173,6 +175,42 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
|
||||
}
|
||||
}
|
||||
|
||||
private void checkObjectActionPermissions(Method method, WebProjectAction objectAction, Object[] args) throws DBException {
|
||||
WebSession webSession = findWebSession(env);
|
||||
|
||||
String[] requireProjectPermissions = objectAction.requireProjectPermissions();
|
||||
if (requireProjectPermissions.length > 0) {
|
||||
int objectIdArgumentIndex = -1;
|
||||
for (int i = 0; i < method.getParameters().length; i++) {
|
||||
Parameter parameter = method.getParameters()[i];
|
||||
if (parameter.isAnnotationPresent(WebObjectId.class)) {
|
||||
if (String.class != parameter.getAnnotatedType().getType()) {
|
||||
throw new DBWebExceptionAccessDenied("Invalid object id type");
|
||||
}
|
||||
objectIdArgumentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (objectIdArgumentIndex < 0) {
|
||||
throw new DBWebExceptionAccessDenied("Project id argument not found");
|
||||
}
|
||||
|
||||
String projectId = args[objectIdArgumentIndex] == null ? null : String.valueOf(args[objectIdArgumentIndex]);
|
||||
VirtualProjectImpl project = webSession.getProjectById(projectId);
|
||||
if(project == null) {
|
||||
throw new DBException("Project not found:" + projectId);
|
||||
}
|
||||
RMProject rmProject = project.getRmProject();
|
||||
|
||||
for (String reqProjectPermission : requireProjectPermissions) {
|
||||
if (!rmProject.getProjectPermissions().contains(reqProjectPermission)) {
|
||||
throw new DBWebExceptionAccessDenied("Access denied");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkServicePermissions(Method method, WebActionSet actionSet) throws DBWebException {
|
||||
String[] features = actionSet.requireFeatures();
|
||||
if (features.length > 0) {
|
||||
|
||||
+13
-10
@@ -18,12 +18,15 @@ package io.cloudbeaver.service.core;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.WebObjectId;
|
||||
import io.cloudbeaver.WebProjectAction;
|
||||
import io.cloudbeaver.model.*;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -105,35 +108,35 @@ public interface DBWServiceCore extends DBWService {
|
||||
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
|
||||
@Nullable Boolean saveCredentials) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
|
||||
WebConnectionInfo createConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@NotNull WebConnectionConfig connectionConfig) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
|
||||
WebConnectionInfo updateConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@NotNull WebConnectionConfig connectionConfig) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
|
||||
boolean deleteConnection(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@NotNull String connectionId) throws DBWebException;
|
||||
|
||||
@WebAction
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
|
||||
WebConnectionInfo createConnectionFromTemplate(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@NotNull String templateId,
|
||||
@Nullable String connectionName) throws DBWebException;
|
||||
|
||||
@WebAction()
|
||||
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
|
||||
WebConnectionInfo copyConnectionFromNode(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@Nullable @WebObjectId String projectId,
|
||||
@NotNull String nodePath,
|
||||
@NotNull WebConnectionConfig config) throws DBWebException;
|
||||
|
||||
|
||||
+1
-1
@@ -567,7 +567,7 @@ public class WebServiceCore implements DBWServiceCore {
|
||||
// Projects
|
||||
@Override
|
||||
public List<WebProjectInfo> getProjects(@NotNull WebSession session) {
|
||||
return session.getSessionProjects().stream()
|
||||
return session.getAccessibleProjects().stream()
|
||||
.map(pr -> new WebProjectInfo(session, pr)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,22 @@
|
||||
<plugin>
|
||||
|
||||
<extension point="io.cloudbeaver.service">
|
||||
<service id="metadata" label="Resource management " description="Resource management services" class="io.cloudbeaver.service.rm.WebServiceBindingRM">
|
||||
<service id="resource.manager" label="Resource management " description="Resource management services" class="io.cloudbeaver.service.rm.WebServiceBindingRM">
|
||||
<permission id="project-resource-view" label="Project Resource View" description="Allow read only access to resources"
|
||||
category="general" scope="project"/>
|
||||
<permission id="project-resource-edit" label="Project Resource Edit" description="Allow write access to resources"
|
||||
category="general" scope="project"/>
|
||||
|
||||
<permission id="project-connection-view" label="Project Connection View" description="Allow view connections in the project"
|
||||
category="general" scope="project"/>
|
||||
<permission id="project-connection-edit" label="Project Connections Edit"
|
||||
description="Allow create/edit connections in the project" category="general" scope="project"/>
|
||||
|
||||
<!-- <permission id="project-admin" label="Project Admin" description="Allow manage access to the project" category="general"-->
|
||||
<!-- scope="project"/>-->
|
||||
|
||||
<!-- <permission id="rm-admin" label="Resource Manager Admin" description="Allow create/delete projects, manage project access"-->
|
||||
<!-- category="general" scope="subject"/>-->
|
||||
</service>
|
||||
</extension>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Metadata queries
|
||||
enum RMProjectPermission {
|
||||
resource_edit
|
||||
}
|
||||
|
||||
type RMProject {
|
||||
id: String!
|
||||
@@ -8,6 +11,8 @@ type RMProject {
|
||||
|
||||
createTime: DateTime!
|
||||
creator: String!
|
||||
canEditResource: Boolean!
|
||||
canEditConnection: Boolean!
|
||||
}
|
||||
|
||||
type RMResource {
|
||||
|
||||
+25
-8
@@ -16,14 +16,14 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.rm;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebAction;
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.model.WebProjectInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.service.DBWService;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
|
||||
@@ -36,43 +36,60 @@ public interface DBWServiceRM extends DBWService {
|
||||
RMProject[] listProjects(@NotNull WebSession webSession) throws DBWebException;
|
||||
|
||||
@NotNull
|
||||
@WebProjectAction(
|
||||
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_RESOURCE_VIEW
|
||||
)
|
||||
RMResource[] listResources(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@Nullable String folder,
|
||||
@Nullable String nameMask,
|
||||
boolean readProperties,
|
||||
boolean readHistory) throws DBException;
|
||||
|
||||
@WebProjectAction(
|
||||
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_RESOURCE_VIEW
|
||||
)
|
||||
String readResourceAsString(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String resourcePath) throws DBException;
|
||||
|
||||
@WebProjectAction(
|
||||
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_RESOURCE_EDIT
|
||||
)
|
||||
String createResource(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder) throws DBException;
|
||||
|
||||
@WebProjectAction(
|
||||
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_RESOURCE_EDIT
|
||||
)
|
||||
boolean deleteResource(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String resourcePath) throws DBException;
|
||||
|
||||
@NotNull
|
||||
@WebProjectAction(
|
||||
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_RESOURCE_EDIT
|
||||
)
|
||||
String writeResourceStringContent(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull String projectId,
|
||||
@NotNull @WebObjectId String projectId,
|
||||
@NotNull String resourcePath,
|
||||
@NotNull String data) throws DBException;
|
||||
|
||||
@WebAction(requirePermissions = {DBWConstants.PERMISSION_ADMIN, RMConstants.PERMISSION_RM_ADMIN})
|
||||
WebProjectInfo createProject(
|
||||
@NotNull WebSession session,
|
||||
@NotNull String name,
|
||||
@Nullable String description) throws DBWebException;
|
||||
|
||||
@WebProjectAction(requireProjectPermissions = RMConstants.PERMISSION_RM_ADMIN)
|
||||
boolean deleteProject(
|
||||
@NotNull WebSession session,
|
||||
@NotNull String projectId) throws DBWebException;
|
||||
@NotNull @WebObjectId String projectId) throws DBWebException;
|
||||
}
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ public class WebServiceRM implements DBWServiceRM {
|
||||
) throws DBWebException {
|
||||
try {
|
||||
RMProject rmProject = getResourceController(session).createProject(name, description);
|
||||
DBPProject project = session.getApplication().createProjectImpl(rmProject, session.getSessionAuthContext(), session);
|
||||
var project = session.getApplication().createProjectImpl(rmProject, session.getSessionAuthContext(), session);
|
||||
session.addSessionProject(project);
|
||||
return new WebProjectInfo(session, project);
|
||||
} catch (DBException e) {
|
||||
|
||||
Reference in New Issue
Block a user