CB-2406 rm project permissions api

This commit is contained in:
Aleksandr Skoblikov
2022-09-12 20:03:31 +03:00
parent 090241d0b4
commit 04efef80ae
20 changed files with 237 additions and 47 deletions
@@ -29,6 +29,7 @@ Export-Package: io.cloudbeaver,
io.cloudbeaver.registry,
io.cloudbeaver.server,
io.cloudbeaver.service,
io.cloudbeaver.service.security,
io.cloudbeaver.service.sql,
io.cloudbeaver.utils
Automatic-Module-Name: io.cloudbeaver.model
@@ -346,7 +346,7 @@ public class WebConnectionInfo {
@Property
public boolean isCanEdit() {
return hasProjectPermission(RMProjectPermission.CONNECTIONS_EDIT);
return hasProjectPermission(RMProjectPermission.DATA_SOURCES_EDIT);
}
@Property
@@ -18,6 +18,7 @@ package io.cloudbeaver.model;
import io.cloudbeaver.VirtualProjectImpl;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.security.SMUtils;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
@@ -55,16 +56,27 @@ public class WebProjectInfo {
}
@Property
public boolean isCanCreateConnections() {
return hasRmPermission(RMProjectPermission.CONNECTIONS_EDIT);
public boolean isCanEditDataSources() {
return hasDatasourcePermission(RMProjectPermission.DATA_SOURCES_EDIT);
}
@Property
public boolean isCanViewConnections() {
return hasRmPermission(RMProjectPermission.CONNECTIONS_VIEW);
public boolean isCanViewDataSources() {
return hasDatasourcePermission(RMProjectPermission.DATA_SOURCES_VIEW);
}
private boolean hasRmPermission(RMProjectPermission permission) {
return project.getRmProject().getProjectPermissions().contains(permission.getPermissionId());
@Property
public boolean isCanEditResources() {
return hasDatasourcePermission(RMProjectPermission.RESOURCE_EDIT);
}
@Property
public boolean isCanViewResources() {
return hasDatasourcePermission(RMProjectPermission.RESOURCE_VIEW);
}
private boolean hasDatasourcePermission(RMProjectPermission permission) {
return SMUtils.isRMAdmin(session)
|| project.getRmProject().getProjectPermissions().contains(permission.getPermissionId());
}
}
@@ -49,7 +49,7 @@ public class RMUtils {
RMProject project = new RMProject("anonymous");
project.setId("anonymous");
project.setType(RMProject.Type.USER);
project.setProjectPermissions(RMProjectPermission.CONNECTIONS_EDIT.getAllPermissions());
project.setProjectPermissions(RMProjectPermission.DATA_SOURCES_EDIT.getAllPermissions());
return project;
}
}
@@ -173,8 +173,8 @@ public class LocalResourceController implements RMController {
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);
? Set.of(RMProjectPermission.RESOURCE_EDIT, RMProjectPermission.DATA_SOURCES_EDIT)
: Set.of(RMProjectPermission.RESOURCE_VIEW, RMProjectPermission.DATA_SOURCES_VIEW);
case SHARED:
if(projectId == null) {
throw new DBException("Project id required");
@@ -185,7 +185,7 @@ public class LocalResourceController implements RMController {
.map(RMProjectPermission::fromPermission)
.collect(Collectors.toSet());
case USER:
return Set.of(RMProjectPermission.RESOURCE_EDIT, RMProjectPermission.CONNECTIONS_EDIT);
return Set.of(RMProjectPermission.RESOURCE_EDIT, RMProjectPermission.DATA_SOURCES_EDIT);
default:
throw new DBException("Unknown project type:" + projectType);
}
@@ -745,10 +745,9 @@ public class LocalResourceController implements RMController {
return new RMProjectName(prefix, name);
}
public static boolean isShared(String projectId) {
public static boolean isGlobalProject(String projectId) {
RMProjectName rmProjectName = parseProjectName(projectId);
return RMProject.Type.SHARED.getPrefix().equals(rmProjectName.getPrefix()) ||
RMProject.Type.GLOBAL.getPrefix().equals(rmProjectName.getPrefix());
return RMProject.Type.GLOBAL.getPrefix().equals(rmProjectName.getPrefix());
}
}
@@ -335,7 +335,7 @@ public class WebSession extends AbstractSessionPersistent implements SMSession,
RMProject[] rmProjects = controller.listAccessibleProjects();
for (RMProject project : rmProjects) {
VirtualProjectImpl virtualProject = createVirtualProject(project);
if (!virtualProject.getRmProject().getProjectPermissions().contains(RMProjectPermission.CONNECTIONS_EDIT.getPermissionId())) {
if (!virtualProject.getRmProject().getProjectPermissions().contains(RMProjectPermission.DATA_SOURCES_EDIT.getPermissionId())) {
// Projects for which user don't have edit permission can't be saved. So mark the as in memory
virtualProject.setInMemory(true);
}
@@ -0,0 +1,36 @@
package io.cloudbeaver.service.security;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebPermissionDescriptor;
import io.cloudbeaver.registry.WebServiceDescriptor;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.service.admin.AdminPermissionInfo;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.model.rm.RMConstants;
import org.jkiss.dbeaver.model.security.SMConstants;
import java.util.ArrayList;
import java.util.List;
public class SMUtils {
public static boolean isAdmin(WebSession webSession) {
return webSession.hasPermission(DBWConstants.PERMISSION_ADMIN);
}
public static boolean isRMAdmin(WebSession webSession) {
return isAdmin(webSession) || webSession.hasPermission(RMConstants.PERMISSION_RM_ADMIN);
}
public static List<AdminPermissionInfo> findPermissions(@NotNull String permissionsScope) {
List<AdminPermissionInfo> permissionInfos = new ArrayList<>();
for (WebServiceDescriptor wsd : WebServiceRegistry.getInstance().getWebServices()) {
for (WebPermissionDescriptor pd : wsd.getPermissions()) {
if (permissionsScope.equals(pd.getScope())) {
permissionInfos.add(new AdminPermissionInfo(pd));
}
}
}
return permissionInfos;
}
}
@@ -370,8 +370,10 @@ type ProjectInfo {
id: String!
name: String!
description: String
canCreateConnections: Boolean!
canViewConnections: Boolean!
canEditDataSources: Boolean!
canViewDataSources: Boolean!
canEditResources: Boolean!
canViewResources: Boolean!
}
type LogEntry {
@@ -111,32 +111,32 @@ public interface DBWServiceCore extends DBWService {
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
@Nullable Boolean saveCredentials) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionInfo createConnection(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@NotNull WebConnectionConfig connectionConfig) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionInfo updateConnection(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@NotNull WebConnectionConfig connectionConfig) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
boolean deleteConnection(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@NotNull String connectionId) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionInfo createConnectionFromTemplate(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@NotNull String templateId,
@Nullable String connectionName) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionInfo copyConnectionFromNode(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@@ -162,14 +162,14 @@ public interface DBWServiceCore extends DBWService {
///////////////////////////////////////////
// Folders
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionFolderInfo createConnectionFolder(
@NotNull WebSession session,
@Nullable @WebObjectId String projectId,
@NotNull String parentNodePath,
@NotNull String newName) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
WebConnectionFolderInfo renameConnectionFolder(
@NotNull WebSession session,
@Nullable @WebObjectId
@@ -177,7 +177,7 @@ public interface DBWServiceCore extends DBWService {
@NotNull String folderPath,
@NotNull String newName) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_CONNECTIONS_EDIT})
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATA_SOURCES_EDIT})
boolean deleteConnectionFolder(
@NotNull WebSession session,
@Nullable @WebObjectId String projectId,
@@ -34,7 +34,6 @@ import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.model.struct.DBSObject;
import org.jkiss.dbeaver.model.struct.rdb.DBSProcedure;
import org.jkiss.dbeaver.registry.DataSourceFolder;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import java.util.ArrayList;
@@ -185,7 +184,7 @@ public class WebNavigatorNodeInfo {
return features.toArray(new String[0]);
}
VirtualProjectImpl project = session.getProjectById(node.getOwnerProject().getId());
if (!project.getRmProject().getProjectPermissions().contains(RMProjectPermission.CONNECTIONS_EDIT.getPermissionId())) {
if (!project.getRmProject().getProjectPermissions().contains(RMProjectPermission.DATA_SOURCES_EDIT.getPermissionId())) {
return features.toArray(new String[0]);
}
if (node instanceof DBNLocalFolder || node instanceof DBNResourceManagerResource) {
@@ -442,7 +442,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
private boolean hasNodeEditPermission(DBNNode node, RMProject rmProject) {
var projectPermissions = rmProject.getProjectPermissions();
if (node instanceof DBNDataSource || node instanceof DBNLocalFolder) {
return projectPermissions.contains(RMProjectPermission.CONNECTIONS_EDIT.getPermissionId());
return projectPermissions.contains(RMProjectPermission.DATA_SOURCES_EDIT.getPermissionId());
} else if (node instanceof DBNAbstractResourceManagerNode) {
return projectPermissions.contains(RMProjectPermission.RESOURCE_EDIT.getPermissionId());
}
@@ -11,6 +11,17 @@ type AdminConnectionGrantInfo {
subjectType: AdminSubjectType!
}
type AdminObjectPermissions {
objectId: ID!
permissions: [String!]!
}
type AdminObjectGrantInfo {
subjectId: ID!
subjectType: AdminSubjectType!
objectPermissions: AdminObjectPermissions!
}
type AdminConnectionSearchInfo {
displayName: String!
host: String!
@@ -37,6 +37,7 @@ import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.DBWServiceServerConfigurator;
import io.cloudbeaver.service.admin.*;
import io.cloudbeaver.service.security.SMUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -118,17 +119,9 @@ public class WebServiceAdmin implements DBWServiceAdmin {
@Override
public List<AdminPermissionInfo> listPermissions(@NotNull WebSession webSession) throws DBWebException {
try {
List<AdminPermissionInfo> permissionInfos = new ArrayList<>();
for (WebServiceDescriptor wsd : WebServiceRegistry.getInstance().getWebServices()) {
for (WebPermissionDescriptor pd : wsd.getPermissions()) {
if(SMConstants.SUBJECT_PERMISSION_SCOPE.equals(pd.getScope())) {
permissionInfos.add(new AdminPermissionInfo(pd));
}
}
}
return permissionInfos;
return SMUtils.findPermissions(SMConstants.SUBJECT_PERMISSION_SCOPE);
} catch (Exception e) {
throw new DBWebException("Error reading users", e);
throw new DBWebException("Error reading permissions", e);
}
}
@@ -6,14 +6,15 @@
<extension point="io.cloudbeaver.service">
<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"/>
category="resources" scope="project"/>
<permission id="project-resource-edit" label="Project Resource Edit" description="Allow write access to resources"
category="general" scope="project"/>
category="resources" 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-data-source-view" label="Project Dat Source View" description="Allow view data sources in the project"
category="data-sources" scope="project"/>
<permission id="project-data-source-edit" label="Project Data Source Edit"
description="Allow create/edit data sources in the project"
category="data-sources" scope="project"/>
<!-- <permission id="project-admin" label="Project Admin" description="Allow manage access to the project" category="general"-->
<!-- scope="project"/>-->
@@ -17,6 +17,11 @@ type RMResource {
length: Int!
}
input RMProjectPermissions {
subjectId: String!
permissions: [String!]!
}
extend type Query {
# List accessible projects
@@ -26,7 +31,12 @@ extend type Query {
rmProject(projectId: String!): RMProject!
# List accessible projects
rmListProjectPermissions: AdminPermissionInfo
rmListProjectGrantedPermissions(projectId: String!): [AdminObjectGrantInfo!]!
rmListSubjectProjectsPermissionGrants(subjectId: String!): [AdminObjectGrantInfo!]!
rmListResources(
projectId: String!,
folder: String,
@@ -70,4 +80,7 @@ extend type Mutation {
description: String): RMProject!
rmDeleteProject(projectId: ID!): Boolean!
rmSetProjectPermissions(projectId: String!, permissions: [RMProjectPermissions!]!): Boolean!
}
@@ -17,15 +17,19 @@
package io.cloudbeaver.service.rm;
import io.cloudbeaver.*;
import io.cloudbeaver.model.WebProjectInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWService;
import io.cloudbeaver.service.admin.AdminPermissionInfo;
import io.cloudbeaver.service.rm.model.RMSubjectProjectPermissions;
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;
import org.jkiss.dbeaver.model.security.SMObjectPermissionsGrant;
import java.util.List;
/**
* Web service API
@@ -99,4 +103,30 @@ public interface DBWServiceRM extends DBWService {
boolean deleteProject(
@NotNull WebSession session,
@NotNull @WebObjectId String projectId) throws DBWebException;
@WebAction(requirePermissions = {RMConstants.PERMISSION_RM_ADMIN})
List<AdminPermissionInfo> listProjectPermissions() throws DBWebException;
@WebProjectAction(
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_ADMIN
)
boolean setProjectPermissions(
@NotNull WebSession webSession,
@NotNull @WebObjectId String projectId,
@NotNull RMSubjectProjectPermissions projectPermissions
) throws DBWebException;
@WebProjectAction(
requireProjectPermissions = RMConstants.PERMISSION_PROJECT_ADMIN
)
List<SMObjectPermissionsGrant> listProjectGrantedPermissions(
@NotNull WebSession webSession,
@NotNull @WebObjectId String projectId
) throws DBWebException;
@WebAction(requirePermissions = {RMConstants.PERMISSION_RM_ADMIN})
List<SMObjectPermissionsGrant> listSubjectProjectsPermissionGrants(
@NotNull WebSession webSession,
@NotNull String subjectId
) throws DBWebException;
}
@@ -20,6 +20,7 @@ import io.cloudbeaver.DBWebException;
import io.cloudbeaver.service.DBWBindingContext;
import io.cloudbeaver.service.WebServiceBindingBase;
import io.cloudbeaver.service.rm.impl.WebServiceRM;
import io.cloudbeaver.service.rm.model.RMSubjectProjectPermissions;
import org.jkiss.utils.CommonUtils;
/**
@@ -53,6 +54,16 @@ public class WebServiceBindingRM extends WebServiceBindingBase<DBWServiceRM> {
env -> getService(env).readResourceAsString(getWebSession(env),
env.getArgument("projectId"),
env.getArgument("resourcePath")))
.dataFetcher("rmListProjectPermissions", env -> getService(env).listProjectPermissions())
.dataFetcher("rmSetProjectPermissions", env -> getService(env).setProjectPermissions(
getWebSession(env),
env.getArgument("projectId"),
new RMSubjectProjectPermissions(env.getArgument("permissions"))
))
.dataFetcher("rmListProjectGrantedPermissions", env -> getService(env).listProjectGrantedPermissions(
getWebSession(env),
env.getArgument("subjectId")
))
;
model.getMutationType()
.dataFetcher("rmCreateResource",
@@ -17,9 +17,11 @@
package io.cloudbeaver.service.rm.impl;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.WebProjectInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.admin.AdminPermissionInfo;
import io.cloudbeaver.service.rm.DBWServiceRM;
import io.cloudbeaver.service.rm.model.RMSubjectProjectPermissions;
import io.cloudbeaver.service.security.SMUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -27,8 +29,12 @@ import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMResource;
import org.jkiss.dbeaver.model.security.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Web service implementation
@@ -148,6 +154,60 @@ public class WebServiceRM implements DBWServiceRM {
}
}
@Override
public List<AdminPermissionInfo> listProjectPermissions() throws DBWebException {
try {
return SMUtils.findPermissions(SMConstants.PROJECT_PERMISSION_SCOPE);
} catch (Exception e) {
throw new DBWebException("Error reading project permissions", e);
}
}
@Override
public boolean setProjectPermissions(@NotNull WebSession webSession,
@NotNull String projectId,
@NotNull RMSubjectProjectPermissions projectPermissions
) throws DBWebException {
try {
SMController sm = webSession.getSecurityController();
//TODO one 'setObjectPermissions' call
for (Map.Entry<String, Set<String>> entry : projectPermissions.getSubjectPermissions().entrySet()) {
String subjectId = entry.getKey();
Set<String> permissions = entry.getValue();
sm.setObjectPermissions(
Set.of(projectId),
SMObjects.PROJECT,
Set.of(subjectId),
permissions,
webSession.getUserId()
);
}
return true;
} catch (Exception e) {
throw new DBWebException("Error granting project permissions", e);
}
}
@Override
public List<SMObjectPermissionsGrant> listProjectGrantedPermissions(@NotNull WebSession webSession, @NotNull String projectId) throws DBWebException {
SMController sm = webSession.getSecurityController();
try {
return sm.getObjectPermissionGrants(projectId, SMObjects.PROJECT);
} catch (DBException e) {
throw new DBWebException("Error reading project permission grants", e);
}
}
@Override
public List<SMObjectPermissionsGrant> listSubjectProjectsPermissionGrants(@NotNull WebSession webSession, @NotNull String subjectId) throws DBWebException {
try {
SMAdminController sm = webSession.getAdminSecurityController();
return sm.getSubjectObjectPermissionGrants(subjectId, SMObjects.PROJECT);
} catch (DBException e) {
throw new DBWebException("Error reading project permission grants", e);
}
}
private RMController getResourceController(WebSession webSession) {
return webSession.getRmController();
}
@@ -0,0 +1,22 @@
package io.cloudbeaver.service.rm.model;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import java.util.*;
public class RMSubjectProjectPermissions {
private final Map<String, Set<String>> subjectPermissions = new HashMap<>();
public RMSubjectProjectPermissions(Map<String, Object> params) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
String subjectId = entry.getKey();
Map<String, Object> subjectParams = JSONUtils.getObject(params, subjectId);
Set<String> permissions = new HashSet<>(JSONUtils.getStringList(subjectParams, "permissions"));
this.subjectPermissions.put(subjectId, permissions);
}
}
public Map<String, Set<String>> getSubjectPermissions() {
return subjectPermissions;
}
}