diff --git a/pom.xml b/pom.xml index cac2861d87d145dfd6b6660da15b11135d4f4467..691c82fb89d727680741201641f18c47e2fb52d6 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.caosdb</groupId> <artifactId>caosdb-server</artifactId> - <version>0.5.1-GRPC0.1.0</version> + <version>0.5.1-GRPC0.1.1</version> <packaging>jar</packaging> <name>CaosDB Server</name> <scm> @@ -56,11 +56,6 @@ </repository> </repositories> <dependencies> - <dependency> - <groupId>io.grpcweb</groupId> - <artifactId>grpcweb-java</artifactId> - <version>0.1-SNAPSHOT</version> - </dependency> <dependency> <groupId>de.timmfitschen</groupId> <artifactId>easy-units</artifactId> diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java index f42c849c08e982b835df2629c07d70e1df882a8e..1e627ebafa2d273fafbc1762c70877df3f8fc2b5 100644 --- a/src/main/java/org/caosdb/server/database/BackendTransaction.java +++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java @@ -45,6 +45,7 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLInsertSparse import org.caosdb.server.database.backend.implementation.MySQL.MySQLInsertTransactionHistory; import org.caosdb.server.database.backend.implementation.MySQL.MySQLIsSubType; import org.caosdb.server.database.backend.implementation.MySQL.MySQLListRoles; +import org.caosdb.server.database.backend.implementation.MySQL.MySQLListUsers; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRegisterSubDomain; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAll; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAllUncheckedFiles; @@ -101,6 +102,7 @@ import org.caosdb.server.database.backend.interfaces.InsertSparseEntityImpl; import org.caosdb.server.database.backend.interfaces.InsertTransactionHistoryImpl; import org.caosdb.server.database.backend.interfaces.IsSubTypeImpl; import org.caosdb.server.database.backend.interfaces.ListRolesImpl; +import org.caosdb.server.database.backend.interfaces.ListUsersImpl; import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl; import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl; import org.caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl; @@ -207,6 +209,7 @@ public abstract class BackendTransaction implements Undoable { setImpl(InsertEntityDatatypeImpl.class, MySQLInsertEntityDatatype.class); setImpl(RetrieveVersionHistoryImpl.class, MySQLRetrieveVersionHistory.class); setImpl(SetFileChecksumImpl.class, MySQLSetFileChecksum.class); + setImpl(ListUsersImpl.class, MySQLListUsers.class); } } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java new file mode 100644 index 0000000000000000000000000000000000000000..625942d3d23d0c4488cdefec76c70ca29a685278 --- /dev/null +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java @@ -0,0 +1,52 @@ +package org.caosdb.server.database.backend.implementation.MySQL; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import org.caosdb.server.accessControl.UserStatus; +import org.caosdb.server.database.access.Access; +import org.caosdb.server.database.backend.interfaces.ListUsersImpl; +import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.database.proto.ProtoUser; + +public class MySQLListUsers extends MySQLTransaction implements ListUsersImpl { + + public MySQLListUsers(Access access) { + super(access); + } + + public static final String STMT_LIST_USERS = + "SELECT status, realm, name, email, entity FROM user_info"; + + @Override + public List<ProtoUser> execute() { + List<ProtoUser> users = new LinkedList<>(); + try { + final PreparedStatement stmt = prepareStatement(STMT_LIST_USERS); + final ResultSet rs = stmt.executeQuery(); + try { + while (rs.next()) { + final ProtoUser user = new ProtoUser(); + user.name = rs.getString("name"); + user.realm = rs.getString("realm"); + user.email = rs.getString("email"); + user.entity = rs.getInt("entity"); + if (user.entity == 0) { + user.entity = null; + } + user.status = UserStatus.valueOf(rs.getString("status")); + users.add(user); + } + } finally { + rs.close(); + } + } catch (final SQLException e) { + throw new TransactionException(e); + } catch (final ConnectionException e) { + throw new TransactionException(e); + } + return users; + } +} diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..771fb913cd30dfad4c5d9662dda5c54f65901823 --- /dev/null +++ b/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java @@ -0,0 +1,9 @@ +package org.caosdb.server.database.backend.interfaces; + +import java.util.List; +import org.caosdb.server.database.proto.ProtoUser; + +public interface ListUsersImpl extends BackendTransactionImpl { + + List<ProtoUser> execute(); +} diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java b/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java new file mode 100644 index 0000000000000000000000000000000000000000..6b3640732ccf884342280178676551432f18bd39 --- /dev/null +++ b/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java @@ -0,0 +1,21 @@ +package org.caosdb.server.database.backend.transaction; + +import java.util.List; +import org.caosdb.server.database.BackendTransaction; +import org.caosdb.server.database.backend.interfaces.ListUsersImpl; +import org.caosdb.server.database.proto.ProtoUser; + +public class ListUsers extends BackendTransaction { + + private List<ProtoUser> users; + + @Override + protected void execute() { + ListUsersImpl t = getImplementation(ListUsersImpl.class); + users = t.execute(); + } + + public List<ProtoUser> getUsers() { + return users; + } +} diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java index 5987b28936e4ea6b4444fad6fe73fe7bd2a71edc..258cb8ada62ffdb5dd01beae1ceff8639fb6a988 100644 --- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java @@ -5,16 +5,65 @@ import java.util.List; 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.ListRolesRequest; import org.caosdb.api.acm.v1alpha1.ListRolesResponse; +import org.caosdb.api.acm.v1alpha1.ListUsersRequest; +import org.caosdb.api.acm.v1alpha1.ListUsersResponse; +import org.caosdb.api.acm.v1alpha1.User; +import org.caosdb.api.acm.v1alpha1.UserStatus; import org.caosdb.server.accessControl.Role; +import org.caosdb.server.database.proto.ProtoUser; import org.caosdb.server.transaction.InsertRoleTransaction; +import org.caosdb.server.transaction.InsertUserTransaction; import org.caosdb.server.transaction.ListRolesTransaction; +import org.caosdb.server.transaction.ListUsersTransaction; public class AccessControlManagementServiceImpl extends AccessControlManagementServiceImplBase { /////////////////////////////////// CONVERTERS + private ProtoUser convert(User user) { + ProtoUser result = new ProtoUser(); + result.realm = user.getRealm(); + result.name = user.getName(); + result.email = user.getEmail(); + result.status = + (user.getStatus() == UserStatus.USER_STATUS_ACTIVE + ? org.caosdb.server.accessControl.UserStatus.ACTIVE + : org.caosdb.server.accessControl.UserStatus.INACTIVE); + return result; + } + + private ListUsersResponse convertUsers(List<ProtoUser> users) { + ListUsersResponse.Builder response = ListUsersResponse.newBuilder(); + users.forEach( + user -> { + response.addUsers(convert(user)); + }); + + return response.build(); + } + + 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.email != null) { + result.setEmail(user.email); + } + if (user.entity != null) { + result.setEntityId(Integer.toString(user.entity)); + } + return result; + } + + private UserStatus convert(org.caosdb.server.accessControl.UserStatus status) { + return UserStatus.valueOf(status.toString()); + } + private Role convert(org.caosdb.api.acm.v1alpha1.Role role) { Role result = new Role(); @@ -62,8 +111,41 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS return CreateSingleRoleResponse.newBuilder().build(); } + ////////////////// ... for users + + private ListUsersResponse listUsersTransaction(ListUsersRequest request) throws Exception { + ListUsersTransaction transaction = new ListUsersTransaction(); + transaction.execute(); + List<ProtoUser> users = transaction.getUsers(); + return convertUsers(users); + } + + private CreateSingleUserResponse createSingleUserTransaction(CreateSingleUserRequest request) + throws Exception { + ProtoUser user = convert(request.getUser()); + InsertUserTransaction transaction = + new InsertUserTransaction(user, request.getUser().getPassword()); + transaction.execute(); + + return CreateSingleUserResponse.newBuilder().build(); + } + ///////////////////////////////////// RPC Methods (API) + @Override + public void listUsers( + ListUsersRequest request, StreamObserver<ListUsersResponse> responseObserver) { + try { + final ListUsersResponse response = listUsersTransaction(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + } catch (final Exception e) { + e.printStackTrace(); + responseObserver.onError(e); + } + } + @Override public void listRoles( ListRolesRequest request, StreamObserver<ListRolesResponse> responseObserver) { @@ -91,4 +173,18 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS responseObserver.onError(e); } } + + @Override + public void createSingleUser( + CreateSingleUserRequest request, StreamObserver<CreateSingleUserResponse> responseObserver) { + try { + final CreateSingleUserResponse response = createSingleUserTransaction(request); + responseObserver.onNext(response); + responseObserver.onCompleted(); + + } catch (final Exception e) { + e.printStackTrace(); + responseObserver.onError(e); + } + } } diff --git a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java index 74adce832c7a76af6b8dd25dfcf0e33aaad32225..5078d55d8bb8bae6e38368f9effb74b2d9d6fede 100644 --- a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java +++ b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java @@ -1,7 +1,10 @@ package org.caosdb.server.grpc; +import static org.caosdb.server.utils.Utils.URLDecodeWithUTF8; + import io.grpc.Context; import io.grpc.Contexts; +import io.grpc.ForwardingServerCall; import io.grpc.Metadata; import io.grpc.Metadata.Key; import io.grpc.ServerCall; @@ -10,14 +13,21 @@ import io.grpc.ServerCallHandler; import io.grpc.ServerInterceptor; import io.grpc.Status; import java.util.Base64; +import java.util.function.Predicate; +import java.util.regex.Pattern; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.caosdb.server.CaosDBServer; import org.caosdb.server.ServerProperties; import org.caosdb.server.accessControl.AnonymousAuthenticationToken; +import org.caosdb.server.accessControl.AuthenticationUtils; import org.caosdb.server.accessControl.RealmUsernamePasswordToken; +import org.caosdb.server.accessControl.SelfValidatingAuthenticationToken; +import org.caosdb.server.accessControl.SessionToken; import org.caosdb.server.accessControl.UserSources; +import org.caosdb.server.utils.Utils; +import org.restlet.data.CookieSetting; /** * ServerInterceptor for Authentication. If the authentication succeeds or if the caller is @@ -26,14 +36,20 @@ import org.caosdb.server.accessControl.UserSources; * * @author Timm Fitschen <t.fitschen@indiscale.com> */ -class AuthInterceptor implements ServerInterceptor { +public class AuthInterceptor implements ServerInterceptor { - private static final Key<String> AUTHENTICATION_HEADER = + public static final Key<String> AUTHENTICATION_HEADER = Key.of("authentication", Metadata.ASCII_STRING_MARSHALLER); - private static final Key<String> AUTHORIZATION_HEADER = + public static final Key<String> AUTHORIZATION_HEADER = Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER); - private static final Context.Key<Subject> SUBJECT_KEY = Context.key("subject"); - private static final String BASIC_SCHEME_PREFIX = "Basic "; + public static final Key<String> COOKIE_HEADER = + Key.of("Cookie", Metadata.ASCII_STRING_MARSHALLER); + public static final Context.Key<Subject> SUBJECT_KEY = Context.key("subject"); + public static final String BASIC_SCHEME_PREFIX = "Basic "; + public static final Pattern SESSION_TOKEN_COOKIE_PREFIX_PATTERN = + Pattern.compile("^\\s*" + AuthenticationUtils.SESSION_TOKEN_COOKIE + "\\s*=\\s*"); + public static final Predicate<String> SESSION_TOKEN_COOKIE_PREFIX_PREDICATE = + SESSION_TOKEN_COOKIE_PREFIX_PATTERN.asPredicate(); /** * A no-op listener. This class is used for failed authentications. We couldn't return a null * instead because the documentation of the {@link ServerInterceptor} explicitely forbids it. @@ -73,6 +89,9 @@ class AuthInterceptor implements ServerInterceptor { if (authentication == null) { authentication = headers.get(AUTHORIZATION_HEADER); } + if (authentication == null) { + authentication = headers.get(COOKIE_HEADER); + } Status status = Status.UNKNOWN.withDescription( "An unknown error occured during authentication. Please report a bug."); @@ -82,15 +101,47 @@ class AuthInterceptor implements ServerInterceptor { status = Status.UNAUTHENTICATED.withDescription("Please login."); } else if (authentication.startsWith(BASIC_SCHEME_PREFIX)) { return basicAuth(authentication.substring(BASIC_SCHEME_PREFIX.length()), call, headers, next); + } else if (SESSION_TOKEN_COOKIE_PREFIX_PREDICATE.test(authentication)) { + return sessionTokenAuth( + SESSION_TOKEN_COOKIE_PREFIX_PATTERN.split(authentication, 2)[1], call, headers, next); } else { - status = - Status.UNAUTHENTICATED.withDescription( - "Unsupported authentication scheme: " + authentication.split(" ", 2)[0]); + status = Status.UNAUTHENTICATED.withDescription("Unsupported authentication scheme."); } call.close(status, new Metadata()); return new NoOpListener<ReqT>(); } + /** + * Login via AuthenticationToken and add the resulting subject to the call context. + * + * @see #updateContext(Subject, ServerCall, Metadata, ServerCallHandler) for more information. + */ + private <ReqT, RespT> Listener<ReqT> sessionTokenAuth( + String sessionTokenCookie, + ServerCall<ReqT, RespT> call, + Metadata headers, + ServerCallHandler<ReqT, RespT> next) { + try { + final String tokenString = URLDecodeWithUTF8(sessionTokenCookie.split(";")[0]); + + final Subject subject = sessionTokenAuth(tokenString); + return updateContext(subject, call, headers, next); + } catch (final AuthenticationException e) { + final Status status = + Status.UNAUTHENTICATED.withDescription( + "Authentication failed. SessionToken was invalid."); + call.close(status, new Metadata()); + return new NoOpListener<ReqT>(); + } + } + + /** Login via AuthenticationToken and return the logged-in subject. */ + private Subject sessionTokenAuth(String tokenString) { + Subject subject = SecurityUtils.getSubject(); + subject.login(SelfValidatingAuthenticationToken.parse(tokenString)); + return subject; + } + /** * Login via username and password with the basic authentication scheme and add the resulting * subject to the call context. @@ -145,6 +196,54 @@ class AuthInterceptor implements ServerInterceptor { final ServerCallHandler<ReqT, RespT> next) { final Context context = Context.current(); context.withValue(SUBJECT_KEY, subject); - return Contexts.interceptCall(Context.current(), call, headers, next); + ServerCall<ReqT, RespT> cookieSetter = new CookieSetter<>(call); + return Contexts.interceptCall(context, cookieSetter, headers, next); + } +} + +final class CookieSetter<ReqT, RespT> + extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> { + private static final Key<String> SET_COOKIE = + Key.of("Set-Cookie", Metadata.ASCII_STRING_MARSHALLER); + + protected CookieSetter(ServerCall<ReqT, RespT> delegate) { + super(delegate); + } + + String getSessionTimeoutSeconds() { + int ms = + Integer.parseInt(CaosDBServer.getServerProperty(ServerProperties.KEY_SESSION_TIMEOUT_MS)); + int seconds = (int) Math.floor(ms / 1000); + return Integer.toString(seconds); + } + + @Override + public void sendHeaders(Metadata headers) { + setSessionCookies(headers); + super.sendHeaders(headers); + }; + + 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) { + final SessionToken sessionToken = SessionToken.generate(subject); + if (sessionToken != null && sessionToken.isValid()) { + + final CookieSetting sessionTokenCookie = + AuthenticationUtils.createSessionTokenCookie(sessionToken); + if (sessionTokenCookie != null) { + // TODO add "Secure;" to cookie setting + headers.put( + SET_COOKIE, + AuthenticationUtils.SESSION_TOKEN_COOKIE + + "=" + + Utils.URLEncodeWithUTF8(sessionToken.toString()) + + "; Path=/; HttpOnly; SameSite=Strict; Max-Age=" + + getSessionTimeoutSeconds()); + } + } + } } } diff --git a/src/main/java/org/caosdb/server/grpc/GRPCServer.java b/src/main/java/org/caosdb/server/grpc/GRPCServer.java index cff13a0e683affc3cb9b641f43532ecc27e20c02..05d4288e961c4e5ce84ba65465ad358ff31d7330 100644 --- a/src/main/java/org/caosdb/server/grpc/GRPCServer.java +++ b/src/main/java/org/caosdb/server/grpc/GRPCServer.java @@ -24,9 +24,6 @@ import io.grpc.ServerInterceptors; import io.grpc.ServerServiceDefinition; import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NettyServerBuilder; -import io.grpcweb.GrpcPortNumRelay; -import io.grpcweb.JettyWebserverForGrpcwebTraffic; -import io.grpcweb.ServiceToClassMapping; import io.netty.handler.ssl.ApplicationProtocolConfig; import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; @@ -45,10 +42,6 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; import javax.net.ssl.KeyManagerFactory; -import org.caosdb.api.acm.v1alpha1.AccessControlManagementServiceGrpc; -import org.caosdb.api.entity.v1alpha1.EntityTransactionServiceGrpc; -import org.caosdb.api.entity.v1alpha1.FileTransmissionServiceGrpc; -import org.caosdb.api.info.v1alpha1.GeneralInfoServiceGrpc; import org.caosdb.server.CaosDBServer; import org.caosdb.server.ServerProperties; import org.slf4j.Logger; @@ -122,29 +115,20 @@ public class GRPCServer { private List<ServerServiceDefinition> getEnabledServices() { final List<ServerServiceDefinition> services = new LinkedList<>(); - // Add mapping from the service name to the service class (for the grpc-web proxy) - ServiceToClassMapping.put( - AccessControlManagementServiceGrpc.SERVICE_NAME, AccessControlManagementServiceGrpc.class); final AccessControlManagementServiceImpl accessControlManagementService = new AccessControlManagementServiceImpl(); - services.add(ServerInterceptors.intercept(accessControlManagementService, authInterceptor)); + services.add( + ServerInterceptors.intercept( + accessControlManagementService, loggingInterceptor, authInterceptor)); - // Add mapping from the service name to the service class (for the grpc-web proxy) - ServiceToClassMapping.put(GeneralInfoServiceGrpc.SERVICE_NAME, GeneralInfoServiceGrpc.class); final GeneralInfoServiceImpl generalInfoService = new GeneralInfoServiceImpl(); services.add( ServerInterceptors.intercept(generalInfoService, loggingInterceptor, authInterceptor)); - // Add mapping from the service name to the service class (for the grpc-web proxy) - ServiceToClassMapping.put( - FileTransmissionServiceGrpc.SERVICE_NAME, FileTransmissionServiceGrpc.class); final FileTransmissionServiceImpl fileTransmissionService = new FileTransmissionServiceImpl(); services.add( ServerInterceptors.intercept(fileTransmissionService, loggingInterceptor, authInterceptor)); - // Add mapping from the service name to the service class (for the grpc-web proxy) - ServiceToClassMapping.put( - EntityTransactionServiceGrpc.SERVICE_NAME, EntityTransactionServiceGrpc.class); final EntityTransactionServiceImpl entityTransactionService = new EntityTransactionServiceImpl(fileTransmissionService); services.add( @@ -220,12 +204,6 @@ public class GRPCServer { server.start(); logger.info("Started GRPC (HTTP) on port {}", port_http); - // Start the grpc-web proxy on grpc-web-port. - (new JettyWebserverForGrpcwebTraffic(9443)).start(); - - // grpc-web proxy needs to know the grpc-port# so it could connect to the grpc service. - GrpcPortNumRelay.setGrpcPortNum(port_http); - } else if (!started) { logger.warn( "No GRPC Server has been started. Please configure {} or {} to do so.", diff --git a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java index b91b1aa6b31911822ed9df4c1eaffa87374ede82..d63cd60fc94e09bb0230551bf9fd432b9bee82bf 100644 --- a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java @@ -36,7 +36,7 @@ import org.jdom2.Element; public class InsertUserTransaction extends AccessControlTransaction { - ProtoUser user = new ProtoUser(); + private final ProtoUser user; private final String password; public InsertUserTransaction( @@ -45,11 +45,16 @@ public class InsertUserTransaction extends AccessControlTransaction { final String email, final UserStatus status, final Integer entity) { + this(new ProtoUser(), password); this.user.realm = UserSources.getInternalRealm().getName(); this.user.name = username; this.user.email = email; this.user.status = status; this.user.entity = entity; + } + + public InsertUserTransaction(ProtoUser user, String password) { + this.user = user; this.password = password; } diff --git a/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java new file mode 100644 index 0000000000000000000000000000000000000000..cfbd68d3412eea162bc96582bddc8193a4781c31 --- /dev/null +++ b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java @@ -0,0 +1,19 @@ +package org.caosdb.server.transaction; + +import java.util.List; +import org.caosdb.server.database.backend.transaction.ListUsers; +import org.caosdb.server.database.proto.ProtoUser; + +public class ListUsersTransaction extends AccessControlTransaction { + + private List<ProtoUser> users = null; + + @Override + protected void transaction() throws Exception { + users = execute(new ListUsers(), getAccess()).getUsers(); + } + + public List<ProtoUser> getUsers() { + return users; + } +} diff --git a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java index ca19603230bf6aa989c495fbadabd0989c2a9790..6ec17c6812f62c4ea5205b8efea488f804d8d0db 100644 --- a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java +++ b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java @@ -24,6 +24,7 @@ package org.caosdb.server.authentication; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; @@ -53,6 +54,7 @@ import org.caosdb.server.database.backend.interfaces.RetrievePasswordValidatorIm import org.caosdb.server.database.backend.interfaces.RetrievePermissionRulesImpl; import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl; import org.caosdb.server.database.backend.interfaces.RetrieveUserImpl; +import org.caosdb.server.grpc.AuthInterceptor; import org.caosdb.server.resource.TestScriptingResource.RetrievePasswordValidator; import org.caosdb.server.resource.TestScriptingResource.RetrievePermissionRules; import org.caosdb.server.resource.TestScriptingResource.RetrieveRole; @@ -448,4 +450,10 @@ public class AuthTokenTest { assertEquals(9223372036854775000L, config.getExpiresAfter()); assertEquals(922337203685477000L, config.getReplayTimeout()); } + + @Test + public void testSessionTokenCookiePattern() { + String cookie = "SessionToken=%5B%22S%22%22%5D"; + assertTrue(AuthInterceptor.SESSION_TOKEN_COOKIE_PREFIX_PATTERN.matcher(cookie).find()); + } }