diff --git a/CHANGELOG.md b/CHANGELOG.md index 70505c9e5719ceba2d40ba2b6c1b197e8eefe603..f66c4ca07a1a300bc6d180954bb6fa82fca3c8f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,9 +48,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #14 - Handle files on file system without File entity: Those entries are returned without ID but with a notice now. +* #11 - pam_authentication leaks the password to unprivileged processes on the + same machine. + ### Security (in case of vulnerabilities) - TLS is by default restricted to v1.2 and v1.3 now. +* #11 - pam_authentication leaks the password to unprivileged processes on the + same machine. ## [0.1.0] - 2018-10-09 diff --git a/misc/pam_authentication/makefile b/misc/pam_authentication/makefile index 47830dc9451abcd3baddb7cce9478e7998287afd..0cd1b7d250358a116a32a45ba18875d5f72d887a 100644 --- a/misc/pam_authentication/makefile +++ b/misc/pam_authentication/makefile @@ -4,6 +4,8 @@ # # Copyright (C) 2018 Research Group Biomedical Physics, # Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# Copyright (C) 2020 Indiscale GmbH <info@indiscale.com> +# Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -23,5 +25,5 @@ # Makefile for pam_authentication.c main: pam_authentication.c - [ -d ./bin ] || mkdir ./bin + mkdir -p ./bin gcc -o ./bin/pam_authentication pam_authentication.c -lpam -lpam_misc diff --git a/misc/pam_authentication/pam_authentication.c b/misc/pam_authentication/pam_authentication.c index 5d71ec2973fa59d18d10ffa4918a5236707b85af..4abef5bddb2a6c11ecefdcc8a1bf9179835bca84 100644 --- a/misc/pam_authentication/pam_authentication.c +++ b/misc/pam_authentication/pam_authentication.c @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,17 +25,31 @@ // Pam Authentication // A. Schlemmer, 07/2018 -#include <security/pam_appl.h> -#include <security/pam_misc.h> +/* +Note: This program needs sufficient right to authenticate against anyone but +oneself. This can be done for example by changing the effective group id: + +``` +$ ls -l bin +-rwxrwsrwx 1 root shadow 16992 Apr 28 07:45 pam_authentication +``` +*/ + +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <termios.h> + +#include <security/pam_appl.h> +#include <security/pam_misc.h> -char * password; // needs to be global +char *password; // needs to be global int supply_password(int num_msg, const struct pam_message **msgm, struct pam_response **responsep, void *appdata_ptr) { - struct pam_response* response = (struct pam_response*)calloc(sizeof(struct pam_response), num_msg); + struct pam_response *response = + (struct pam_response*)calloc(sizeof(struct pam_response), num_msg); int i; for (i=0; i<num_msg; i++) { if (msgm[i]->msg_style == PAM_PROMPT_ECHO_OFF) { @@ -51,24 +67,95 @@ static struct pam_conv conv = NULL }; -int main(int args, char** argv) { - if (args != 3) { - fprintf(stderr, "Usage: pam_authentication username password\n"); +/** + * @brief Obtain the password from the given file. + * + * @param filename: If "-", read from stdin. + * + * @return 0 if successful, else 1. + */ +bool get_password(char *filename) { + // With code from https://stackoverflow.com/a/1196696/232888 + // by user https://stackoverflow.com/users/89266/dfa + struct termios backup, secret_setting; + bool is_tty = isatty(fileno(stdin)); + /* disable echo */ + if (is_tty) { + tcgetattr(fileno(stdin), &backup); + secret_setting = backup; + secret_setting.c_lflag &= ~ECHO; + secret_setting.c_lflag |= ECHONL; + + if (tcsetattr(fileno(stdin), TCSANOW, &secret_setting) != 0) { + perror("Setting echo-less output flags failed."); + return EXIT_FAILURE; + } + } + + FILE *pwfile; + if (strcmp(filename, "-")) { + pwfile = fopen(filename, "r"); + if (pwfile == NULL) { + perror(filename); + if (is_tty) { + tcsetattr(fileno(stdin), TCSANOW, &backup); + } + return false; + } + } else { + pwfile = stdin; + printf("Enter the password: "); + } + password = NULL; + size_t n = 0; + ssize_t pwlen = getline(&password, &n, pwfile); + if (strcmp(filename, "-")) { + fclose(pwfile); + } + password[pwlen - 1] = 0; + + /* restore terminal settings */ + if (is_tty) { + if (tcsetattr(fileno(stdin), TCSANOW, &backup) != 0) { + perror("Resetting output flags failed."); + } + } + + return true; +} + +int main(int argc, char **argv) { + if (argc < 2 || argc > 3) { + fprintf(stderr, "Usage: pam_authentication <username> [<password_file>]\n"); return 2; } pam_handle_t *pamh; - char * username = argv[1]; - password = argv[2]; + char *username = argv[1]; + char *pw_file = "-"; + if (argc == 3) { + pw_file = argv[2]; + } + + if (! get_password(pw_file)) { + fprintf(stderr, "Error while reading password.\n"); + return 3; + } + int res = pam_start("login", username, &conv, &pamh); if (!res == PAM_SUCCESS) { fprintf(stderr, "Error in starting pam authentication.\n"); return 2; } - + /* printf("\n>%s<\n", password); // Warning: this prints the password! */ res = pam_authenticate(pamh, 0); - // printf("Return code %i: %s\n", res, pam_strerror(pamh, res)); + +/* printf("PAM_AUTH_ERR: %i\n\ */ +/* PAM_CRED_INSUFFICIENT: %i\n\ */ +/* PAM_AUTHINFO_UNAVAIL: %i\n", PAM_AUTH_ERR, PAM_CRED_INSUFFICIENT, PAM_AUTHINFO_UNAVAIL); */ +/* printf("Return code (success=%i) %i: %s\n", PAM_SUCCESS, res, pam_strerror(pamh, res)); */ + free(password); return res; } diff --git a/misc/pam_authentication/pam_authentication.sh b/misc/pam_authentication/pam_authentication.sh index e2a6fb76d8bada2f21749d1e9c9f3f5330ef8555..972e48f1ccc9c65ca0c56773f7e20a5da0977ebb 100755 --- a/misc/pam_authentication/pam_authentication.sh +++ b/misc/pam_authentication/pam_authentication.sh @@ -5,6 +5,8 @@ # # Copyright (C) 2018 Research Group Biomedical Physics, # Max-Planck-Institute for Dynamics and Self-Organization Göttingen +# Copyright (C) 2020 Indiscale GmbH <info@indiscale.com> +# Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -21,7 +23,7 @@ # # ** end header # -if ./bin/pam_authentication $1 $2 ; then +if ./bin/pam_authentication $1 ; then echo "[OK]" exit 0 else diff --git a/src/main/java/caosdb/server/accessControl/Pam.java b/src/main/java/caosdb/server/accessControl/Pam.java index 606fddb6858968c9ccf363dfd5fb703d4b264913..0cae6fd74ae3924e868757e1f058a430a23b1ff3 100644 --- a/src/main/java/caosdb/server/accessControl/Pam.java +++ b/src/main/java/caosdb/server/accessControl/Pam.java @@ -4,8 +4,9 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019 IndiScale GmbH - * Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com) + * Copyright (C) 2019, 2020 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2019 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -70,9 +71,13 @@ public class Pam implements UserSource { final File script = new File(this.pam_script); - final ProcessBuilder pb = new ProcessBuilder(script.getAbsolutePath(), username, password); + final ProcessBuilder pb = new ProcessBuilder(script.getAbsolutePath(), username); pb.directory(script.getParentFile()); - return pb.start(); + final Process p = pb.start(); + p.getOutputStream().write(password.getBytes()); + p.getOutputStream().write('\n'); + p.getOutputStream().flush(); + return p; } @Override diff --git a/src/main/java/caosdb/server/accessControl/UserSources.java b/src/main/java/caosdb/server/accessControl/UserSources.java index 7a0298f6ddb7722eecbb1cf8c0cd115bbedb5b7e..d0f707ebaaec8aa97e9b87d760fc0631cbd2f72a 100644 --- a/src/main/java/caosdb/server/accessControl/UserSources.java +++ b/src/main/java/caosdb/server/accessControl/UserSources.java @@ -1,24 +1,33 @@ /* - * ** header v3.0 This file is a part of the CaosDB Project. + * ** header v3.0 + * This file is a part of the CaosDB Project. * - * Copyright (C) 2018 Research Group Biomedical Physics, Max-Planck-Institute for Dynamics and - * Self-Organization Göttingen + * Copyright (C) 2018 Research Group Biomedical Physics, + * Max-Planck-Institute for Dynamics and Self-Organization Göttingen * - * This program is free software: you can redistribute it and/or modify it under the terms of the - * GNU Affero General Public License as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see <https://www.gnu.org/licenses/>. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. * * ** end header */ package caosdb.server.accessControl; +import caosdb.server.CaosDBServer; +import caosdb.server.ServerProperties; +import caosdb.server.entity.Message; +import caosdb.server.transaction.RetrieveRoleTransaction; +import caosdb.server.transaction.RetrieveUserTransaction; +import caosdb.server.utils.ServerMessages; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -31,12 +40,6 @@ import org.apache.shiro.config.Ini; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import caosdb.server.CaosDBServer; -import caosdb.server.ServerProperties; -import caosdb.server.entity.Message; -import caosdb.server.transaction.RetrieveRoleTransaction; -import caosdb.server.transaction.RetrieveUserTransaction; -import caosdb.server.utils.ServerMessages; public class UserSources extends HashMap<String, UserSource> { @@ -58,8 +61,10 @@ public class UserSources extends HashMap<String, UserSource> { private UserSources() { initMap(); this.put(getInternalRealm()); - final String[] realms = this.map.getSectionProperty(Ini.DEFAULT_SECTION_NAME, KEY_REALMS) - .split("(\\s*,\\s*)|(\\s+)"); + final String[] realms = + this.map + .getSectionProperty(Ini.DEFAULT_SECTION_NAME, KEY_REALMS) + .split("(\\s*,\\s*)|(\\s+)"); for (final String realm : realms) { if (realm != null && !realm.isEmpty()) { final String className = this.map.getSectionProperty(realm, KEY_REALM_CLASS); @@ -69,8 +74,12 @@ public class UserSources extends HashMap<String, UserSource> { final Class<? extends UserSource> clazz = (Class<? extends UserSource>) Class.forName(className); this.put(clazz.getDeclaredConstructor().newInstance()); - } catch (IllegalArgumentException | InvocationTargetException | NoSuchMethodException - | SecurityException | ClassNotFoundException | InstantiationException + } catch (IllegalArgumentException + | InvocationTargetException + | NoSuchMethodException + | SecurityException + | ClassNotFoundException + | InstantiationException | IllegalAccessException e) { logger.error("LOAD_USER_SOURCE", e); } @@ -99,8 +108,9 @@ public class UserSources extends HashMap<String, UserSource> { public void initMap() { this.map = null; try { - final FileInputStream f = new FileInputStream( - CaosDBServer.getServerProperty(ServerProperties.KEY_USER_SOURCES_INI_FILE)); + final FileInputStream f = + new FileInputStream( + CaosDBServer.getServerProperty(ServerProperties.KEY_USER_SOURCES_INI_FILE)); this.map = new Ini(); this.map.load(f); f.close(); diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java index a7a5c607df280a29c89a947e276bfdb789d11baa..0e557da58074729b9c3635453dd4b84b657599bb 100644 --- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java +++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java @@ -22,14 +22,14 @@ */ package caosdb.server.database.backend.implementation.MySQL; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Iterator; import caosdb.server.database.access.Access; import caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl; import caosdb.server.database.exceptions.TransactionException; import caosdb.server.database.proto.SparseEntity; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Iterator; public class MySQLRetrieveAllUncheckedFiles extends MySQLTransaction implements RetrieveAllUncheckedFilesImpl { diff --git a/src/main/java/caosdb/server/datatype/AbstractDatatype.java b/src/main/java/caosdb/server/datatype/AbstractDatatype.java index 81072a72f2026c8b6fa4dbea65a3e99b85b513e4..0cde534aa74a05e33591d5e8d719316e7f1704f6 100644 --- a/src/main/java/caosdb/server/datatype/AbstractDatatype.java +++ b/src/main/java/caosdb/server/datatype/AbstractDatatype.java @@ -22,11 +22,11 @@ */ package caosdb.server.datatype; -import java.util.HashMap; -import org.reflections.Reflections; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Message; import caosdb.server.entity.container.Container; +import java.util.HashMap; +import org.reflections.Reflections; public abstract class AbstractDatatype { diff --git a/src/main/java/caosdb/server/jobs/Job.java b/src/main/java/caosdb/server/jobs/Job.java index fb6b4aeb91e4d3227fb6b773dae26118c115b92a..110809e77ae0d40076642d953c15b0d9566eddd6 100644 --- a/src/main/java/caosdb/server/jobs/Job.java +++ b/src/main/java/caosdb/server/jobs/Job.java @@ -22,16 +22,6 @@ */ package caosdb.server.jobs; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authz.Permission; -import org.apache.shiro.subject.Subject; -import org.reflections.Reflections; import caosdb.server.CaosDBException; import caosdb.server.database.BackendTransaction; import caosdb.server.database.backend.transaction.GetIDByName; @@ -54,6 +44,16 @@ import caosdb.server.utils.EntityStatus; import caosdb.server.utils.Observable; import caosdb.server.utils.Observer; import caosdb.server.utils.ServerMessages; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.subject.Subject; +import org.reflections.Reflections; public abstract class Job extends AbstractObservable implements Observer { private Transaction<? extends TransactionContainer> transaction = null; @@ -88,7 +88,12 @@ public abstract class Job extends AbstractObservable implements Observer { final Job job = clazz.getDeclaredConstructor().newInstance(); job.init(getMode(), entity, getTransaction()); return getTransaction().getSchedule().add(job); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + } catch (InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException + | NoSuchMethodException + | SecurityException e) { throw new CaosDBException(e); } } @@ -332,7 +337,12 @@ public abstract class Job extends AbstractObservable implements Observer { return ret; } return null; - } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + } catch (final InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException + | NoSuchMethodException + | SecurityException e) { throw new TransactionException(e); } } diff --git a/src/main/java/caosdb/server/jobs/extension/AWIBoxLoanModel.java b/src/main/java/caosdb/server/jobs/extension/AWIBoxLoanModel.java index 5177cb0d201825ade810694ceee16e7079dd929c..d647b64d2d56a8cc073467326d9ecb868b6c7d0f 100644 --- a/src/main/java/caosdb/server/jobs/extension/AWIBoxLoanModel.java +++ b/src/main/java/caosdb/server/jobs/extension/AWIBoxLoanModel.java @@ -1,6 +1,5 @@ package caosdb.server.jobs.extension; -import java.util.Objects; import caosdb.server.CaosDBServer; import caosdb.server.database.exceptions.EntityDoesNotExistException; import caosdb.server.database.exceptions.EntityWasNotUniqueException; @@ -8,6 +7,7 @@ import caosdb.server.entity.EntityInterface; import caosdb.server.entity.Role; import caosdb.server.entity.wrapper.Property; import caosdb.server.jobs.ContainerJob; +import caosdb.server.utils.Utils; import java.util.Objects; public abstract class AWIBoxLoanModel extends ContainerJob { diff --git a/src/main/java/caosdb/server/jobs/extension/SQLiteTransaction.java b/src/main/java/caosdb/server/jobs/extension/SQLiteTransaction.java index 41778ac6ca184d819b42954a80369df813373297..4882f0aff290cc53f80372f1451b538c664dae56 100644 --- a/src/main/java/caosdb/server/jobs/extension/SQLiteTransaction.java +++ b/src/main/java/caosdb/server/jobs/extension/SQLiteTransaction.java @@ -22,6 +22,11 @@ */ package caosdb.server.jobs.extension; +import caosdb.server.CaosDBException; +import caosdb.server.entity.FileProperties; +import caosdb.server.entity.Message; +import caosdb.server.jobs.EntityJob; +import caosdb.server.utils.FileUtils; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.Connection; @@ -34,11 +39,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.concurrent.locks.ReentrantLock; -import caosdb.server.CaosDBException; -import caosdb.server.entity.FileProperties; -import caosdb.server.entity.Message; -import caosdb.server.jobs.EntityJob; -import caosdb.server.utils.FileUtils; public class SQLiteTransaction extends EntityJob { private static HashMap<String, ReentrantLock> lockedTables = new HashMap<String, ReentrantLock>(); diff --git a/src/main/java/caosdb/server/query/StoredAt.java b/src/main/java/caosdb/server/query/StoredAt.java index 243822f740f0410345fe322e472146d4a965039b..b16bb83f2f95c4da1f86292a7d0f17e73f8150d4 100644 --- a/src/main/java/caosdb/server/query/StoredAt.java +++ b/src/main/java/caosdb/server/query/StoredAt.java @@ -23,6 +23,8 @@ package caosdb.server.query; import static java.sql.Types.VARCHAR; + +import caosdb.server.query.Query.QueryException; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.CallableStatement; @@ -30,7 +32,6 @@ import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jdom2.Element; -import caosdb.server.query.Query.QueryException; public class StoredAt implements EntityFilterInterface { diff --git a/src/main/java/caosdb/server/resource/AuthenticationResource.java b/src/main/java/caosdb/server/resource/AuthenticationResource.java index 116439074544f57990ab32ad99d69c20cc5cabe3..d3b49580cf1a86347398ef7d36f6021f710e7da3 100644 --- a/src/main/java/caosdb/server/resource/AuthenticationResource.java +++ b/src/main/java/caosdb/server/resource/AuthenticationResource.java @@ -23,6 +23,7 @@ package caosdb.server.resource; import caosdb.server.CaosDBException; +import caosdb.server.CaosDBServer; import caosdb.server.accessControl.AuthenticationUtils; import caosdb.server.accessControl.RealmUsernamePasswordToken; import caosdb.server.accessControl.UserSources; @@ -108,12 +109,14 @@ public class AuthenticationResource extends AbstractCaosDBServerResource { } return success(null); - } catch (final CredentialsException e) { - getLogger().log(Level.INFO, "LOGIN_FAILED", e); - } catch (final AccountException e) { + } catch (final CredentialsException | AccountException e) { getLogger().log(Level.INFO, "LOGIN_FAILED", e); } catch (final AuthenticationException e) { - getLogger().log(Level.INFO, "LOGIN_FAILED", e); + if (CaosDBServer.isDebugMode()) { + getLogger().log(Level.INFO, "LOGIN_FAILED", e); + } else { + getLogger().log(Level.INFO, "LOGIN_FAILED\t" + e.getMessage()); + } } } diff --git a/src/main/java/caosdb/server/utils/mail/Mail.java b/src/main/java/caosdb/server/utils/mail/Mail.java index 36d5e9332ff33c925c8b04c8ae44d0e4e5120537..c2036f909009a5ac0f294c6b0abed572e2d45790 100644 --- a/src/main/java/caosdb/server/utils/mail/Mail.java +++ b/src/main/java/caosdb/server/utils/mail/Mail.java @@ -22,9 +22,9 @@ */ package caosdb.server.utils.mail; -import java.util.Date; import caosdb.server.CaosDBServer; import caosdb.server.ServerProperties; +import java.util.Date; public class Mail { diff --git a/src/test/java/caosdb/server/query/TestCQL.java b/src/test/java/caosdb/server/query/TestCQL.java index ebe1cc8c53b5beaeb3762565f351fe801d20f649..37bb5e59cbeef858392293253d4382e7e2f044ae 100644 --- a/src/test/java/caosdb/server/query/TestCQL.java +++ b/src/test/java/caosdb/server/query/TestCQL.java @@ -28,6 +28,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; + +import caosdb.server.CaosDBServer; +import caosdb.server.database.access.Access; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.database.exceptions.TransactionException; +import caosdb.server.query.CQLParser.CqContext; +import caosdb.server.query.Query.Pattern; +import caosdb.server.query.Query.QueryException; +import caosdb.server.utils.Initialization; import java.io.IOException; import java.sql.SQLException; import java.util.LinkedList; @@ -38,14 +47,6 @@ import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.junit.BeforeClass; import org.junit.Test; -import caosdb.server.CaosDBServer; -import caosdb.server.database.access.Access; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.database.exceptions.TransactionException; -import caosdb.server.query.CQLParser.CqContext; -import caosdb.server.query.Query.Pattern; -import caosdb.server.query.Query.QueryException; -import caosdb.server.utils.Initialization; public class TestCQL {