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:
Alexander Skoblikov
2022-07-27 13:48:22 +03:00
committed by GitHub
co-authored by yagudin10
parent 905eace2e8
commit 15599bc29b
17 changed files with 288 additions and 111 deletions
@@ -79,4 +79,8 @@ public class VirtualProjectImpl extends BaseProjectImpl {
public void ensureOpen() {
}
@NotNull
public RMProject getRmProject() {
return this.project;
}
}
@@ -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) {
@@ -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();
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
@@ -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 {
}
@@ -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 {};
}
@@ -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) {
@@ -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;
@@ -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 {
@@ -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;
}
@@ -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) {