From 5b65100afcb5f0cdf369214899a0b3b6e63aa564 Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Mon, 22 Nov 2021 10:02:04 +0100 Subject: [PATCH] WIP: delete users --- caosdb-proto | 2 +- .../org/caosdb/server/accessControl/Role.java | 4 +- .../implementation/MySQL/MySQLDeleteRole.java | 4 + .../MySQL/MySQLRetrieveRole.java | 49 ++++++-- .../server/database/proto/ProtoUser.java | 6 +- .../org/caosdb/server/entity/Message.java | 5 + .../AccessControlManagementServiceImpl.java | 111 +++++++++++++++--- .../transaction/DeleteRoleTransaction.java | 13 +- .../caosdb/server/utils/ServerMessages.java | 12 ++ 9 files changed, 172 insertions(+), 34 deletions(-) diff --git a/caosdb-proto b/caosdb-proto index 4e0552f3..9c22dfb9 160000 --- a/caosdb-proto +++ b/caosdb-proto @@ -1 +1 @@ -Subproject commit 4e0552f3bd0e40c96ec534f605f38ed7650bcdf1 +Subproject commit 9c22dfb9fbaf32d4072f84e4e04aae891aff5919 diff --git a/src/main/java/org/caosdb/server/accessControl/Role.java b/src/main/java/org/caosdb/server/accessControl/Role.java index 1938d9f1..32ffb447 100644 --- a/src/main/java/org/caosdb/server/accessControl/Role.java +++ b/src/main/java/org/caosdb/server/accessControl/Role.java @@ -24,15 +24,17 @@ package org.caosdb.server.accessControl; import java.io.Serializable; import java.util.LinkedList; +import org.caosdb.server.database.proto.ProtoUser; import org.caosdb.server.permissions.PermissionRule; import org.jdom2.Element; public class Role implements Serializable { - private static final long serialVersionUID = 8968219504349206982L; + private static final long serialVersionUID = -243913823L; public String name = null; public String description = null; public LinkedList<PermissionRule> permission_rules = null; + public LinkedList<ProtoUser> users = null; public Element toElement() { final Element ret = new Element("Role"); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteRole.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteRole.java index 8fd8d35f..006e8d4e 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteRole.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteRole.java @@ -24,9 +24,11 @@ package org.caosdb.server.database.backend.implementation.MySQL; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.SQLIntegrityConstraintViolationException; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.interfaces.DeleteRoleImpl; import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.utils.ServerMessages; public class MySQLDeleteRole extends MySQLTransaction implements DeleteRoleImpl { @@ -42,6 +44,8 @@ public class MySQLDeleteRole extends MySQLTransaction implements DeleteRoleImpl final PreparedStatement stmt = prepareStatement(STMT_DELETE_ROLE); stmt.setString(1, role); stmt.execute(); + } catch (final SQLIntegrityConstraintViolationException e) { + throw new TransactionException(ServerMessages.ROLE_CANNOT_BE_DELETED); } catch (final SQLException e) { throw new TransactionException(e); } catch (final ConnectionException e) { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java index 95dfd2cf..b6229675 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java @@ -31,6 +31,7 @@ import org.caosdb.server.accessControl.Role; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl; import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.database.proto.ProtoUser; import org.caosdb.server.permissions.PermissionRule; import org.eclipse.jetty.util.ajax.JSON; @@ -42,31 +43,53 @@ public class MySQLRetrieveRole extends MySQLTransaction implements RetrieveRoleI public static final String STMT_RETRIEVE_ROLE = "SELECT r.description AS description, p.permissions AS permissions FROM roles AS r LEFT JOIN permissions AS p ON (r.name = p.role) WHERE r.name=?"; + public static final String STMT_RETRIEVE_USERS = + "SELECT u.realm, u.user FROM user_roles AS u WHERE u.role = ?"; @Override public Role retrieve(final String role) throws TransactionException { - try { - final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_ROLE); + Role ret = null; + try (final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_ROLE)) { stmt.setString(1, role); final ResultSet rs = stmt.executeQuery(); - try { - if (rs.next()) { - final Role ret = new Role(); - ret.name = role; - ret.description = rs.getString("description"); - ret.permission_rules = parse(rs.getString("permissions")); - return ret; - } else { - return null; + if (rs.next()) { + ret = new Role(); + ret.name = role; + ret.description = rs.getString("description"); + ret.permission_rules = parse(rs.getString("permissions")); + } else { + return null; + } + } catch (final SQLException e) { + throw new TransactionException(e); + } catch (final ConnectionException e) { + throw new TransactionException(e); + } + + try (final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_USERS)) { + stmt.setString(1, role); + final ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + ret.users = new LinkedList<>(); + + ret.users.add(nextUser(rs)); + while (rs.next()) { + ret.users.add(nextUser(rs)); } - } finally { - rs.close(); } } catch (final SQLException e) { throw new TransactionException(e); } catch (final ConnectionException e) { throw new TransactionException(e); } + return ret; + } + + private ProtoUser nextUser(ResultSet rs) throws SQLException { + ProtoUser nextUser = new ProtoUser(); + nextUser.realm = rs.getString("realm"); + nextUser.name = rs.getString("user"); + return nextUser; } @SuppressWarnings("unchecked") diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoUser.java b/src/main/java/org/caosdb/server/database/proto/ProtoUser.java index cafa32d2..91dfe9aa 100644 --- a/src/main/java/org/caosdb/server/database/proto/ProtoUser.java +++ b/src/main/java/org/caosdb/server/database/proto/ProtoUser.java @@ -23,19 +23,19 @@ package org.caosdb.server.database.proto; import java.io.Serializable; -import java.util.Set; +import java.util.HashSet; import org.caosdb.server.accessControl.UserStatus; public class ProtoUser implements Serializable { public ProtoUser() {} - private static final long serialVersionUID = -2704114543883567439L; + private static final long serialVersionUID = 5381234723597234L; public UserStatus status = null; public String name = null; public String email = null; public Integer entity = null; public String realm = null; - public Set<String> roles = null; + public HashSet<String> roles = null; } diff --git a/src/main/java/org/caosdb/server/entity/Message.java b/src/main/java/org/caosdb/server/entity/Message.java index 4c89e1ee..e206137e 100644 --- a/src/main/java/org/caosdb/server/entity/Message.java +++ b/src/main/java/org/caosdb/server/entity/Message.java @@ -35,6 +35,11 @@ public class Message extends Exception implements Comparable<Message>, ToElement private final String description; private final String body; + @Override + public String getMessage() { + return description; + } + @Deprecated private static final Map<String, String> legacy_codes_mapping = new HashMap<>(); static void init() { diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java index 182d5cb5..dbfa1512 100644 --- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java @@ -1,14 +1,21 @@ package org.caosdb.server.grpc; +import io.grpc.Status; +import io.grpc.StatusException; import io.grpc.stub.StreamObserver; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.UnauthorizedException; +import org.apache.shiro.subject.Subject; import org.caosdb.api.acm.v1alpha1.AccessControlManagementServiceGrpc.AccessControlManagementServiceImplBase; import org.caosdb.api.acm.v1alpha1.CreateSingleRoleRequest; import org.caosdb.api.acm.v1alpha1.CreateSingleRoleResponse; import org.caosdb.api.acm.v1alpha1.CreateSingleUserRequest; import org.caosdb.api.acm.v1alpha1.CreateSingleUserResponse; +import org.caosdb.api.acm.v1alpha1.DeleteSingleRoleRequest; +import org.caosdb.api.acm.v1alpha1.DeleteSingleRoleResponse; import org.caosdb.api.acm.v1alpha1.ListRolesRequest; import org.caosdb.api.acm.v1alpha1.ListRolesResponse; import org.caosdb.api.acm.v1alpha1.ListUsersRequest; @@ -18,16 +25,22 @@ import org.caosdb.api.acm.v1alpha1.RetrieveSingleRoleRequest; import org.caosdb.api.acm.v1alpha1.RetrieveSingleRoleResponse; import org.caosdb.api.acm.v1alpha1.RetrieveSingleUserRequest; import org.caosdb.api.acm.v1alpha1.RetrieveSingleUserResponse; +import org.caosdb.api.acm.v1alpha1.UpdateSingleRoleRequest; +import org.caosdb.api.acm.v1alpha1.UpdateSingleRoleResponse; import org.caosdb.api.acm.v1alpha1.User; import org.caosdb.api.acm.v1alpha1.UserStatus; +import org.caosdb.server.accessControl.AuthenticationUtils; import org.caosdb.server.accessControl.Role; import org.caosdb.server.database.proto.ProtoUser; +import org.caosdb.server.transaction.DeleteRoleTransaction; import org.caosdb.server.transaction.InsertRoleTransaction; import org.caosdb.server.transaction.InsertUserTransaction; import org.caosdb.server.transaction.ListRolesTransaction; import org.caosdb.server.transaction.ListUsersTransaction; import org.caosdb.server.transaction.RetrieveRoleTransaction; import org.caosdb.server.transaction.RetrieveUserTransaction; +import org.caosdb.server.transaction.UpdateRoleTransaction; +import org.caosdb.server.utils.ServerMessages; public class AccessControlManagementServiceImpl extends AccessControlManagementServiceImplBase { @@ -70,16 +83,16 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS private User.Builder convert(ProtoUser user) { User.Builder result = User.newBuilder(); - result.setStatus(convert(user.status)); result.setRealm(user.realm); result.setName(user.name); + if (user.status != null) result.setStatus(convert(user.status)); if (user.email != null) { result.setEmail(user.email); } if (user.entity != null) { result.setEntityId(Integer.toString(user.entity)); } - if (user.roles != null) { + if (user.roles != null && !user.roles.isEmpty()) { result.addAllRoles(user.roles); } return result; @@ -167,7 +180,31 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS RetrieveRoleTransaction transaction = new RetrieveRoleTransaction(request.getName()); transaction.execute(); - return RetrieveSingleRoleResponse.newBuilder().setRole(convert(transaction.getRole())).build(); + Role role = transaction.getRole(); + RetrieveSingleRoleResponse.Builder builder = + RetrieveSingleRoleResponse.newBuilder().setRole(convert(transaction.getRole())); + if (role.users != null && !role.users.isEmpty()) + role.users.forEach( + (u) -> { + builder.addUsers(convert(u)); + }); + return builder.build(); + } + + private DeleteSingleRoleResponse deleteSingleRoleTransaction(DeleteSingleRoleRequest request) + throws Exception { + DeleteRoleTransaction transaction = new DeleteRoleTransaction(request.getName()); + transaction.execute(); + + return DeleteSingleRoleResponse.newBuilder().build(); + } + + private UpdateSingleRoleResponse updateSingleRoleTransaction(UpdateSingleRoleRequest request) + throws Exception { + Role role = convert(request.getRole()); + UpdateRoleTransaction transaction = new UpdateRoleTransaction(role); + transaction.execute(); + return UpdateSingleRoleResponse.newBuilder().build(); } ////////////////// ... for users @@ -208,8 +245,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); } } @@ -222,8 +258,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); } } @@ -236,8 +271,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); } } @@ -251,8 +285,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); } } @@ -265,8 +298,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); } } @@ -280,8 +312,57 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onCompleted(); } catch (final Exception e) { - e.printStackTrace(); - responseObserver.onError(e); + handleException(responseObserver, e); + } + } + + @Override + public void deleteSingleRole( + DeleteSingleRoleRequest request, StreamObserver<DeleteSingleRoleResponse> responseObserver) { + try { + final DeleteSingleRoleResponse response = deleteSingleRoleTransaction(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + } catch (final Exception e) { + handleException(responseObserver, e); + } + } + + @Override + public void updateSingleRole( + UpdateSingleRoleRequest request, StreamObserver<UpdateSingleRoleResponse> responseObserver) { + try { + final UpdateSingleRoleResponse response = updateSingleRoleTransaction(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + } catch (final Exception e) { + handleException(responseObserver, e); + } + } + + private void handleException(StreamObserver<?> responseObserver, Exception e) { + if (e instanceof UnauthorizedException) { + Subject subject = SecurityUtils.getSubject(); + if (AuthenticationUtils.isAnonymous(subject)) { + responseObserver.onError( + new StatusException(Status.UNAUTHENTICATED.withDescription("Please login!"))); + return; + } else { + responseObserver.onError( + new StatusException( + Status.PERMISSION_DENIED.withCause(e).withDescription(e.getMessage()))); + return; + } + } else if (e == ServerMessages.ROLE_DOES_NOT_EXIST + || e == ServerMessages.ACCOUNT_DOES_NOT_EXIST) { + responseObserver.onError( + new StatusException(Status.NOT_FOUND.withDescription(e.getMessage()).withCause(e))); + return; } + e.printStackTrace(); + responseObserver.onError( + new StatusException(Status.UNKNOWN.withDescription(e.getMessage()).withCause(e))); } } diff --git a/src/main/java/org/caosdb/server/transaction/DeleteRoleTransaction.java b/src/main/java/org/caosdb/server/transaction/DeleteRoleTransaction.java index ad9a9925..23c31261 100644 --- a/src/main/java/org/caosdb/server/transaction/DeleteRoleTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/DeleteRoleTransaction.java @@ -27,6 +27,8 @@ import org.caosdb.server.accessControl.ACMPermissions; import org.caosdb.server.database.backend.transaction.DeleteRole; import org.caosdb.server.database.backend.transaction.RetrieveRole; import org.caosdb.server.database.backend.transaction.SetPermissionRules; +import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.entity.Message; import org.caosdb.server.utils.ServerMessages; public class DeleteRoleTransaction extends AccessControlTransaction { @@ -41,10 +43,19 @@ public class DeleteRoleTransaction extends AccessControlTransaction { protected void transaction() throws Exception { SecurityUtils.getSubject().checkPermission(ACMPermissions.PERMISSION_DELETE_ROLE(this.name)); + if (this.name == "administration" || this.name == "anonymous") { + throw ServerMessages.SPECIAL_ROLE_CANNOT_BE_DELETED; + } if (execute(new RetrieveRole(this.name), getAccess()).getRole() == null) { throw ServerMessages.ROLE_DOES_NOT_EXIST; } execute(new SetPermissionRules(this.name, null), getAccess()); - execute(new DeleteRole(this.name), getAccess()); + try { + execute(new DeleteRole(this.name), getAccess()); + } catch (TransactionException e) { + if (e.getCause() == ServerMessages.ROLE_CANNOT_BE_DELETED) { + throw (Message) e.getCause(); + } + } } } diff --git a/src/main/java/org/caosdb/server/utils/ServerMessages.java b/src/main/java/org/caosdb/server/utils/ServerMessages.java index 4197f2cf..cc844fee 100644 --- a/src/main/java/org/caosdb/server/utils/ServerMessages.java +++ b/src/main/java/org/caosdb/server/utils/ServerMessages.java @@ -267,6 +267,18 @@ public class ServerMessages { MessageCode.MESSAGE_CODE_UNKNOWN, "Role name is already in use. Choose a different name."); + public static final Message ROLE_CANNOT_BE_DELETED = + new Message( + MessageType.Error, + MessageCode.MESSAGE_CODE_UNKNOWN, + "Role cannot be deleted because there are still users with this role."); + + public static final Message SPECIAL_ROLE_CANNOT_BE_DELETED = + new Message( + MessageType.Error, + MessageCode.MESSAGE_CODE_UNKNOWN, + "This special role cannot be deleted. Ever."); + public static final Message QUERY_EXCEPTION = new Message( MessageType.Error, -- GitLab