From 97fc72d3762bfb0bb303c1e03ce33261324778c7 Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Fri, 10 Dec 2021 00:38:47 +0100 Subject: [PATCH] WIP: acm grpc --- .../SinglePermissionSubject.java | 210 ++++++++++++++++++ .../server/accessControl/UserSources.java | 8 +- .../AccessControlManagementServiceImpl.java | 21 +- .../caosdb/server/grpc/AuthInterceptor.java | 46 +++- .../grpc/EntityTransactionServiceImpl.java | 3 + .../grpc/FileTransmissionServiceImpl.java | 3 + .../server/grpc/GeneralInfoServiceImpl.java | 122 +++++++--- src/main/java/org/caosdb/server/jobs/Job.java | 7 - .../jobs/core/CheckDatatypePresent.java | 9 +- .../server/jobs/core/CheckParValid.java | 21 +- .../server/jobs/core/CheckPropValid.java | 11 +- .../server/jobs/core/CheckRefidValid.java | 11 +- .../AbstractCaosDBServerResource.java | 1 + .../caosdb/server/resource/RolesResource.java | 2 - .../transaction/RetrieveRoleTransaction.java | 13 +- 15 files changed, 400 insertions(+), 88 deletions(-) create mode 100644 src/main/java/org/caosdb/server/accessControl/SinglePermissionSubject.java diff --git a/src/main/java/org/caosdb/server/accessControl/SinglePermissionSubject.java b/src/main/java/org/caosdb/server/accessControl/SinglePermissionSubject.java new file mode 100644 index 00000000..2c3a3479 --- /dev/null +++ b/src/main/java/org/caosdb/server/accessControl/SinglePermissionSubject.java @@ -0,0 +1,210 @@ +package org.caosdb.server.accessControl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.authz.permission.WildcardPermission; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.ExecutionException; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.Subject; + +public class SinglePermissionSubject implements Subject { + + private WildcardPermission permission; + + public SinglePermissionSubject(String permission) { + this.permission = new WildcardPermission(permission); + } + + @Override + public Object getPrincipal() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public PrincipalCollection getPrincipals() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean isPermitted(String permission) { + return this.permission.implies(new WildcardPermission(permission)); + } + + @Override + public boolean isPermitted(Permission permission) { + return this.permission.implies(permission); + } + + @Override + public boolean[] isPermitted(String... permissions) { + boolean[] result = new boolean[permissions.length]; + for (int i = 0; i < result.length; i++) { + result[i] = this.permission.implies(new WildcardPermission(permissions[i])); + } + return result; + } + + @Override + public boolean[] isPermitted(List<Permission> permissions) { + boolean[] result = new boolean[permissions.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = this.permission.implies(permissions.get(i)); + } + return result; + } + + @Override + public boolean isPermittedAll(String... permissions) { + boolean result = true; + for (int i = 0; i < permissions.length; i++) { + result &= this.permission.implies(new WildcardPermission(permissions[i])); + } + return result; + } + + @Override + public boolean isPermittedAll(Collection<Permission> permissions) { + Boolean result = true; + Iterator<Permission> iterator = permissions.iterator(); + while (iterator.hasNext()) { + result &= this.permission.implies(iterator.next()); + } + return result; + } + + @Override + public void checkPermission(String permission) throws AuthorizationException { + if (!isPermitted(permission)) { + throw new AuthenticationException("Not permitted: " + permission); + } + } + + @Override + public void checkPermission(Permission permission) throws AuthorizationException { + if (!isPermitted(permission)) { + throw new AuthenticationException("Not permitted: " + permission.toString()); + } + } + + @Override + public void checkPermissions(String... permissions) throws AuthorizationException { + if (!isPermittedAll(permissions)) { + throw new AuthenticationException("Not permitted: " + permissions.toString()); + } + } + + @Override + public void checkPermissions(Collection<Permission> permissions) throws AuthorizationException { + if (!isPermittedAll(permissions)) { + throw new AuthenticationException("Not permitted: " + permissions.toString()); + } + } + + @Override + public boolean hasRole(String roleIdentifier) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean[] hasRoles(List<String> roleIdentifiers) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean hasAllRoles(Collection<String> roleIdentifiers) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void checkRole(String roleIdentifier) throws AuthorizationException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void checkRoles(String... roleIdentifiers) throws AuthorizationException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void login(AuthenticationToken token) throws AuthenticationException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean isAuthenticated() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean isRemembered() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public Session getSession() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public Session getSession(boolean create) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void logout() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public <V> V execute(Callable<V> callable) throws ExecutionException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void execute(Runnable runnable) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public <V> Callable<V> associateWith(Callable<V> callable) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public Runnable associateWith(Runnable runnable) { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public void runAs(PrincipalCollection principals) + throws NullPointerException, IllegalStateException { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public boolean isRunAs() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public PrincipalCollection getPreviousPrincipals() { + throw new UnsupportedOperationException("This method should never be called"); + } + + @Override + public PrincipalCollection releaseRunAs() { + throw new UnsupportedOperationException("This method should never be called"); + } +} diff --git a/src/main/java/org/caosdb/server/accessControl/UserSources.java b/src/main/java/org/caosdb/server/accessControl/UserSources.java index bbbb1bcb..760c3e90 100644 --- a/src/main/java/org/caosdb/server/accessControl/UserSources.java +++ b/src/main/java/org/caosdb/server/accessControl/UserSources.java @@ -26,6 +26,7 @@ package org.caosdb.server.accessControl; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; @@ -169,6 +170,10 @@ public class UserSources extends HashMap<String, UserSource> { * @return A set of user roles. */ public static Set<String> resolveRoles(String realm, final String username) { + if (AnonymousAuthenticationToken.PRINCIPAL.getRealm().equals(realm) + && AnonymousAuthenticationToken.PRINCIPAL.getUsername().equals(username)) { + return Collections.singleton(Role.ANONYMOUS_ROLE.toString()); + } if (realm == null) { realm = guessRealm(username); } @@ -237,7 +242,8 @@ public class UserSources extends HashMap<String, UserSource> { } public static boolean isRoleExisting(final String role) { - final RetrieveRoleTransaction t = new RetrieveRoleTransaction(role); + final RetrieveRoleTransaction t = + new RetrieveRoleTransaction(role, new SinglePermissionSubject("ACM:*:RETRIEVE:*")); try { t.execute(); return true; diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java index 18dd29a2..71225820 100644 --- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java @@ -430,6 +430,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void listUsers( ListUsersRequest request, StreamObserver<ListUsersResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final ListUsersResponse response = listUsersTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -443,6 +444,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void listRoles( ListRolesRequest request, StreamObserver<ListRolesResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final ListRolesResponse response = listRolesTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -456,6 +458,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void createSingleRole( CreateSingleRoleRequest request, StreamObserver<CreateSingleRoleResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final CreateSingleRoleResponse response = createSingleRoleTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -470,6 +473,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS RetrieveSingleRoleRequest request, StreamObserver<RetrieveSingleRoleResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final RetrieveSingleRoleResponse response = retrieveSingleRoleTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -483,6 +487,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void createSingleUser( CreateSingleUserRequest request, StreamObserver<CreateSingleUserResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final CreateSingleUserResponse response = createSingleUserTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -496,6 +501,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void updateSingleUser( UpdateSingleUserRequest request, StreamObserver<UpdateSingleUserResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final UpdateSingleUserResponse response = updateSingleUserTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -510,6 +516,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS RetrieveSingleUserRequest request, StreamObserver<RetrieveSingleUserResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final RetrieveSingleUserResponse response = retrieveSingleUserTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -523,6 +530,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void deleteSingleUser( DeleteSingleUserRequest request, StreamObserver<DeleteSingleUserResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final DeleteSingleUserResponse response = deleteSingleUserTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -536,6 +544,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void deleteSingleRole( DeleteSingleRoleRequest request, StreamObserver<DeleteSingleRoleResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final DeleteSingleRoleResponse response = deleteSingleRoleTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -549,6 +558,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS public void updateSingleRole( UpdateSingleRoleRequest request, StreamObserver<UpdateSingleRoleResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final UpdateSingleRoleResponse response = updateSingleRoleTransaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -563,6 +573,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS ListKnownPermissionsRequest request, StreamObserver<ListKnownPermissionsResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final ListKnownPermissionsResponse response = listKnownPermissions(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -573,6 +584,10 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS } public static void handleException(StreamObserver<?> responseObserver, Exception e) { + String description = e.getMessage(); + if (description == null || description.isBlank()) { + description = "Unknown Error. Please Report!"; + } if (e instanceof UnauthorizedException) { Subject subject = SecurityUtils.getSubject(); if (AuthenticationUtils.isAnonymous(subject)) { @@ -582,17 +597,17 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS } else { responseObserver.onError( new StatusException( - Status.PERMISSION_DENIED.withCause(e).withDescription(e.getMessage()))); + Status.PERMISSION_DENIED.withCause(e).withDescription(description))); 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))); + new StatusException(Status.NOT_FOUND.withDescription(description).withCause(e))); return; } e.printStackTrace(); responseObserver.onError( - new StatusException(Status.UNKNOWN.withDescription(e.getMessage()).withCause(e))); + new StatusException(Status.UNKNOWN.withDescription(description).withCause(e))); } } diff --git a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java index b58769d4..5d06f05c 100644 --- a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java +++ b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java @@ -39,6 +39,7 @@ import java.util.regex.Pattern; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; import org.caosdb.server.CaosDBServer; import org.caosdb.server.ServerProperties; import org.caosdb.server.accessControl.AnonymousAuthenticationToken; @@ -72,6 +73,13 @@ public class AuthInterceptor implements ServerInterceptor { public static final Predicate<String> SESSION_TOKEN_COOKIE_PREFIX_PREDICATE = SESSION_TOKEN_COOKIE_PREFIX_PATTERN.asPredicate(); + @SuppressWarnings("unused") + public static Subject bindSubject() { + Subject subject = (Subject) SUBJECT_KEY.get(); + ThreadContext.bind(subject); + return subject; + } + public final Metadata expiredSessionMetadata() { Metadata metadata = new Metadata(); metadata.put(CookieSetter.SET_COOKIE, CookieSetter.EXPIRED_SESSION_COOKIE); @@ -102,7 +110,7 @@ public class AuthInterceptor implements ServerInterceptor { final String password = split[1]; final RealmUsernamePasswordToken token = new RealmUsernamePasswordToken(UserSources.getDefaultRealm(), username, password); - final org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject(); + final Subject subject = SecurityUtils.getSubject(); subject.login(token); return subject; } @@ -112,6 +120,16 @@ public class AuthInterceptor implements ServerInterceptor { final ServerCall<ReqT, RespT> call, final Metadata headers, final ServerCallHandler<ReqT, RespT> next) { + ThreadContext.remove(); + Subject user = SecurityUtils.getSubject(); + System.out.println( + "interceptCall: " + + Long.toString(Thread.currentThread().getId()) + + " " + + Thread.currentThread().getName() + + " subject: " + + user.toString()); + String authentication = headers.get(AUTHENTICATION_HEADER); if (authentication == null) { authentication = headers.get(AUTHORIZATION_HEADER); @@ -162,7 +180,7 @@ public class AuthInterceptor implements ServerInterceptor { final String tokenString = URLDecodeWithUTF8(sessionTokenCookie.split(";")[0]); final Subject subject = sessionTokenAuth(tokenString); - return updateContext(subject, call, headers, next); + return updateContext(subject, call, headers, next, "sessionToken: " + tokenString); } catch (final AuthenticationException e) { final Status status = Status.UNAUTHENTICATED.withDescription( @@ -192,7 +210,12 @@ public class AuthInterceptor implements ServerInterceptor { final ServerCallHandler<ReqT, RespT> next) { try { final Subject subject = basicAuth(base64); - return updateContext(subject, call, headers, next); + return updateContext( + subject, + call, + headers, + next, + "basic: " + base64 + " thread: " + Thread.currentThread().getName()); } catch (final AuthenticationException e) { final Status status = Status.UNAUTHENTICATED.withDescription( @@ -212,7 +235,7 @@ public class AuthInterceptor implements ServerInterceptor { final Metadata headers, final ServerCallHandler<ReqT, RespT> next) { final Subject subject = anonymous(); - return updateContext(subject, call, headers, next); + return updateContext(subject, call, headers, next, "anonymous"); } /** Login as anonymous. */ @@ -230,10 +253,11 @@ public class AuthInterceptor implements ServerInterceptor { final Subject subject, final ServerCall<ReqT, RespT> call, final Metadata headers, - final ServerCallHandler<ReqT, RespT> next) { + final ServerCallHandler<ReqT, RespT> next, + final String tag) { final Context context = Context.current(); context.withValue(SUBJECT_KEY, subject); - ServerCall<ReqT, RespT> cookieSetter = new CookieSetter<>(call); + ServerCall<ReqT, RespT> cookieSetter = new CookieSetter<>(call, subject, tag); return Contexts.interceptCall(context, cookieSetter, headers, next); } } @@ -245,9 +269,11 @@ final class CookieSetter<ReqT, RespT> + "=expired; Path=/; HttpOnly; SameSite=Strict; Max-Age=0"; public static final Key<String> SET_COOKIE = Key.of("Set-Cookie", Metadata.ASCII_STRING_MARSHALLER); + private Subject subject; - protected CookieSetter(ServerCall<ReqT, RespT> delegate) { + protected CookieSetter(ServerCall<ReqT, RespT> delegate, Subject subject, String tag) { super(delegate); + this.subject = subject; } String getSessionTimeoutSeconds() { @@ -264,10 +290,9 @@ final class CookieSetter<ReqT, RespT> }; private void setSessionCookies(Metadata headers) { - final Subject subject = SecurityUtils.getSubject(); // if authenticated as a normal user: generate and set session cookie. if (subject.isAuthenticated() - && subject.getPrincipal() != AnonymousAuthenticationToken.PRINCIPAL) { + && !AnonymousAuthenticationToken.PRINCIPAL.equals(subject.getPrincipal())) { final SessionToken sessionToken = SessionToken.generate(subject); if (sessionToken != null && sessionToken.isValid()) { @@ -284,6 +309,9 @@ final class CookieSetter<ReqT, RespT> + getSessionTimeoutSeconds()); } } + } else if (AnonymousAuthenticationToken.PRINCIPAL.equals(subject.getPrincipal())) { + // this is anonymous, do nothing + headers.toString(); } else { headers.put(SET_COOKIE, EXPIRED_SESSION_COOKIE); } diff --git a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java index 9b43a04c..8abb2847 100644 --- a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java @@ -410,6 +410,7 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa final MultiTransactionRequest request, final StreamObserver<MultiTransactionResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final MultiTransactionResponse response = transaction(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -424,6 +425,7 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa MultiRetrieveEntityACLRequest request, StreamObserver<MultiRetrieveEntityACLResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final MultiRetrieveEntityACLResponse response = multiRetrieveEntityACL(request); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -438,6 +440,7 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa MultiUpdateEntityACLRequest request, StreamObserver<MultiUpdateEntityACLResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final MultiUpdateEntityACLResponse response = multiUpdateEntityACL(request); responseObserver.onNext(response); responseObserver.onCompleted(); diff --git a/src/main/java/org/caosdb/server/grpc/FileTransmissionServiceImpl.java b/src/main/java/org/caosdb/server/grpc/FileTransmissionServiceImpl.java index 0b0c4b50..1f549b37 100644 --- a/src/main/java/org/caosdb/server/grpc/FileTransmissionServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/FileTransmissionServiceImpl.java @@ -45,6 +45,7 @@ public class FileTransmissionServiceImpl extends FileTransmissionServiceImplBase @Override public void onNext(final FileUploadRequest request) { + AuthInterceptor.bindSubject(); final FileChunk chunk = request.getChunk(); if (chunk.hasFileTransmissionId()) { fileUpload = @@ -104,6 +105,7 @@ public class FileTransmissionServiceImpl extends FileTransmissionServiceImplBase final RegisterFileUploadRequest request, final StreamObserver<RegisterFileUploadResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); final FileTransmission result = fileUploadRegistration.registerFileUpload(); final Builder builder = RegisterFileUploadResponse.newBuilder(); builder.setStatus(result.getRegistrationStatus()); @@ -133,6 +135,7 @@ public class FileTransmissionServiceImpl extends FileTransmissionServiceImplBase final FileDownloadRequest request, final StreamObserver<FileDownloadResponse> responseObserver) { try { + AuthInterceptor.bindSubject(); FileDownloadResponse response = fileDownloadRegistration.downloadNextChunk(request.getFileTransmissionId()); responseObserver.onNext(response); diff --git a/src/main/java/org/caosdb/server/grpc/GeneralInfoServiceImpl.java b/src/main/java/org/caosdb/server/grpc/GeneralInfoServiceImpl.java index de7b6987..295ed15b 100644 --- a/src/main/java/org/caosdb/server/grpc/GeneralInfoServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/GeneralInfoServiceImpl.java @@ -20,10 +20,10 @@ package org.caosdb.server.grpc; +import io.grpc.Context; import io.grpc.stub.StreamObserver; import java.util.Collection; import java.util.LinkedList; -import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.Subject; @@ -50,10 +50,7 @@ import org.caosdb.server.permissions.CaosPermission; */ public class GeneralInfoServiceImpl extends GeneralInfoServiceImplBase { - @Override - public void getVersionInfo( - final GetVersionInfoRequest request, - final StreamObserver<GetVersionInfoResponse> responseObserver) { + private GetVersionInfoResponse getVersionInfo(GetVersionInfoRequest request) { final String version[] = CaosDBServer.getServerProperty(ServerProperties.KEY_PROJECT_VERSION).split("[\\.-]", 4); @@ -71,43 +68,100 @@ public class GeneralInfoServiceImpl extends GeneralInfoServiceImplBase { .setPreRelease(pre_release) .setBuild(build) .build(); - final GetVersionInfoResponse response = - GetVersionInfoResponse.newBuilder().setVersionInfo(versionInfo).build(); - - responseObserver.onNext(response); - responseObserver.onCompleted(); + return GetVersionInfoResponse.newBuilder().setVersionInfo(versionInfo).build(); } @Override - public void getSessionInfo( - GetSessionInfoRequest request, StreamObserver<GetSessionInfoResponse> responseObserver) { - final GetSessionInfoResponse.Builder response = GetSessionInfoResponse.newBuilder(); - - Subject user = SecurityUtils.getSubject(); - Principal principal = (Principal) user.getPrincipal(); + public void getVersionInfo( + final GetVersionInfoRequest request, + final StreamObserver<GetVersionInfoResponse> responseObserver) { - response.setUsername(principal.getUsername()); - response.setRealm(principal.getRealm()); + try { + GetVersionInfoResponse response = getVersionInfo(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); - AuthorizationInfo authorizationInfo = AuthenticationUtils.getAuthorizationInfo(user); - Collection<String> roles = authorizationInfo.getRoles(); - if (roles != null && !roles.isEmpty()) { - response.addAllRoles(roles); + } catch (final Exception e) { + AccessControlManagementServiceImpl.handleException(responseObserver, e); } - Collection<String> permissions = - new LinkedList<String>(authorizationInfo.getStringPermissions()); - for (Permission p : authorizationInfo.getObjectPermissions()) { - if (p instanceof CaosPermission) { - permissions.addAll(((CaosPermission) p).getStringPermissions(user)); - } else { - permissions.add(p.toString()); + } + + private GetSessionInfoResponse getSessionInfo(GetSessionInfoRequest request) { + + Subject subject = AuthInterceptor.bindSubject(); + System.out.println( + "getSessionInfo (in): " + + Long.toString(Thread.currentThread().getId()) + + " " + + Thread.currentThread().getName() + + " subject: " + + subject.toString()); + GetSessionInfoResponse.Builder response = GetSessionInfoResponse.newBuilder(); + + Principal principal = (Principal) subject.getPrincipal(); + + try { + response.setUsername(principal.getUsername()); + response.setRealm(principal.getRealm()); + + AuthorizationInfo authorizationInfo = AuthenticationUtils.getAuthorizationInfo(subject); + Collection<String> roles = authorizationInfo.getRoles(); + if (roles != null && !roles.isEmpty()) { + response.addAllRoles(roles); } + + Collection<String> permissions = new LinkedList<>(); + + Collection<String> stringPermissions = authorizationInfo.getStringPermissions(); + if (stringPermissions != null && !stringPermissions.isEmpty()) { + permissions.addAll(stringPermissions); + } + + for (Permission p : authorizationInfo.getObjectPermissions()) { + if (p instanceof CaosPermission) { + permissions.addAll(((CaosPermission) p).getStringPermissions(subject)); + } else { + permissions.add(p.toString()); + } + } + + if (permissions != null && !permissions.isEmpty()) { + response.addAllPermissions(permissions); + } + + System.out.println( + "getSessionInfo (out): " + + Long.toString(Thread.currentThread().getId()) + + " " + + Thread.currentThread().getName() + + " subject: " + + subject.toString()); + return response.build(); + } catch (NullPointerException e) { + final Context context = Context.current(); + + System.out.println( + "getSessionInfo (exc): " + + Long.toString(Thread.currentThread().getId()) + + " " + + Thread.currentThread().getName() + + " subject: " + + subject.toString()); + throw e; } - if (permissions != null && !permissions.isEmpty()) { - response.addAllPermissions(permissions); - } + } + + @Override + public void getSessionInfo( + GetSessionInfoRequest request, StreamObserver<GetSessionInfoResponse> responseObserver) { - responseObserver.onNext(response.build()); - responseObserver.onCompleted(); + try { + final GetSessionInfoResponse response = getSessionInfo(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + } catch (final Exception e) { + AccessControlManagementServiceImpl.handleException(responseObserver, e); + } } } diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index 9bca9eb2..675686aa 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -27,8 +27,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Set; -import org.apache.shiro.authz.AuthorizationException; -import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.Subject; import org.caosdb.server.CaosDBException; import org.caosdb.server.database.BackendTransaction; @@ -300,11 +298,6 @@ public abstract class Job { return entity; } - protected final void checkPermission(final EntityInterface entity, final Permission permission) - throws AuthorizationException { - entity.checkPermission(permission); - } - /** * Create a Job object with the given parameters. * diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java index 9ea7d4b3..dd87d529 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java @@ -23,6 +23,7 @@ package org.caosdb.server.jobs.core; import java.util.List; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.datatype.AbstractCollectionDatatype; @@ -97,13 +98,15 @@ public final class CheckDatatypePresent extends EntityJob { // finally, no data type throw ServerMessages.PROPERTY_HAS_NO_DATATYPE; } - } catch (final Message m) { if (m == ServerMessages.ENTITY_DOES_NOT_EXIST) { getEntity().addError(ServerMessages.UNKNOWN_DATATYPE); } else { getEntity().addError(m); } + } catch (AuthorizationException exc) { + getEntity().addError(ServerMessages.AUTHORIZATION_ERROR); + getEntity().addInfo(exc.getMessage()); } catch (final EntityDoesNotExistException exc) { getEntity().addError(ServerMessages.UNKNOWN_DATATYPE); } catch (final EntityWasNotUniqueException exc) { @@ -152,8 +155,8 @@ public final class CheckDatatypePresent extends EntityJob { } } - private void assertAllowedToUse(final EntityInterface datatype) throws Message { - checkPermission(datatype, EntityPermission.USE_AS_DATA_TYPE); + private void assertAllowedToUse(final EntityInterface datatype) { + datatype.checkPermission(EntityPermission.USE_AS_DATA_TYPE); } private void checkIfOverride() throws Message { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java index 4e6b097e..d3b28bd5 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java @@ -23,6 +23,7 @@ package org.caosdb.server.jobs.core; import com.google.common.base.Objects; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.entity.Affiliation; @@ -118,13 +119,16 @@ public class CheckParValid extends EntityJob { } } - addError(parent, ServerMessages.ENTITY_DOES_NOT_EXIST); + parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final Message m) { - addError(parent, m); + parent.addError(m); + } catch (AuthorizationException e) { + parent.addError(ServerMessages.AUTHORIZATION_ERROR); + parent.addInfo(e.getMessage()); } catch (final EntityDoesNotExistException exc) { - addError(parent, ServerMessages.ENTITY_DOES_NOT_EXIST); + parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final EntityWasNotUniqueException exc) { - addError(parent, ServerMessages.ENTITY_NAME_DUPLICATES); + parent.addError(ServerMessages.ENTITY_NAME_DUPLICATES); } } } @@ -191,12 +195,7 @@ public class CheckParValid extends EntityJob { throw ServerMessages.AFFILIATION_ERROR; } - private void assertAllowedToUse(final EntityInterface entity) throws Message { - checkPermission(entity, EntityPermission.USE_AS_PARENT); - } - - private void addError(final EntityInterface parent, final Message m) { - parent.addError(m); - parent.setEntityStatus(EntityStatus.UNQUALIFIED); + private void assertAllowedToUse(final EntityInterface entity) { + entity.checkPermission(EntityPermission.USE_AS_PARENT); } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java index 112692c8..eeea52b8 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java @@ -138,17 +138,10 @@ public class CheckPropValid extends EntityJob { // process names appendJob(ProcessNameProperties.class); - // final ProcessNameProperties processNameProperties = new - // ProcessNameProperties(); - // processNameProperties.init(getMode(), getEntity(), getContainer(), - // getTransaction()); - // getTransaction().getSchedule().add(processNameProperties); - // getTransaction().getSchedule().runJob(processNameProperties); - } - private void assertAllowedToUse(final EntityInterface property) throws Message { - checkPermission(property, EntityPermission.USE_AS_PROPERTY); + private void assertAllowedToUse(final EntityInterface property) { + property.checkPermission(EntityPermission.USE_AS_PROPERTY); } private static void deriveOverrideStatus(final Property child, final EntityInterface parent) { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java index f19424b1..645f87d0 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java @@ -24,6 +24,7 @@ */ package org.caosdb.server.jobs.core; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.datatype.CollectionValue; @@ -71,13 +72,13 @@ public class CheckRefidValid extends EntityJob implements Observer { } } catch (final Message m) { getEntity().addError(m); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); + } catch (AuthorizationException exc) { + getEntity().addError(ServerMessages.AUTHORIZATION_ERROR); + getEntity().addInfo(exc.getMessage()); } catch (final EntityDoesNotExistException e) { getEntity().addError(ServerMessages.REFERENCED_ENTITY_DOES_NOT_EXIST); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } catch (final EntityWasNotUniqueException e) { getEntity().addError(ServerMessages.REFERENCE_NAME_DUPLICATES); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } } @@ -139,8 +140,8 @@ public class CheckRefidValid extends EntityJob implements Observer { } } - private void assertAllowedToUse(final EntityInterface referencedEntity) throws Message { - checkPermission(referencedEntity, EntityPermission.USE_AS_REFERENCE); + private void assertAllowedToUse(final EntityInterface referencedEntity) { + referencedEntity.checkPermission(EntityPermission.USE_AS_REFERENCE); } @Override diff --git a/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java index 0fa5b4b4..44ebfa22 100644 --- a/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java +++ b/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java @@ -389,6 +389,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { public Representation handleThrowable(final Throwable t) { try { getRequest().getAttributes().put("THROWN", t); + t.printStackTrace(); throw t; } catch (final AuthenticationException e) { return error(ServerMessages.UNAUTHENTICATED, Status.CLIENT_ERROR_UNAUTHORIZED); diff --git a/src/main/java/org/caosdb/server/resource/RolesResource.java b/src/main/java/org/caosdb/server/resource/RolesResource.java index a89a7319..ad1544da 100644 --- a/src/main/java/org/caosdb/server/resource/RolesResource.java +++ b/src/main/java/org/caosdb/server/resource/RolesResource.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import org.caosdb.server.CaosDBException; -import org.caosdb.server.accessControl.ACMPermissions; import org.caosdb.server.accessControl.Role; import org.caosdb.server.database.backend.implementation.MySQL.ConnectionException; import org.caosdb.server.entity.Message; @@ -55,7 +54,6 @@ public class RolesResource extends AbstractCaosDBServerResource { if (getRequestedItems().length > 0) { final String name = getRequestedItems()[0]; if (name != null) { - getUser().checkPermission(ACMPermissions.PERMISSION_RETRIEVE_ROLE_DESCRIPTION(name)); final RetrieveRoleTransaction t = new RetrieveRoleTransaction(name); try { t.execute(); diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveRoleTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveRoleTransaction.java index 1047e1f6..8e92a347 100644 --- a/src/main/java/org/caosdb/server/transaction/RetrieveRoleTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/RetrieveRoleTransaction.java @@ -36,15 +36,20 @@ public class RetrieveRoleTransaction extends AccessControlTransaction { private final String name; private Role role; + private Subject transactor; - public RetrieveRoleTransaction(final String name) { + public RetrieveRoleTransaction(final String name, Subject transactor) { + this.transactor = transactor; this.name = name; } + public RetrieveRoleTransaction(final String name) { + this(name, SecurityUtils.getSubject()); + } + @Override protected void transaction() throws Exception { - Subject currentUser = SecurityUtils.getSubject(); - if (!currentUser.isPermitted(ACMPermissions.PERMISSION_RETRIEVE_ROLE_DESCRIPTION(this.name))) { + if (!transactor.isPermitted(ACMPermissions.PERMISSION_RETRIEVE_ROLE_DESCRIPTION(this.name))) { throw new AuthorizationException("You are not permitted to retrieve this role"); } this.role = execute(new RetrieveRole(this.name), getAccess()).getRole(); @@ -55,7 +60,7 @@ public class RetrieveRoleTransaction extends AccessControlTransaction { Iterator<ProtoUser> iterator = this.role.users.iterator(); while (iterator.hasNext()) { ProtoUser user = iterator.next(); - if (!currentUser.isPermitted( + if (!transactor.isPermitted( ACMPermissions.PERMISSION_RETRIEVE_USER_ROLES(user.realm, user.name))) { iterator.remove(); } -- GitLab