From 7ac5999e4d921a84e93c03a22f1bb379c56a5f0f Mon Sep 17 00:00:00 2001 From: Sijie Xiang Date: Wed, 17 Jan 2024 19:11:10 -0800 Subject: [PATCH 01/25] Upgrade to MongoDB 7.0.4 (#415) upgrade to Mongo 7.0.4 --- .github/workflows/test.yml | 2 +- build.xml | 5 +- src/us/kbase/auth2/service/AuthBuilder.java | 14 +- .../auth2/service/AuthenticationService.java | 2 +- src/us/kbase/test/auth2/MongoController.java | 150 +++++++++++++ .../test/auth2/MongoStorageTestManager.java | 10 +- .../MongoStorageDuplicateKeyCheckerTest.java | 5 +- .../mongo/MongoStorageStartUpTest.java | 202 +++++++----------- .../lib/storage/mongo/MongoStorageTester.java | 4 +- 9 files changed, 257 insertions(+), 137 deletions(-) create mode 100644 src/us/kbase/test/auth2/MongoController.java diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1aee87ec..eceabf35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: # doesn't touch mongo against multiple mongo versions include: - java: '8' - mongo: 'mongodb-linux-x86_64-3.6.23' + mongo: 'mongodb-linux-x86_64-ubuntu2204-7.0.4' wired_tiger: 'false' - java: '11' mongo: 'mongodb-linux-x86_64-3.6.23' diff --git a/build.xml b/build.xml index 659d03d2..0a18e8bf 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,10 @@ - + + + + diff --git a/src/us/kbase/auth2/service/AuthBuilder.java b/src/us/kbase/auth2/service/AuthBuilder.java index 5fda63be..e80de19a 100644 --- a/src/us/kbase/auth2/service/AuthBuilder.java +++ b/src/us/kbase/auth2/service/AuthBuilder.java @@ -2,13 +2,15 @@ import static java.util.Objects.requireNonNull; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.slf4j.LoggerFactory; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.MongoClientSettings; import com.mongodb.MongoCredential; import com.mongodb.MongoException; import com.mongodb.ServerAddress; @@ -62,15 +64,17 @@ public AuthBuilder( private MongoClient buildMongo(final AuthStartupConfig c) throws StorageInitException { //TODO ZLATER MONGO handle shards & replica sets + final MongoClientSettings.Builder mongoBuilder = MongoClientSettings.builder().applyToClusterSettings( + builder -> builder.hosts(Arrays.asList(new ServerAddress(c.getMongoHost())))); + try { if (c.getMongoUser().isPresent()) { final MongoCredential creds = MongoCredential.createCredential( c.getMongoUser().get(), c.getMongoDatabase(), c.getMongoPwd().get()); // unclear if and when it's safe to clear the password - return new MongoClient(new ServerAddress(c.getMongoHost()), creds, - MongoClientOptions.builder().build()); + return MongoClients.create(mongoBuilder.credential(creds).build()); } else { - return new MongoClient(new ServerAddress(c.getMongoHost())); + return MongoClients.create(mongoBuilder.build()); } } catch (MongoException e) { LoggerFactory.getLogger(getClass()).error( diff --git a/src/us/kbase/auth2/service/AuthenticationService.java b/src/us/kbase/auth2/service/AuthenticationService.java index b51db4a2..a69f8706 100644 --- a/src/us/kbase/auth2/service/AuthenticationService.java +++ b/src/us/kbase/auth2/service/AuthenticationService.java @@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; -import com.mongodb.MongoClient; +import com.mongodb.client.MongoClient; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; diff --git a/src/us/kbase/test/auth2/MongoController.java b/src/us/kbase/test/auth2/MongoController.java new file mode 100644 index 00000000..6d33266d --- /dev/null +++ b/src/us/kbase/test/auth2/MongoController.java @@ -0,0 +1,150 @@ +package us.kbase.test.auth2; + +import static us.kbase.common.test.controllers.ControllerCommon.checkExe; +import static us.kbase.common.test.controllers.ControllerCommon.findFreePort; +import static us.kbase.common.test.controllers.ControllerCommon.makeTempDirs; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import com.github.zafarkhaja.semver.Version; +import org.apache.commons.io.FileUtils; + + +/** Q&D Utility to run a Mongo server for the purposes of testing from + * Java. + * @author gaprice@lbl.gov, sijiex@lbl.gov + * + */ +public class MongoController { + + private final static String DATA_DIR = "data"; + + private final static List tempDirectories = + new LinkedList(); + static { + tempDirectories.add(DATA_DIR); + } + + private final static Version MONGO_DB_6_1 = + Version.forIntegers(6,1); + + private final Path tempDir; + + private final Process mongo; + + private final int port; + + public MongoController( + final String mongoExe, + final Path rootTempDir) + throws Exception { + this(mongoExe, rootTempDir, false); + } + + public MongoController( + final String mongoExe, + final Path rootTempDir, + final boolean useWiredTiger) + throws Exception { + checkExe(mongoExe, "mongod server"); + tempDir = makeTempDirs(rootTempDir, "MongoController-", tempDirectories); + port = findFreePort(); + Version dbVer = getMongoDBVer(mongoExe); + List command = getMongoServerStartCommand(mongoExe, useWiredTiger, dbVer); + mongo = startProcess(command); + } + + public int getServerPort() { + return port; + } + + public Path getTempDir() { + return tempDir; + } + + public void destroy(boolean deleteTempFiles) throws IOException { + if (mongo != null) { + mongo.destroy(); + } + if (tempDir != null && deleteTempFiles) { + FileUtils.deleteDirectory(tempDir.toFile()); + } + } + + private static Version getMongoDBVer(final String mongoExe) throws IOException { + + // build MongoDB version check command + List command = new LinkedList(); + command.addAll(Arrays.asList(mongoExe, "--version")); + + // start MongoDB version check process + ProcessBuilder checkVerPb = new ProcessBuilder(command); + Process checkVerProcess = checkVerPb.start(); + + // parse mongod --version output string + String dbVer = new BufferedReader( + new InputStreamReader(checkVerProcess.getInputStream())) + .lines() + .collect(Collectors.joining(" ")) + .split(" ")[2].substring(1); + + System.out.println("MongoDB version: " + dbVer); + checkVerProcess.destroy(); + return Version.valueOf(dbVer); + } + + private List getMongoServerStartCommand(final String mongoExe, + final boolean useWiredTiger, + final Version dbVer) { + List command = new LinkedList(); + command.addAll(Arrays.asList(mongoExe, "--port", "" + port, + "--dbpath", tempDir.resolve(DATA_DIR).toString())); + + // Starting in MongoDB 6.1, journaling is always enabled. + // As a result, MongoDB removes the storage.journal.enabled option + // and the corresponding --journal and --nojournal command-line options. + // https://www.mongodb.com/docs/manual/release-notes/6.1/#changes-to-journaling + if (dbVer.lessThan(MONGO_DB_6_1)) { + command.addAll(Arrays.asList("--nojournal")); + } + if (useWiredTiger) { + command.addAll(Arrays.asList("--storageEngine", "wiredTiger")); + } + return command; + } + + private Process startProcess(List command) throws Exception { + ProcessBuilder servpb = new ProcessBuilder(command) + .redirectErrorStream(true) + .redirectOutput(getTempDir().resolve("mongo.log").toFile()); + + Process mongoProcess = servpb.start(); + Thread.sleep(1000); //wait for server to start up + return mongoProcess; + } + + public static void main(String[] args) throws Exception { + us.kbase.common.test.controllers.mongo.MongoController ac = new us.kbase.common.test.controllers.mongo.MongoController( + "/kb/runtime/bin/mongod", + Paths.get("workspacetesttemp")); + System.out.println(ac.getServerPort()); + System.out.println(ac.getTempDir()); + Scanner reader = new Scanner(System.in); + System.out.println("any char to shut down"); + //get user input for a + reader.next(); + ac.destroy(false); + reader.close(); + } + +} + diff --git a/src/us/kbase/test/auth2/MongoStorageTestManager.java b/src/us/kbase/test/auth2/MongoStorageTestManager.java index 45d79ab9..ebf1a2cb 100644 --- a/src/us/kbase/test/auth2/MongoStorageTestManager.java +++ b/src/us/kbase/test/auth2/MongoStorageTestManager.java @@ -15,12 +15,12 @@ import org.bson.Document; import com.github.zafarkhaja.semver.Version; -import com.mongodb.MongoClient; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; import com.mongodb.client.MongoDatabase; import us.kbase.auth2.cryptutils.RandomDataGenerator; import us.kbase.auth2.lib.storage.mongo.MongoStorage; -import us.kbase.common.test.controllers.mongo.MongoController; public class MongoStorageTestManager { @@ -42,16 +42,16 @@ public MongoStorageTestManager(final String dbName) throws Exception { wiredTiger = useWiredTigerEngine(); System.out.println(String.format("Testing against mongo executable %s on port %s", getMongoExe(), mongo.getServerPort())); - mc = new MongoClient("localhost:" + mongo.getServerPort()); + mc = MongoClients.create("mongodb://localhost:" + mongo.getServerPort()); db = mc.getDatabase(dbName); - + final Document bi = db.runCommand(new Document("buildinfo", 1)); final String version = bi.getString("version"); mongoDBVer = Version.valueOf(version); indexVer = mongoDBVer.greaterThanOrEqualTo(Version.forIntegers(3, 4)) ? 2 : 1; reset(); } - + public void destroy() throws Exception { if (mc != null) { mc.close(); diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java index 19fe314c..cf19b290 100644 --- a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java +++ b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java @@ -131,8 +131,9 @@ public void unparseable() throws Exception { fail("expected exception"); } catch (InvocationTargetException ex) { TestCommon.assertExceptionCorrect((Exception) ex.getTargetException(), - new AuthStorageException("Unable to parse duplicate key error: some ")); - + new AuthStorageException("Unable to parse duplicate key error: " + + "Write operation error on server 127.0.0.1:27017. " + + "Write error: WriteError{code=11000, message='some ")); } } diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java index 14e12c3a..f6642907 100644 --- a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java +++ b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java @@ -1,9 +1,9 @@ package us.kbase.test.auth2.lib.storage.mongo; import static org.hamcrest.CoreMatchers.is; + import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; - import static us.kbase.test.auth2.TestCommon.set; import java.util.Arrays; @@ -36,7 +36,7 @@ public void nullConstructor() throws Exception { assertThat("incorrect exception message", e.getMessage(), is("db")); } } - + @Test public void startUpAndCheckConfigDoc() throws Exception { final MongoDatabase db = mc.getDatabase("startUpAndCheckConfigDoc"); @@ -45,35 +45,35 @@ public void startUpAndCheckConfigDoc() throws Exception { assertThat("Only one config doc", col.countDocuments(), is(1L)); final FindIterable c = col.find(); final Document d = c.first(); - + assertThat("correct config key & value", (String)d.get("schema"), is("schema")); assertThat("not in update", (Boolean)d.get("inupdate"), is(false)); assertThat("schema v1", (Integer)d.get("schemaver"), is(1)); - + //check startup works with the config object in place final MongoStorage ms = new MongoStorage(db); ms.setCustomRole(new CustomRole("foo", "bar")); assertThat("failed basic storage operation", ms.getCustomRoles(), is(set(new CustomRole("foo", "bar")))); } - + @Test public void startUpWith2ConfigDocs() throws Exception { final MongoDatabase db = mc.getDatabase("startUpWith2ConfigDocs"); - + final Document m = new Document("schema", "schema") .append("inupdate", false) .append("schemaver", 1); - + db.getCollection("config").insertMany(Arrays.asList(m, // need to create a new document to create a new mongo _id new Document(m))); - + final Pattern errorPattern = Pattern.compile( "Failed to create index: Write failed with error code 11000 and error message " + - "'(exception: )?E11000 duplicate key error (index|collection): " + + "'(exception: )?(.*)E11000 duplicate key error (index|collection): " + "startUpWith2ConfigDocs.config( index: |\\.\\$)schema_1\\s+dup key: " + - "\\{ : \"schema\" \\}'"); + "(\\{ schema: \"schema\" \\}'|\\{ : \"schema\" \\}')"); try { new MongoStorage(db); fail("started mongo with bad config"); @@ -82,36 +82,36 @@ public void startUpWith2ConfigDocs() throws Exception { assertThat("exception did not match: \n" + e.getMessage(), match.matches(), is(true)); } } - + @Test public void startUpWithBadSchemaVersion() throws Exception { final MongoDatabase db = mc.getDatabase("startUpWithBadSchemaVersion"); - + final Document m = new Document("schema", "schema") .append("inupdate", false) .append("schemaver", 4); - + db.getCollection("config").insertOne(m); - + failMongoStart(db, new StorageInitException( "Incompatible database schema. Server is v1, DB is v4")); } - + @Test public void startUpWithUpdateInProgress() throws Exception { final MongoDatabase db = mc.getDatabase("startUpWithUpdateInProgress"); - + final Document m = new Document("schema", "schema") .append("inupdate", true) .append("schemaver", 1); - + db.getCollection("config").insertOne(m); - + failMongoStart(db, new StorageInitException( "The database is in the middle of an update from v1 of the " + "schema. Aborting startup.")); } - + private void failMongoStart(final MongoDatabase db, final Exception exp) throws Exception { try { @@ -121,13 +121,13 @@ private void failMongoStart(final MongoDatabase db, final Exception exp) TestCommon.assertExceptionCorrect(e, exp); } } - + /* The following tests ensure that all indexes are created correctly. The collection names - * are tested so that if a new collection is added the test will fail without altering + * are tested so that if a new collection is added the test will fail without altering * said test, at which time the coder will hopefully read this notice and add index tests * for the new collection. */ - + @Test public void checkCollectionNames() throws Exception { final Set names = new HashSet<>(); @@ -151,284 +151,246 @@ public void checkCollectionNames() throws Exception { db.listCollectionNames().forEach((Consumer) names::add); assertThat("incorrect collection names", names, is(expected)); } - + @Test public void indexesConfig() { - final Set indexes = new HashSet<>(); - db.getCollection("config").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("config"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("schema", 1)) - .append("name", "schema_1") - .append("ns", "test_mongostorage.config"), + .append("name", "schema_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") - .append("ns", "test_mongostorage.config") ))); } - + @Test public void indexesConfigApp() { - final Set indexes = new HashSet<>(); - db.getCollection("config_app").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("config_app"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("key", 1)) - .append("name", "key_1") - .append("ns", "test_mongostorage.config_app"), + .append("name", "key_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") - .append("ns", "test_mongostorage.config_app") ))); } - + @Test public void indexesConfigExt() { - final Set indexes = new HashSet<>(); - db.getCollection("config_ext").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("config_ext"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("key", 1)) - .append("name", "key_1") - .append("ns", "test_mongostorage.config_ext"), + .append("name", "key_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") - .append("ns", "test_mongostorage.config_ext") ))); } - + @Test public void indexesConfigProv() { - final Set indexes = new HashSet<>(); - db.getCollection("config_prov").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("config_prov"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("prov", 1).append("key", 1)) - .append("name", "prov_1_key_1") - .append("ns", "test_mongostorage.config_prov"), + .append("name", "prov_1_key_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") - .append("ns", "test_mongostorage.config_prov") ))); } - + @Test public void indexesCustRoles() { - final Set indexes = new HashSet<>(); - db.getCollection("cust_roles").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("cust_roles"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("id", 1)) - .append("name", "id_1") - .append("ns", "test_mongostorage.cust_roles"), + .append("name", "id_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) .append("name", "_id_") - .append("ns", "test_mongostorage.cust_roles") ))); } - + @Test public void indexesTestCustRoles() { - final Set indexes = new HashSet<>(); - db.getCollection("test_cust_roles").listIndexes() - .forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("test_cust_roles"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("id", 1)) - .append("name", "id_1") - .append("ns", "test_mongostorage.test_cust_roles"), + .append("name", "id_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.test_cust_roles"), + .append("name", "_id_"), new Document("v", indexVer) .append("key", new Document("expires", 1)) .append("name", "expires_1") - .append("ns", "test_mongostorage.test_cust_roles") .append("expireAfterSeconds", 0L) ))); } - + @Test public void indexesTempData() { - final Set indexes = new HashSet<>(); - db.getCollection("tempdata").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("tempdata"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("token", 1)) - .append("name", "token_1") - .append("ns", "test_mongostorage.tempdata"), + .append("name", "token_1"), new Document("v", indexVer) .append("key", new Document("expires", 1)) .append("name", "expires_1") - .append("ns", "test_mongostorage.tempdata") .append("expireAfterSeconds", 0L), new Document("v", indexVer) .append("sparse", true) .append("key", new Document("user", 1)) - .append("name", "user_1") - .append("ns", "test_mongostorage.tempdata"), + .append("name", "user_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.tempdata"), + .append("name", "_id_"), new Document("v", indexVer) .append("unique", true) .append("key", new Document("id", 1)) .append("name", "id_1") - .append("ns", "test_mongostorage.tempdata") ))); } - + @Test public void indexesTokens() { - final Set indexes = new HashSet<>(); - db.getCollection("tokens").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("tokens"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("token", 1)) - .append("name", "token_1") - .append("ns", "test_mongostorage.tokens"), + .append("name", "token_1"), new Document("v", indexVer) .append("key", new Document("expires", 1)) .append("name", "expires_1") - .append("ns", "test_mongostorage.tokens") .append("expireAfterSeconds", 0L), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.tokens"), + .append("name", "_id_"), new Document("v", indexVer) .append("key", new Document("user", 1)) - .append("name", "user_1") - .append("ns", "test_mongostorage.tokens"), + .append("name", "user_1"), new Document("v", indexVer) .append("unique", true) .append("key", new Document("id", 1)) .append("name", "id_1") - .append("ns", "test_mongostorage.tokens") ))); } - + @Test public void indexesTestTokens() { - final Set indexes = new HashSet<>(); - db.getCollection("test_tokens").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("test_tokens"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("unique", true) .append("key", new Document("token", 1)) - .append("name", "token_1") - .append("ns", "test_mongostorage.test_tokens"), + .append("name", "token_1"), new Document("v", indexVer) .append("key", new Document("expires", 1)) .append("name", "expires_1") - .append("ns", "test_mongostorage.test_tokens") .append("expireAfterSeconds", 0L), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.test_tokens"), + .append("name", "_id_"), new Document("v", indexVer) .append("key", new Document("user", 1)) - .append("name", "user_1") - .append("ns", "test_mongostorage.test_tokens"), + .append("name", "user_1"), new Document("v", indexVer) .append("unique", true) .append("key", new Document("id", 1)) .append("name", "id_1") - .append("ns", "test_mongostorage.test_tokens") ))); } - + @Test public void indexesUsers() { - final Set indexes = new HashSet<>(); - db.getCollection("users").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("users"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("key", new Document("custrls", 1)) .append("name", "custrls_1") - .append("ns", "test_mongostorage.users") .append("sparse", true), new Document("v", indexVer) .append("key", new Document("dispcan", 1)) - .append("name", "dispcan_1") - .append("ns", "test_mongostorage.users"), + .append("name", "dispcan_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.users"), + .append("name", "_id_"), new Document("v", indexVer) - .append("unique", true) + .append("unique", true) .append("key", new Document("idents.id", 1)) .append("name", "idents.id_1") - .append("ns", "test_mongostorage.users") .append("sparse", true), new Document("v", indexVer) .append("key", new Document("roles", 1)) .append("name", "roles_1") - .append("ns", "test_mongostorage.users") .append("sparse", true), new Document("v", indexVer) .append("unique", true) .append("key", new Document("user", 1)) - .append("name", "user_1") - .append("ns", "test_mongostorage.users"), + .append("name", "user_1"), new Document("v", indexVer) .append("key", new Document("anonid", 1)) .append("name", "anonid_1") - .append("ns", "test_mongostorage.users") .append("sparse", true) ))); } - + @Test public void indexesTestUsers() { - final Set indexes = new HashSet<>(); - db.getCollection("test_users").listIndexes().forEach((Consumer) indexes::add); + final Set indexes = getAndNormalizeIndexes("test_users"); assertThat("incorrect indexes", indexes, is(set( new Document("v", indexVer) .append("key", new Document("custrls", 1)) .append("name", "custrls_1") - .append("ns", "test_mongostorage.test_users") .append("sparse", true), new Document("v", indexVer) .append("key", new Document("dispcan", 1)) - .append("name", "dispcan_1") - .append("ns", "test_mongostorage.test_users"), + .append("name", "dispcan_1"), new Document("v", indexVer) .append("key", new Document("_id", 1)) - .append("name", "_id_") - .append("ns", "test_mongostorage.test_users"), + .append("name", "_id_"), new Document("v", indexVer) .append("key", new Document("roles", 1)) .append("name", "roles_1") - .append("ns", "test_mongostorage.test_users") .append("sparse", true), new Document("v", indexVer) .append("unique", true) .append("key", new Document("user", 1)) - .append("name", "user_1") - .append("ns", "test_mongostorage.test_users"), + .append("name", "user_1"), new Document("v", indexVer) .append("key", new Document("expires", 1)) .append("name", "expires_1") - .append("ns", "test_mongostorage.test_users") .append("expireAfterSeconds", 0L) ))); } + + private Set getAndNormalizeIndexes(final String collectionName) { + final Set indexes = new HashSet<>(); + for (Document index: db.getCollection(collectionName).listIndexes()) { + // In MongoDB 4.4, the listIndexes and the mongo shell helper method db.collection.getIndexes() + // no longer returns the namespace ns field in the index specification documents. + index.remove("ns"); + // some versions of Mongo return ints, some longs. Convert all to longs. + if (index.containsKey("expireAfterSeconds")) { + index.put("expireAfterSeconds", ((Number) index.get("expireAfterSeconds")).longValue()); + } + indexes.add(index); + } + return indexes; + } } diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java index 0e53ca84..4679e6c7 100644 --- a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java +++ b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java @@ -7,12 +7,12 @@ import org.junit.BeforeClass; import com.github.zafarkhaja.semver.Version; -import com.mongodb.MongoClient; +import com.mongodb.client.MongoClient; import com.mongodb.client.MongoDatabase; import us.kbase.auth2.cryptutils.RandomDataGenerator; import us.kbase.auth2.lib.storage.mongo.MongoStorage; -import us.kbase.common.test.controllers.mongo.MongoController; +import us.kbase.test.auth2.MongoController; import us.kbase.test.auth2.MongoStorageTestManager; public class MongoStorageTester { From aec2f93c7552aae731553fd66c5ecfe53229db6f Mon Sep 17 00:00:00 2001 From: Sijie Date: Wed, 7 Feb 2024 18:18:46 -0800 Subject: [PATCH 02/25] remove MongoController.java --- .classpath | 2 +- src/us/kbase/test/auth2/MongoController.java | 150 ------------------ .../test/auth2/MongoStorageTestManager.java | 1 + .../lib/storage/mongo/MongoStorageTester.java | 2 +- 4 files changed, 3 insertions(+), 152 deletions(-) delete mode 100644 src/us/kbase/test/auth2/MongoController.java diff --git a/.classpath b/.classpath index 1d5fa234..b75439e9 100644 --- a/.classpath +++ b/.classpath @@ -21,7 +21,7 @@ - + diff --git a/src/us/kbase/test/auth2/MongoController.java b/src/us/kbase/test/auth2/MongoController.java deleted file mode 100644 index 6d33266d..00000000 --- a/src/us/kbase/test/auth2/MongoController.java +++ /dev/null @@ -1,150 +0,0 @@ -package us.kbase.test.auth2; - -import static us.kbase.common.test.controllers.ControllerCommon.checkExe; -import static us.kbase.common.test.controllers.ControllerCommon.findFreePort; -import static us.kbase.common.test.controllers.ControllerCommon.makeTempDirs; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; -import java.util.stream.Collectors; - -import com.github.zafarkhaja.semver.Version; -import org.apache.commons.io.FileUtils; - - -/** Q&D Utility to run a Mongo server for the purposes of testing from - * Java. - * @author gaprice@lbl.gov, sijiex@lbl.gov - * - */ -public class MongoController { - - private final static String DATA_DIR = "data"; - - private final static List tempDirectories = - new LinkedList(); - static { - tempDirectories.add(DATA_DIR); - } - - private final static Version MONGO_DB_6_1 = - Version.forIntegers(6,1); - - private final Path tempDir; - - private final Process mongo; - - private final int port; - - public MongoController( - final String mongoExe, - final Path rootTempDir) - throws Exception { - this(mongoExe, rootTempDir, false); - } - - public MongoController( - final String mongoExe, - final Path rootTempDir, - final boolean useWiredTiger) - throws Exception { - checkExe(mongoExe, "mongod server"); - tempDir = makeTempDirs(rootTempDir, "MongoController-", tempDirectories); - port = findFreePort(); - Version dbVer = getMongoDBVer(mongoExe); - List command = getMongoServerStartCommand(mongoExe, useWiredTiger, dbVer); - mongo = startProcess(command); - } - - public int getServerPort() { - return port; - } - - public Path getTempDir() { - return tempDir; - } - - public void destroy(boolean deleteTempFiles) throws IOException { - if (mongo != null) { - mongo.destroy(); - } - if (tempDir != null && deleteTempFiles) { - FileUtils.deleteDirectory(tempDir.toFile()); - } - } - - private static Version getMongoDBVer(final String mongoExe) throws IOException { - - // build MongoDB version check command - List command = new LinkedList(); - command.addAll(Arrays.asList(mongoExe, "--version")); - - // start MongoDB version check process - ProcessBuilder checkVerPb = new ProcessBuilder(command); - Process checkVerProcess = checkVerPb.start(); - - // parse mongod --version output string - String dbVer = new BufferedReader( - new InputStreamReader(checkVerProcess.getInputStream())) - .lines() - .collect(Collectors.joining(" ")) - .split(" ")[2].substring(1); - - System.out.println("MongoDB version: " + dbVer); - checkVerProcess.destroy(); - return Version.valueOf(dbVer); - } - - private List getMongoServerStartCommand(final String mongoExe, - final boolean useWiredTiger, - final Version dbVer) { - List command = new LinkedList(); - command.addAll(Arrays.asList(mongoExe, "--port", "" + port, - "--dbpath", tempDir.resolve(DATA_DIR).toString())); - - // Starting in MongoDB 6.1, journaling is always enabled. - // As a result, MongoDB removes the storage.journal.enabled option - // and the corresponding --journal and --nojournal command-line options. - // https://www.mongodb.com/docs/manual/release-notes/6.1/#changes-to-journaling - if (dbVer.lessThan(MONGO_DB_6_1)) { - command.addAll(Arrays.asList("--nojournal")); - } - if (useWiredTiger) { - command.addAll(Arrays.asList("--storageEngine", "wiredTiger")); - } - return command; - } - - private Process startProcess(List command) throws Exception { - ProcessBuilder servpb = new ProcessBuilder(command) - .redirectErrorStream(true) - .redirectOutput(getTempDir().resolve("mongo.log").toFile()); - - Process mongoProcess = servpb.start(); - Thread.sleep(1000); //wait for server to start up - return mongoProcess; - } - - public static void main(String[] args) throws Exception { - us.kbase.common.test.controllers.mongo.MongoController ac = new us.kbase.common.test.controllers.mongo.MongoController( - "/kb/runtime/bin/mongod", - Paths.get("workspacetesttemp")); - System.out.println(ac.getServerPort()); - System.out.println(ac.getTempDir()); - Scanner reader = new Scanner(System.in); - System.out.println("any char to shut down"); - //get user input for a - reader.next(); - ac.destroy(false); - reader.close(); - } - -} - diff --git a/src/us/kbase/test/auth2/MongoStorageTestManager.java b/src/us/kbase/test/auth2/MongoStorageTestManager.java index ebf1a2cb..58776004 100644 --- a/src/us/kbase/test/auth2/MongoStorageTestManager.java +++ b/src/us/kbase/test/auth2/MongoStorageTestManager.java @@ -21,6 +21,7 @@ import us.kbase.auth2.cryptutils.RandomDataGenerator; import us.kbase.auth2.lib.storage.mongo.MongoStorage; +import us.kbase.common.test.controllers.mongo.MongoController; public class MongoStorageTestManager { diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java index 4679e6c7..94f6c902 100644 --- a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java +++ b/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java @@ -12,7 +12,7 @@ import us.kbase.auth2.cryptutils.RandomDataGenerator; import us.kbase.auth2.lib.storage.mongo.MongoStorage; -import us.kbase.test.auth2.MongoController; +import us.kbase.common.test.controllers.mongo.MongoController; import us.kbase.test.auth2.MongoStorageTestManager; public class MongoStorageTester { From b64a9c5e06486f54b572031cafef4743888f33cc Mon Sep 17 00:00:00 2001 From: Sijie Date: Wed, 7 Feb 2024 19:13:49 -0800 Subject: [PATCH 03/25] update build file --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 0a18e8bf..5013dd30 100644 --- a/build.xml +++ b/build.xml @@ -40,7 +40,7 @@ - + From 4351d97af8b053099fa876f507223ac045dc4613 Mon Sep 17 00:00:00 2001 From: Sijie Date: Wed, 7 Feb 2024 21:12:08 -0800 Subject: [PATCH 04/25] test kbase-common-0.2.0.jar --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eceabf35..2109c8ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: shell: bash run: | cd .. - git clone https://github.com/kbase/jars + git clone -b update_java_common https://github.com/kbase/jars cd - - name: Install mongo and set up test config From 09b20119ae823b7b4434dfa8cf5359259b00088c Mon Sep 17 00:00:00 2001 From: Sijie Date: Wed, 7 Feb 2024 21:26:25 -0800 Subject: [PATCH 05/25] both v3 and v7 pass --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2109c8ed..eceabf35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: shell: bash run: | cd .. - git clone -b update_java_common https://github.com/kbase/jars + git clone https://github.com/kbase/jars cd - - name: Install mongo and set up test config From f9c71c3f11452ac92fcac22580fbde4ebfdb677c Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 18 Jan 2024 15:43:15 -0800 Subject: [PATCH 06/25] Tests pass --- .gitattributes | 6 + .github/workflows/test.yml | 13 +- build.gradle | 154 +++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 +++++++++++++++++++++++ settings.gradle | 10 + 7 files changed, 411 insertions(+), 11 deletions(-) create mode 100644 .gitattributes create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 settings.gradle diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..00a51aff --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eceabf35..ce84ce6e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,10 +19,8 @@ jobs: auth2_tests: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - # Note that there's a mongo-only ant test directive. Use that for all mongo versions - # except for one if there's > 1 mongo version to test. No need for testing code that - # doesn't touch mongo against multiple mongo versions include: - java: '8' mongo: 'mongodb-linux-x86_64-ubuntu2204-7.0.4' @@ -40,13 +38,6 @@ jobs: distribution: 'temurin' java-version: ${{matrix.java}} - - name: Clone jars as sister repo - shell: bash - run: | - cd .. - git clone https://github.com/kbase/jars - cd - - - name: Install mongo and set up test config shell: bash run: | @@ -62,7 +53,7 @@ jobs: cat test.cfg - name: Run tests - run: ant test + run: ./gradlew test - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..ee1cd366 --- /dev/null +++ b/build.gradle @@ -0,0 +1,154 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.4.2/userguide/building_java_projects.html + */ + +plugins { + id 'java' + id 'war' + id 'jacoco' +} + +repositories { + mavenCentral() +} + +compileJava { + if (JavaVersion.current() <= JavaVersion.VERSION_1_8) { + // TODO BUILD remove when we no longer support java 8 + java.sourceCompatibility = JavaVersion.VERSION_1_8 + java.targetCompatibility = JavaVersion.VERSION_1_8 + } else { + options.release = 8 + } + // TODO NOW build in git commit + // TODO NOW javadocs +} + +test { + /* + * TODO TEST Figure out why tests fail without this and remove. Might have something to do + * with the stfuLoggers() call in many of the tests, might kill logging for tests that + * require it + * Although it seems to make Mongo start up correctly as well which is odd + */ + /* + * TODO TEST split tests into mongo wrapper tests & all other tests (incl. integration). + * Set up GHA to run the non-mongo tests with a single version of mongo and run the + * mong tests with matrixed mongo versions. Combine coverage at the end somehow + */ + forkEvery = 1 + systemProperty "AUTH2_TEST_CONFIG", "./test.cfg" + testLogging { + exceptionFormat = 'full' + showStandardStreams = true + } + // TODO NOW coverage working? +} + +// TODO NOW make auth, test, and template jars +// TODO NOW make script + +// Custom java project layout +sourceSets { + main { + java { + srcDirs = ["src"] + exclude '**/test/**' + } + } + test { + java { + srcDirs = ["src"] + include '**/test/**' + } + resources { + srcDirs = ["src"] + include "**/*.testdata" + include "**/authjars" + } + } +} + +war { + webXml = file('war/web.xml') + // TODO NOW GRADLE probably needs updates +} + +def fromURL = { url, name -> + File file = new File("$buildDir/download/${name}.jar") + file.parentFile.mkdirs() + if (!file.exists()) { + new URL(url).withInputStream { downloadStream -> + file.withOutputStream { fileOut -> + fileOut << downloadStream + } + } + } + files(file.absolutePath) +} + +dependencies { + + // ### General application dependencies ### + + implementation 'commons-codec:commons-codec:1.8' + implementation 'commons-validator:commons-validator:1.5.1' + implementation 'com.google.guava:guava:18.0' + implementation 'org.ini4j:ini4j:0.5.2' + implementation 'com.beust:jcommander:1.48' + implementation 'org.mongodb:mongodb-driver-core:4.11.1' + implementation 'org.mongodb:mongodb-driver-sync:4.11.1' + implementation 'org.mongodb:bson-record-codec:4.11.1' + implementation 'org.mongodb:bson:4.11.1' + implementation 'com.github.spullara.mustache.java:compiler:0.9.3' + implementation 'com.nulab-inc:zxcvbn:1.2.2' + implementation 'nl.basjes.parse.useragent:yauaa:1.3' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.5.4' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.5.4' + implementation 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.5.4' + implementation 'com.github.zafarkhaja:java-semver:0.9.0' + implementation 'org.glassfish.jersey.containers:jersey-container-servlet:2.23.2' + implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2' + implementation 'org.glassfish.jersey.ext:jersey-mvc-mustache:2.23.2' + implementation 'javax.persistence:persistence-api:1.0' + implementation 'javax.servlet:javax.servlet-api:3.0.1' + implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' + + + // ### Logging dependencies ### + implementation fromURL( + 'https://github.com/kbase/jars/raw/master/lib/jars/kbase/common/kbase-common-0.2.0.jar', + 'kbase-common-0.2.0' + ) + implementation 'ch.qos.logback:logback-classic:1.1.2' + implementation 'org.slf4j:slf4j-api:1.7.25' + // Syslog4j 0.9.46 doesn't appear to be available on Maven. It apparently lives in + // a JetBrains artifact server, but that's too much trouble and there's only one version there + // anyway. + // https://mvnrepository.com/artifact/org.jetbrains/syslog4j/0.9.46 + // Need to rework the java common logger to not use syslog4j at all since it's abandonware + // and has a ton of CVEs, even in the newer versions. + implementation fromURL( + 'https://github.com/kbase/jars/raw/master/lib/jars/syslog4j/syslog4j-0.9.46.jar', + 'syslog4j-0.9.46' + ) + // needed for syslog + implementation 'net.java.dev.jna:jna:3.4.0' + + // ### Test ### + + testImplementation 'commons-io:commons-io:2.4' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.1.10' + testImplementation 'junit:junit:4.12' + testImplementation 'org.mock-server:mockserver-netty:3.10.4' + testImplementation 'org.eclipse.jetty:jetty-server:9.3.11.v20160721' + testImplementation 'org.eclipse.jetty:jetty-servlet:9.3.11.v20160721' + testImplementation 'io.github.java-diff-utils:java-diff-utils:2.2.0' + testImplementation 'de.danielbechler:java-object-diff:0.94' + testImplementation 'org.jsoup:jsoup:1.10.2' + testImplementation 'org.mockito:mockito-core:3.0.0' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a5952066 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..8a19017a --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.4.2/userguide/multi_project_builds.html + */ + +rootProject.name = 'auth2' From f140d30b6778d46b955e3298a16357e1082afafa Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 19 Jan 2024 14:11:17 -0800 Subject: [PATCH 07/25] Try and fix coverage --- .codecov.yml | 1 - build.gradle | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.codecov.yml b/.codecov.yml index 42c0d62b..b75b4f7a 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -26,5 +26,4 @@ comment: require_changes: no ignore: - - "build" - "deployment" diff --git a/build.gradle b/build.gradle index ee1cd366..13084a21 100644 --- a/build.gradle +++ b/build.gradle @@ -46,9 +46,19 @@ test { exceptionFormat = 'full' showStandardStreams = true } + finalizedBy jacocoTestReport // TODO NOW coverage working? } +jacocoTestReport { + reports { + xml.required = true + csv.required = true + } +} + + +// TODO NOW run tests from Eclipse w/o specifying classpath manually // TODO NOW make auth, test, and template jars // TODO NOW make script From 9df0d13379765726e2fd6fabca46bed964f44822 Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 19 Jan 2024 16:29:42 -0800 Subject: [PATCH 08/25] Handle creating git commit file --- build.gradle | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 13084a21..326ed197 100644 --- a/build.gradle +++ b/build.gradle @@ -10,13 +10,22 @@ plugins { id 'java' id 'war' id 'jacoco' + id 'org.ajoberstar.grgit' version '4.1.1' } repositories { mavenCentral() } +task getGitCommitId { + doLast { + def commitId = grgit.head().id + file('src/us/kbase/auth2/gitcommit').text = commitId + } +} + compileJava { + dependsOn getGitCommitId if (JavaVersion.current() <= JavaVersion.VERSION_1_8) { // TODO BUILD remove when we no longer support java 8 java.sourceCompatibility = JavaVersion.VERSION_1_8 @@ -25,7 +34,6 @@ compileJava { options.release = 8 } // TODO NOW build in git commit - // TODO NOW javadocs } test { @@ -35,19 +43,18 @@ test { * require it * Although it seems to make Mongo start up correctly as well which is odd */ + forkEvery = 1 /* * TODO TEST split tests into mongo wrapper tests & all other tests (incl. integration). * Set up GHA to run the non-mongo tests with a single version of mongo and run the * mong tests with matrixed mongo versions. Combine coverage at the end somehow */ - forkEvery = 1 systemProperty "AUTH2_TEST_CONFIG", "./test.cfg" testLogging { exceptionFormat = 'full' showStandardStreams = true } finalizedBy jacocoTestReport - // TODO NOW coverage working? } jacocoTestReport { @@ -57,10 +64,11 @@ jacocoTestReport { } } - +// TODO NOW javadocs // TODO NOW run tests from Eclipse w/o specifying classpath manually // TODO NOW make auth, test, and template jars // TODO NOW make script +// TODO NOW update Docker to move warfile to correct place // Custom java project layout sourceSets { @@ -69,6 +77,10 @@ sourceSets { srcDirs = ["src"] exclude '**/test/**' } + resources { + srcDirs = ["src"] + include "**/gitcommit" + } } test { java { @@ -85,7 +97,6 @@ sourceSets { war { webXml = file('war/web.xml') - // TODO NOW GRADLE probably needs updates } def fromURL = { url, name -> From 8f8527a85c4f4a738098690180ae4604ebb003b9 Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 19 Jan 2024 18:08:20 -0800 Subject: [PATCH 09/25] Build server with gradle w/ ant --- Dockerfile | 7 +++---- README.md | 27 ++++++++++++++++++--------- build.gradle | 5 +++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 82676aa7..aab59932 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,7 @@ FROM kbase/sdkbase2 as build COPY . /tmp/auth2 -RUN cd /tmp \ - && git clone https://github.com/kbase/jars \ - && cd auth2 \ - && ant buildwar +RUN cd /tmp/auth2 && ./gradlew war FROM kbase/kb_jre:latest @@ -15,6 +12,8 @@ ARG BRANCH=develop COPY --from=build /tmp/auth2/deployment/ /kb/deployment/ COPY --from=build /tmp/auth2/jettybase/ /kb/deployment/jettybase/ +COPY --from=build /tmp/auth2/build/libs/auth2.war /kb/deployment/jettybase/webapps/ROOT.war +COPY --from=build /tmp/auth2/templates /kb/deployment/jettybase/templates # The BUILD_DATE value seem to bust the docker cache when the timestamp changes, move to # the end diff --git a/README.md b/README.md index b1f266cb..1f33d7e8 100644 --- a/README.md +++ b/README.md @@ -235,24 +235,33 @@ Removes all test mode data from the system. ## Requirements Java 8 (OpenJDK OK) -Apache Ant (http://ant.apache.org/) MongoDB 2.6+ (https://www.mongodb.com/) Jetty 9.3+ (http://www.eclipse.org/jetty/download.html) (see jetty-config.md for version used for testing) This repo (git clone https://github.com/kbase/auth2) -The jars repo (git clone https://github.com/kbase/jars) -The two repos above need to be in the same parent folder. ## To start server start mongodb if using mongo auth, create a mongo user -cd into the auth2 repo -`ant build` -copy `deploy.cfg.example` to `deploy.cfg` and fill in appropriately -`export KB_DEPLOYMENT_CONFIG=` -`cd jettybase` -`./jettybase$ java -jar -Djetty.port= /start.jar` +cd into the auth2 repo + +``` +./gradlew war +mkdir -p jettybase/webapps +cp build/libs/auth2.war jettybase/webapps/ROOT.war +cp templates jettybase/templates +``` + +copy `deploy.cfg.example` to `deploy.cfg` and fill in appropriately + +``` +export KB_DEPLOYMENT_CONFIG= +cd jettybase +./jettybase$ java -jar -Djetty.port= /start.jar +``` + +Or just use the Docker build, which is probably a lot easier. ## Administer the server diff --git a/build.gradle b/build.gradle index 326ed197..d6699426 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,6 @@ compileJava { } else { options.release = 8 } - // TODO NOW build in git commit } test { @@ -68,7 +67,7 @@ jacocoTestReport { // TODO NOW run tests from Eclipse w/o specifying classpath manually // TODO NOW make auth, test, and template jars // TODO NOW make script -// TODO NOW update Docker to move warfile to correct place +// TODO NOW test server starts in docker container, including templates when gradle done // Custom java project layout sourceSets { @@ -97,6 +96,8 @@ sourceSets { war { webXml = file('war/web.xml') + // For some reason the test directories are included in the war file + // although the files are gone. Not sure why } def fromURL = { url, name -> From f983686b48d33b1a021a0cb8d7e4fa5b5562110f Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 24 Jan 2024 16:33:50 -0800 Subject: [PATCH 10/25] Build a fat test jar with dependencies and templates --- build.gradle | 99 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index d6699426..bdc6a0fd 100644 --- a/build.gradle +++ b/build.gradle @@ -16,12 +16,15 @@ plugins { repositories { mavenCentral() } +// Warning - these values are hard coded in AuthController.java +def JAR_TEMPLATE_DIR = 'kbase_auth2_templates' +def TEMPLATE_LIST_FILE_NAME = "templateFileList.txt" task getGitCommitId { - doLast { - def commitId = grgit.head().id - file('src/us/kbase/auth2/gitcommit').text = commitId - } + doLast { + def commitId = grgit.head().id + file('src/us/kbase/auth2/gitcommit').text = commitId + } } compileJava { @@ -46,7 +49,7 @@ test { /* * TODO TEST split tests into mongo wrapper tests & all other tests (incl. integration). * Set up GHA to run the non-mongo tests with a single version of mongo and run the - * mong tests with matrixed mongo versions. Combine coverage at the end somehow + * mongo tests with matrixed mongo versions. Combine coverage at the end somehow */ systemProperty "AUTH2_TEST_CONFIG", "./test.cfg" testLogging { @@ -56,6 +59,8 @@ test { finalizedBy jacocoTestReport } +// TODO TEST add a test that starts the server in a docker container and checks some simple cmds + jacocoTestReport { reports { xml.required = true @@ -68,6 +73,9 @@ jacocoTestReport { // TODO NOW make auth, test, and template jars // TODO NOW make script // TODO NOW test server starts in docker container, including templates when gradle done +// TODO NOW make test server for workspace etc. work w/o jars repo (testwar w/ templates incl.?) +// https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file +// https://stackoverflow.com/questions/28145187/test-if-application-is-running-in-a-jar-file-in-java // Custom java project layout sourceSets { @@ -100,6 +108,47 @@ war { // although the files are gone. Not sure why } +configurations { + // can't directly access testImplementation, so extend and access + testimpl.extendsFrom testImplementation +} + +task generateTemplateFileList { + doLast { + File directory = file('templates') + + // List files in the directory + def files = fileTree(dir: directory).files.collect { it.name } + + File outputFile = file("$buildDir/" + TEMPLATE_LIST_FILE_NAME) + outputFile.text = files.join('\n') + } +} + + +task fatTestJar(type: Jar) { + // Be careful when updating jars - you may want to set the duplicates strategy to WARN + // to see if any of the jars are shadowing the others when building the fat jar, which + // has been the case in the past + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + dependsOn generateTemplateFileList + archiveAppendix = 'test-fat' + + // include source files + from sourceSets.test.output + + // include all jars + from { + configurations.testimpl.collect { it.isDirectory() ? it : zipTree(it) } + } + + // Include text files from the "templates" directory + from('templates') { into JAR_TEMPLATE_DIR } + from("$buildDir/" + TEMPLATE_LIST_FILE_NAME) { into JAR_TEMPLATE_DIR } + + with jar +} + def fromURL = { url, name -> File file = new File("$buildDir/download/${name}.jar") file.parentFile.mkdirs() @@ -116,6 +165,15 @@ def fromURL = { url, name -> dependencies { // ### General application dependencies ### + + /* Notes on exclusions: + * Bizarrely, the glassfish verison of inject has a dependency on v1 inject, which + * causes problems when trying to build the fat jar + * There are other spots in the dependency tree where v1 inject exists as well, and + * collides with the newer version. + * Inject v1 has the exact same directories and classes as the 2.5.0-b05 and so is + * shadowed in any case, so removal is presumably safe since the tests pass. + */ implementation 'commons-codec:commons-codec:1.8' implementation 'commons-validator:commons-validator:1.5.1' @@ -133,11 +191,21 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.5.4' implementation 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.5.4' implementation 'com.github.zafarkhaja:java-semver:0.9.0' - implementation 'org.glassfish.jersey.containers:jersey-container-servlet:2.23.2' - implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2' - implementation 'org.glassfish.jersey.ext:jersey-mvc-mustache:2.23.2' + implementation('org.glassfish.jersey.containers:jersey-container-servlet:2.23.2') { + exclude group: 'javax.inject', module: 'javax.inject' + } + implementation('org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2') { + exclude group: 'javax.inject', module: 'javax.inject' + } + implementation('org.glassfish.jersey.ext:jersey-mvc-mustache:2.23.2') { + exclude group: 'javax.inject', module: 'javax.inject' + // The servlet namespace changed between 2.4 and 3.X, and so we need to exclude the + // 2.4 namespace to avoid collisions. 3.X is backwards compatible: + // https://docs.oracle.com/cd/E19798-01/821-1752/beagj/index.html + exclude group: 'javax.servlet', module: 'servlet-api' + } implementation 'javax.persistence:persistence-api:1.0' - implementation 'javax.servlet:javax.servlet-api:3.0.1' + implementation 'javax.servlet:javax.servlet-api:3.1.0' implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' @@ -166,7 +234,12 @@ dependencies { testImplementation 'commons-io:commons-io:2.4' testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.1.10' testImplementation 'junit:junit:4.12' - testImplementation 'org.mock-server:mockserver-netty:3.10.4' + testImplementation('org.mock-server:mockserver-netty:3.10.4') { + exclude group: 'javax.inject', module: 'javax.inject' + // uses an old version of the activation api in a different namespace, so exclude it + // in favor of the new one which is a transitive dependency elsewhere + exclude group: 'javax.activation', module: 'activation' + } testImplementation 'org.eclipse.jetty:jetty-server:9.3.11.v20160721' testImplementation 'org.eclipse.jetty:jetty-servlet:9.3.11.v20160721' testImplementation 'io.github.java-diff-utils:java-diff-utils:2.2.0' @@ -174,3 +247,9 @@ dependencies { testImplementation 'org.jsoup:jsoup:1.10.2' testImplementation 'org.mockito:mockito-core:3.0.0' } + +task showTestClassPath { + doLast { + configurations.testimpl.each { println it } + } +} From 87244ab459c0bf841a7d744a35e1ef195bfa6f24 Mon Sep 17 00:00:00 2001 From: Gavin Date: Tue, 6 Feb 2024 19:09:07 -0800 Subject: [PATCH 11/25] Get fat test jar working for test support Tested by replacing the old auth jar in workspace deluxe with the fat jar --- build.gradle | 7 +- .../auth2/authcontroller/AuthController.java | 127 ++++++------------ .../kbase/test/auth2/authcontroller/authjars | 74 ---------- 3 files changed, 41 insertions(+), 167 deletions(-) delete mode 100644 src/us/kbase/test/auth2/authcontroller/authjars diff --git a/build.gradle b/build.gradle index bdc6a0fd..a3898398 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ repositories { } // Warning - these values are hard coded in AuthController.java def JAR_TEMPLATE_DIR = 'kbase_auth2_templates' -def TEMPLATE_LIST_FILE_NAME = "templateFileList.txt" +def TEMPLATE_LIST_FILE_NAME = "templates.manifest" task getGitCommitId { doLast { @@ -70,12 +70,8 @@ jacocoTestReport { // TODO NOW javadocs // TODO NOW run tests from Eclipse w/o specifying classpath manually -// TODO NOW make auth, test, and template jars // TODO NOW make script // TODO NOW test server starts in docker container, including templates when gradle done -// TODO NOW make test server for workspace etc. work w/o jars repo (testwar w/ templates incl.?) -// https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file -// https://stackoverflow.com/questions/28145187/test-if-application-is-running-in-a-jar-file-in-java // Custom java project layout sourceSets { @@ -97,7 +93,6 @@ sourceSets { resources { srcDirs = ["src"] include "**/*.testdata" - include "**/authjars" } } } diff --git a/src/us/kbase/test/auth2/authcontroller/AuthController.java b/src/us/kbase/test/auth2/authcontroller/AuthController.java index 556ffeee..8588c6e7 100644 --- a/src/us/kbase/test/auth2/authcontroller/AuthController.java +++ b/src/us/kbase/test/auth2/authcontroller/AuthController.java @@ -17,17 +17,10 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Scanner; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -37,10 +30,18 @@ import us.kbase.common.test.TestException; import us.kbase.test.auth2.StandaloneAuthServer; +/** Q&D utility to run the auth server in test mode for use in testing rigs. Expected to + * be packaged in a test jar with all dependencies and the templates in a /templates/ directory. + */ public class AuthController { - private final static String AUTH_CLASS = StandaloneAuthServer.class.getName(); - private static final String JARS_FILE = "authjars"; + // hardcoded in build.gradle + private static final String TEMPLATES_JAR_DIR = "kbase_auth2_templates"; + private static final String TEMPLATES_LIST_FILE = "templates.manifest"; + + private static final String AUTH_CLASS = StandaloneAuthServer.class.getName(); + private static final String JAR_PATH = StandaloneAuthServer.class.getProtectionDomain() + .getCodeSource().getLocation().getPath(); private final Process auth; private final int port; @@ -48,16 +49,14 @@ public class AuthController { private final String version; public AuthController( - final Path jarsDir, final String mongoHost, final String mongoDatabase, final Path rootTempDir) throws Exception { - this(jarsDir, mongoHost, mongoDatabase, rootTempDir, null, null); + this(mongoHost, mongoDatabase, rootTempDir, null, null); } public AuthController( - final Path jarsDir, final String mongoHost, final String mongoDatabase, final Path rootTempDir, @@ -67,16 +66,16 @@ public AuthController( if (mongoUser == null ^ mongoPwd == null) { throw new TestException("Both or neither of the mongo user / pwd must be provided"); } - final String classPath = getClassPath(jarsDir); tempDir = makeTempDirs(rootTempDir, "AuthController-", Arrays.asList("templates")); port = findFreePort(); + System.out.println("Using classpath " + JAR_PATH); final Path templateDir = tempDir.resolve("templates"); - installTemplates(jarsDir, templateDir); + installTemplates(templateDir); final List command = new ArrayList<>(Arrays.asList( "java", - "-classpath", classPath, + "-classpath", JAR_PATH, "-D" + MONGO_HOST_KEY + "=" + mongoHost, "-D" + MONGO_DB_KEY + "=" + mongoDatabase, "-D" + MONGO_TEMPLATES_KEY + "=" + templateDir.toString())); @@ -149,83 +148,37 @@ public void destroy(boolean deleteTempFiles) throws IOException { } } - private void installTemplates(final Path jarsDir, final Path templatesDir) throws IOException { - final Path templateZipFile; - try (final InputStream is = getJarsFileInputStream()) { - final BufferedReader br = new BufferedReader(new InputStreamReader(is)); - //first line is zip file with templates - templateZipFile = jarsDir.resolve(br.readLine().trim()); - } - try (final ZipFile zf = new ZipFile(templateZipFile.toFile())) { - for (Enumeration e = zf.entries(); e.hasMoreElements();) { - final ZipEntry ze = e.nextElement(); - if (ze.isDirectory()) { - continue; - } - final Path zippath = Paths.get(ze.getName()).normalize(); - if (zippath.isAbsolute() || zippath.startsWith("..")) { - throw new TestException("Zip file " + templateZipFile + - " contains files outside the zip " + - "directory - this is a sign of a malicious zip file."); - } - final Path file = templatesDir.resolve(zippath).toAbsolutePath(); - Files.createDirectories(file.getParent()); - Files.createFile(file); - try (final OutputStream os = Files.newOutputStream(file); - final InputStream zipinput = zf.getInputStream(ze)) { - IOUtils.copy(zipinput, os); - } + private void installTemplates(final Path templatesDir) throws IOException { + final String templatesJarFileList = "/" + TEMPLATES_JAR_DIR + "/" + TEMPLATES_LIST_FILE; + final List templateFiles = new ArrayList<>(); + try (final InputStream templateFilesStream = getClass() + .getResourceAsStream(templatesJarFileList) + ) { + if (templateFilesStream == null) { + throw new TestException(String.format( + "Could not find template file list file %s in jar", templatesJarFileList)); } - } catch (ZipException e) { - throw new TestException("Unable to open the zip file " + templateZipFile, e); + final BufferedReader br = new BufferedReader( + new InputStreamReader(templateFilesStream)); + br.lines().forEach(templateFile -> templateFiles.add(templateFile)); } - - } - - private String getClassPath(final Path jarsDir) throws IOException { - final List classpath = new LinkedList<>(); - try (final InputStream is = getJarsFileInputStream();) { - final BufferedReader br = new BufferedReader(new InputStreamReader(is)); - br.readLine(); // discard first line, which has the templates zip file - String line; - while ((line = br.readLine()) != null) { - if (!line.trim().isEmpty() && !line.trim().startsWith("#")) { - final Path jarPath = jarsDir.resolve(line); - if (Files.notExists(jarPath)) { - throw new TestException("Required jar does not exist: " + jarPath); - - } - classpath.add(jarPath.toString()); + for (final String templateFileName: templateFiles) { + final String templateJarPath = "/" + TEMPLATES_JAR_DIR + "/" + templateFileName; + final Path templateTargetPath = templatesDir.resolve(templateFileName) + .toAbsolutePath(); + try ( + final InputStream template = getClass() + .getResourceAsStream(templateJarPath); + final OutputStream target = Files.newOutputStream(templateTargetPath); + + ) { + if (template == null) { + throw new TestException(String.format( + "Could not find template file %s in jar", templateJarPath)); } + IOUtils.copy(template, target); } } - return String.join(":", classpath); - } - - private InputStream getJarsFileInputStream() { - final InputStream is = getClass().getResourceAsStream(JARS_FILE); - if (is == null) { - throw new TestException("No auth versions file " + JARS_FILE); - } - return is; - } - - public static void main(final String[] args) throws Exception { - final AuthController ac = new AuthController( - Paths.get("/home/crushingismybusiness/github/mrcreosote/jars/lib/jars"), - "localhost:27017", - "AuthController", - Paths.get("authtesttemp"), - "auth", - "auth"); - System.out.println(ac.getServerPort()); - System.out.println(ac.getTempDir()); - System.out.println(ac.getVersion()); - Scanner reader = new Scanner(System.in); - System.out.println("any char to shut down"); - reader.next(); - ac.destroy(false); - reader.close(); } } diff --git a/src/us/kbase/test/auth2/authcontroller/authjars b/src/us/kbase/test/auth2/authcontroller/authjars deleted file mode 100644 index c79b8d68..00000000 --- a/src/us/kbase/test/auth2/authcontroller/authjars +++ /dev/null @@ -1,74 +0,0 @@ -kbase/auth2/kbase-auth2templates-0.4.3.zip - -kbase/auth2/kbase-auth2-0.4.3.jar -kbase/auth2/kbase-auth2test-0.4.3.jar - -#lib -apache_commons/commons-codec-1.8.jar -apache_commons/commons-validator-1.5.1.jar -google/guava-18.0.jar -ini4j/ini4j-0.5.2.jar -jcommander/jcommander-1.48.jar -mongo/mongo-java-driver-3.8.2.jar -mustache/compiler-0.9.3.jar -nulab-inc/zxcvbn-1.2.2.jar - -#logging -kbase/common/kbase-common-0.0.22.jar -jna/jna-3.4.0.jar -logback/logback-core-1.1.2.jar -logback/logback-classic-1.1.2.jar -slf4j/slf4j-api-1.7.25.jar -syslog4j/syslog4j-0.9.46.jar - -#yauaa -yauaa/yauaa-1.3.jar -apache_commons/commons-lang3-3.5.jar -apache_commons/commons-collections4-4.1.jar -apache_commons/commons-logging-1.2.jar -apache_commons/commons-io-2.4.jar -kohsuke/args4j-2.33.jar -snakeyaml/snakeyaml-1.18.jar - -#jackson -jackson/jackson-annotations-2.5.4.jar -jackson/jackson-core-2.5.4.jar -jackson/jackson-databind-2.5.4.jar -jackson/jackson-jaxrs-base-2.5.4.jar -jackson/jackson-jaxrs-json-provider-2.5.4.jar -jackson/jackson-module-jaxb-annotations-2.5.4.jar - -#jersey -jersey/entity-filtering/jersey-entity-filtering-2.23.2.jar -jersey/entity-filtering/jersey-media-json-jackson-2.23.2.jar -jersey/mvc/jersey-mvc-2.23.2.jar -jersey/mvc/jersey-mvc-mustache-2.23.2.jar -jersey/jersey-client-2.23.2.jar -jersey/jersey-common-2.23.2.jar -jersey/jersey-container-servlet-2.23.2.jar -jersey/jersey-container-servlet-core-2.23.2.jar -jersey/jersey-guava-2.23.2.jar -jersey/jersey-media-jaxb-2.23.2.jar -jersey/jersey-server-2.23.2.jar - -#jerseydeps -annotation/javax.annotation-api-1.2.jar -asm/asm-debug-all-5.0.4.jar -inject/javax.inject-2.5.0-b05.jar -javassist/javassist-3.20.0-GA.jar -jaxb/jaxb-api-2.2.7.jar -jaxrs/javax.ws.rs-api-2.0.1.jar -osgi/org.osgi.core-4.2.0.jar -persistence/persistence-api-1.0.jar -servlet/javax.servlet-api-3.0.1.jar -validationapi/validation-api-1.1.0.Final.jar - -#jerseydep_hk2 -hk2/aopalliance-repackaged-2.5.0-b05.jar -hk2/hk2-api-2.5.0-b05.jar -hk2/hk2-locator-2.5.0-b05.jar -hk2/hk2-utils-2.5.0-b05.jar -hk2/osgi-resource-locator-1.0.1.jar - -#test -jetty/jetty-all-9.3.11.v20160721-uber.jar From 7b38eb08b073e2890099a3ba10b063a2b6d338bf Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 7 Feb 2024 15:25:04 -0800 Subject: [PATCH 12/25] Get javadoc working --- build.gradle | 14 +++++++++++--- src/us/kbase/auth2/lib/Authentication.java | 12 ++++++------ src/us/kbase/auth2/lib/DisplayName.java | 1 + src/us/kbase/auth2/lib/OAuth2StartData.java | 1 + src/us/kbase/auth2/lib/UserSearchSpec.java | 2 +- .../kbase/auth2/lib/identity/IdentityProvider.java | 2 +- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index a3898398..2825e320 100644 --- a/build.gradle +++ b/build.gradle @@ -68,10 +68,9 @@ jacocoTestReport { } } -// TODO NOW javadocs // TODO NOW run tests from Eclipse w/o specifying classpath manually // TODO NOW make script -// TODO NOW test server starts in docker container, including templates when gradle done +// TODO NOW test server starts in docker container via compose, including templates when gradle done // Custom java project layout sourceSets { @@ -97,6 +96,14 @@ sourceSets { } } +javadoc { + options { + links "https://mongodb.github.io/mongo-java-driver/4.11/apidocs/mongodb-driver-sync/" + // I don't know why this isn't working, but it's not worth spending time on right now + links "https://docs.oracle.com/javase/8/docs/api/" + } +} + war { webXml = file('war/web.xml') // For some reason the test directories are included in the war file @@ -221,8 +228,9 @@ dependencies { 'https://github.com/kbase/jars/raw/master/lib/jars/syslog4j/syslog4j-0.9.46.jar', 'syslog4j-0.9.46' ) - // needed for syslog + // needed for syslog4j implementation 'net.java.dev.jna:jna:3.4.0' + implementation 'joda-time:joda-time:2.3' // ### Test ### diff --git a/src/us/kbase/auth2/lib/Authentication.java b/src/us/kbase/auth2/lib/Authentication.java index 59b5104b..93ba621d 100644 --- a/src/us/kbase/auth2/lib/Authentication.java +++ b/src/us/kbase/auth2/lib/Authentication.java @@ -1659,7 +1659,7 @@ private IdentityProvider getIdentityProvider(final String provider) /** Start the OAuth2 login process. * @param lifetimeSec the lifetime of the temporary token to be returned. * @param provider the name of the 3rd party identity provider to login with. - * @param string the auth environment in which the login is taking place, or null for the + * @param environment the auth environment in which the login is taking place, or null for the * default environment. * @return data to start the login process. * @throws AuthStorageException if an error occurs accessing the storage system. @@ -1718,7 +1718,7 @@ private String generatePKCECodeChallenge(final String codeVerifier) { * @return A login token. In this case the token will always be a temporary token so the * control flow can be returned to the UI before returning an error. * @throws AuthStorageException if an error occurred accessing the storage system. - * @see #login(String, String, String, TokenCreationContext) + * @see #login(IncomingToken, String, String, String, TokenCreationContext, String) */ public LoginToken loginProviderError(final String providerError) throws AuthStorageException { checkStringNoCheckedException(providerError, "providerError"); @@ -1845,7 +1845,7 @@ private TemporaryToken storeTemporarySessionData(final TemporarySessionData data /** Get the current state of a login process associated with a temporary token. * This method is expected to be called after - * {@link #login(String, String, String, TokenCreationContext)}. + * {@link #login(IncomingToken, String, String, String, TokenCreationContext, String)}. * After user interaction is completed, a new user can be created via * {@link #createUser(IncomingToken, String, UserName, DisplayName, EmailAddress, Set, TokenCreationContext, boolean)} * or the login can complete via @@ -2433,7 +2433,7 @@ public OAuth2StartData linkStart( * @param providerError the error returned by the provider. * @return A temporary token. * @throws AuthStorageException if an error occurred accessing the storage system. - * @see #linkStart(IncomingToken, int) + * @see #link(IncomingToken, String, String, String, String) */ public TemporaryToken linkProviderError(final String providerError) throws AuthStorageException { @@ -2476,7 +2476,7 @@ private TemporaryToken storeErrorTemporarily( * to a new URL that does not contain said authcode as soon as possible. * * @param token the user's temporary token acquired from - * {@link #linkStart(IncomingToken, int)}. + * {@link #linkStart(IncomingToken, int, String, String)} * @param provider the name of the identity provider that is servicing the link request. * @param authcode the authcode provided by the provider. * @param environment the environment in which the link request is proceeding. Null for the @@ -2604,7 +2604,7 @@ private void filterLinkCandidates(final Set rids) /** Get the current state of a linking process associated with a temporary token. * This method is expected to be called after - * {@link #link(IncomingToken, String, String, String)}. + * {@link #link(IncomingToken, String, String, String, String)}. * After user interaction is completed, the link can be completed by calling * {@link #link(IncomingToken, IncomingToken, String)} or * {@link #linkAll(IncomingToken, IncomingToken)}. diff --git a/src/us/kbase/auth2/lib/DisplayName.java b/src/us/kbase/auth2/lib/DisplayName.java index 69b8f4fe..a6e3e4eb 100644 --- a/src/us/kbase/auth2/lib/DisplayName.java +++ b/src/us/kbase/auth2/lib/DisplayName.java @@ -34,6 +34,7 @@ public DisplayName(final String name) /** Get the canonical display name for a string. Returns a list of the whitespace and hyphen * separated tokens in the name. The tokens are lowercased, punctuation in the token is * removed, and non-unique elements are removed. + * @param name the string to analyze. * @return the canonical display name. */ public static List getCanonicalDisplayName(final String name) { diff --git a/src/us/kbase/auth2/lib/OAuth2StartData.java b/src/us/kbase/auth2/lib/OAuth2StartData.java index 43f30389..8973a285 100644 --- a/src/us/kbase/auth2/lib/OAuth2StartData.java +++ b/src/us/kbase/auth2/lib/OAuth2StartData.java @@ -24,6 +24,7 @@ private OAuth2StartData(final URI redirectURI, final TemporaryToken temporaryTok /** Create the OAuth data. * @param redirectURI the 3rd party redirect URI. * @param tempToken the temporary token to provide to the user to track them through the flow. + * @return the OAuth2 data. */ public static OAuth2StartData build(final URI redirectURI, final TemporaryToken tempToken) { return new OAuth2StartData(redirectURI, tempToken); diff --git a/src/us/kbase/auth2/lib/UserSearchSpec.java b/src/us/kbase/auth2/lib/UserSearchSpec.java index deaa5ca3..613fe483 100644 --- a/src/us/kbase/auth2/lib/UserSearchSpec.java +++ b/src/us/kbase/auth2/lib/UserSearchSpec.java @@ -77,7 +77,7 @@ public boolean hasSearchRegex() { } /** Returns true if the search prefixes are set. This means the search regex is not set. - * @ return true if the search prefixes are set. + * @return true if the search prefixes are set. */ public boolean hasSearchPrefixes() { return prefixes != null; diff --git a/src/us/kbase/auth2/lib/identity/IdentityProvider.java b/src/us/kbase/auth2/lib/identity/IdentityProvider.java index 7085b975..59d34eec 100644 --- a/src/us/kbase/auth2/lib/identity/IdentityProvider.java +++ b/src/us/kbase/auth2/lib/identity/IdentityProvider.java @@ -54,7 +54,7 @@ Set getIdentities( throws IdentityRetrievalException, NoSuchEnvironmentException; /** Get the names of the additional environments beyond the default environment that are - * configured. See {@link #getLoginURI(String, boolean, String)}. + * configured. See {@link #getLoginURI(String, String, boolean, String)}. * @return the environments. */ Set getEnvironments(); From a02ab2f4b107b8e939693db3552d87f1f0bd5f41 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 7 Feb 2024 16:36:05 -0800 Subject: [PATCH 13/25] Make docker-compose start the service & test Service responds to testmode endpoints that access the db, namely GET customroles Also split the build into dependencies and compile so the dependencies are cached --- Dockerfile | 24 ++++++++++++++++++++++-- build.gradle | 1 - docker-compose.yml | 35 +++++++++-------------------------- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index aab59932..90e65fea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,23 @@ FROM kbase/sdkbase2 as build -COPY . /tmp/auth2 -RUN cd /tmp/auth2 && ./gradlew war +WORKDIR /tmp/auth2 + +# dependencies take a while to D/L, so D/L & cache before the build so code changes don't cause +# a new D/L +# can't glob *gradle because of the .gradle dir +COPY build.gradle gradlew settings.gradle /tmp/auth2/ +COPY gradle/ /tmp/auth2/gradle/ +RUN ./gradlew dependencies + +# Now build the code +COPY deployment/ /tmp/auth2/deployment/ +COPY jettybase/ /tmp/auth2/jettybase/ +COPY src /tmp/auth2/src/ +COPY templates /tmp/auth2/templates/ +COPY war /tmp/auth2/war/ +# for the git commit +COPY .git /tmp/auth2/.git/ +RUN ./gradlew war FROM kbase/kb_jre:latest @@ -27,6 +43,10 @@ LABEL org.label-schema.build-date=$BUILD_DATE \ WORKDIR /kb/deployment/jettybase ENV KB_DEPLOYMENT_CONFIG=/kb/deployment/conf/deployment.cfg +# TODO BUILD update to no longer use dockerize and take env vars (e.g. like Collections). +# TODO BUILD figure out how to add multiple environments as env vars (multiline env vars in rancher?) +# TODO BUILD Use subsections in the ini file / switch to TOML + ENTRYPOINT [ "/kb/deployment/bin/dockerize" ] # Here are some default params passed to dockerize. They would typically diff --git a/build.gradle b/build.gradle index 2825e320..ba0498ab 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,6 @@ jacocoTestReport { // TODO NOW run tests from Eclipse w/o specifying classpath manually // TODO NOW make script -// TODO NOW test server starts in docker container via compose, including templates when gradle done // Custom java project layout sourceSets { diff --git a/docker-compose.yml b/docker-compose.yml index e090887c..9280439e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,16 +5,17 @@ version: "3.1" # that is started up and polled services: auth2: - image: kbase/kb_auth2:latest + build: + context: . + dockerfile: Dockerfile ports: - "8080:8080" environment: - - KB_DEPLOYMENT_CONFIG=/kb/deployment/conf/deployment.cfg + mongo_host: "mongo:27017" + test_mode_enabled: "true" command: - "-wait" - - "tcp://ci-mongo:27017" - - "-wait" - - "tcp://mongoinit:8080" + - "tcp://mongo:27017" - "-timeout" - "120s" - "-template" @@ -35,28 +36,10 @@ services: # you can add the following flag: # - "-validateCert=false" - "/kb/deployment/bin/start_auth2.sh" - depends_on: ["ci-mongo", "mongoinit"] + depends_on: ["mongo"] - mongoinit: - image: kbase/db_initialize:latest - volumes: - - ./auth2.mongodump:/mnt/auth2.mongodump - entrypoint: - - "/kb/deployment/bin/dockerize.sh" - - "-wait" - - "tcp://ci-mongo:27017" - - "-timeout" - - "120s" - - "mongorestore" - - "--host" - - "ci-mongo" - - "/mnt/auth2.mongodump" - depends_on: [ "ci-mongo" ] - - ci-mongo: - image: mongo:2 - command: - - "--smallfiles" + mongo: + image: mongo:7 ports: - "27017:27017" From 3a15bfc501f0476a9d4cf78f265a554b2ba5b012 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 7 Feb 2024 17:30:33 -0800 Subject: [PATCH 14/25] Create the manage_auth script --- README.md | 16 +++++++++++----- build.gradle | 22 +++++++++++++++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1f33d7e8..43eb77a8 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,8 @@ Removes all test mode data from the system. * Note that only the public API has been tested with the auth server. * In version 0.6.0, the canonicalization algorithm for user display names changed and the database needs to be updated. - * See the `--recanonicalize-display-names` option for the `manage_auth` script. This can + * See the `--recanonicalize-display-names` option for the `manage_auth` script + (See the administration section below). This can be run while the server is live **after** updating to version 0.6.0. * Once the names have been recanonicalized, the `--remove-recanonicalization-flag` can be used to remove flags set on database objects to avoid reprocessing if the recanonicalize @@ -242,6 +243,9 @@ This repo (git clone https://github.com/kbase/auth2) ## To start server +Either use `docker-compose --build -d`, which is easier and starts the server in test mode +(which can be configured in the docker-compose file), or: + start mongodb if using mongo auth, create a mongo user cd into the auth2 repo @@ -261,12 +265,14 @@ cd jettybase ./jettybase$ java -jar -Djetty.port= /start.jar ``` -Or just use the Docker build, which is probably a lot easier. - ## Administer the server +Create the administration script: + +`./gradlew generateManageAuthScript` + Set a root password: -`./manage_auth -d -r` +`build/manage_auth -d -r` Login to a local account as `***ROOT***` with the password you set. Create a local account and assign it the create administrator role. That account can @@ -302,7 +308,7 @@ Omit the stop key to have jetty generate one for you. * Copy `test.cfg.example` to `test.cfg` and fill in the values appropriately. * If it works as is start buying lottery tickets immediately. -* `ant test` +* `./gradlew test` ### UI diff --git a/build.gradle b/build.gradle index ba0498ab..bfc60c65 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,6 @@ jacocoTestReport { } // TODO NOW run tests from Eclipse w/o specifying classpath manually -// TODO NOW make script // Custom java project layout sourceSets { @@ -150,6 +149,27 @@ task fatTestJar(type: Jar) { with jar } +task generateManageAuthScript { + dependsOn compileJava + doLast { + def dependencies = configurations.runtimeClasspath.collect { File file -> + file.absolutePath + } + + def classpath = dependencies.join(':') + + def scriptContent = """#!/bin/sh + +CLASSPATH=$classpath + +java -cp build/classes/java/main:\$CLASSPATH us.kbase.auth2.cli.AuthCLI \$@ +""" + + file("$buildDir/manage_auth").text = scriptContent + file("$buildDir/manage_auth").setExecutable(true) + } +} + def fromURL = { url, name -> File file = new File("$buildDir/download/${name}.jar") file.parentFile.mkdirs() From d890287a13af2a341fb4631a1891022a36047c87 Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 8 Feb 2024 11:32:43 -0800 Subject: [PATCH 15/25] Delete build.xml --- build.xml | 439 ------------------------------------------------------ 1 file changed, 439 deletions(-) delete mode 100644 build.xml diff --git a/build.xml b/build.xml deleted file mode 100644 index 5013dd30..00000000 --- a/build.xml +++ /dev/null @@ -1,439 +0,0 @@ - - - - Build file for the second KBase Authentication Service - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Classpath is ${classpathInName} - - - - - Classpath is ${classpathInName} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #!/bin/sh -java -cp ${dist}/${jar.file}:${lib.classpath} us.kbase.auth2.cli.AuthCLI $@ - - - - - - - - - - - - From 2d1f2f9e5899a30f81a8046a107c0f50a7920822 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 7 Feb 2024 17:56:58 -0800 Subject: [PATCH 16/25] Update release notes --- RELEASE_NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c27b795e..fdf13f62 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,13 @@ # Authentication Service MKII release notes +## 0.6.1 + +* Gradle has replaced Ant as the build tool. As a consequence, all the built artifacts + are now located in the `build` directory, including the `manage_auth` script. +* The MongoDB clients have been updated to the most recent version and the service tested + against Mongo 7. +* The docker-compose file has been updated to start an auth server in test mode. + ## 0.6.0 * ADMIN ACTION REQUIRED - after the server is upgraded, use the `manage_auth` script to From 8fce16882495323a6a624e3f8910cd0509ab197a Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 9 Feb 2024 13:49:05 -0800 Subject: [PATCH 17/25] Only store the git commit file in the build --- build.gradle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index bfc60c65..6c7fd7a4 100644 --- a/build.gradle +++ b/build.gradle @@ -20,15 +20,15 @@ repositories { def JAR_TEMPLATE_DIR = 'kbase_auth2_templates' def TEMPLATE_LIST_FILE_NAME = "templates.manifest" -task getGitCommitId { +task buildGitCommitFile { doLast { def commitId = grgit.head().id - file('src/us/kbase/auth2/gitcommit').text = commitId + // is there a varible for builddir/classe/java/main? + file("$buildDir/classes/java/main/us/kbase/auth2/gitcommit").text = commitId } } compileJava { - dependsOn getGitCommitId if (JavaVersion.current() <= JavaVersion.VERSION_1_8) { // TODO BUILD remove when we no longer support java 8 java.sourceCompatibility = JavaVersion.VERSION_1_8 @@ -36,6 +36,7 @@ compileJava { } else { options.release = 8 } + finalizedBy buildGitCommitFile } test { From bf4bc0cd526abe0e232b518a476b3c3dc4a6122d Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 9 Feb 2024 17:38:17 -0800 Subject: [PATCH 18/25] Use standard gradle layout The current layout plays havoc with Eclipse integration. No code changes, just files are moved. --- .classpath | 84 ------------------- README.md | 2 +- build.gradle | 21 +---- .../java}/us/kbase/auth2/GitCommit.java | 0 .../java}/us/kbase/auth2/Version.java | 0 .../java}/us/kbase/auth2/cli/AuthCLI.java | 0 .../us/kbase/auth2/cli/package-info.java | 0 .../kbase/auth2/cryptutils/PasswordCrypt.java | 0 .../auth2/cryptutils/RandomDataGenerator.java | 0 .../cryptutils/SHA1RandomDataGenerator.java | 0 .../us/kbase/auth2/kbase/KBaseAuthConfig.java | 0 .../us/kbase/auth2/lib/Authentication.java | 0 .../java}/us/kbase/auth2/lib/CustomRole.java | 0 .../java}/us/kbase/auth2/lib/DisplayName.java | 0 .../us/kbase/auth2/lib/EmailAddress.java | 0 .../us/kbase/auth2/lib/LinkIdentities.java | 0 .../java}/us/kbase/auth2/lib/LinkToken.java | 0 .../us/kbase/auth2/lib/LocalLoginResult.java | 0 .../java}/us/kbase/auth2/lib/LoginState.java | 0 .../java}/us/kbase/auth2/lib/LoginToken.java | 0 .../java}/us/kbase/auth2/lib/Name.java | 0 .../us/kbase/auth2/lib/OAuth2StartData.java | 0 .../java}/us/kbase/auth2/lib/Password.java | 0 .../kbase/auth2/lib/PasswordHashAndSalt.java | 0 .../java}/us/kbase/auth2/lib/PolicyID.java | 0 .../java}/us/kbase/auth2/lib/Role.java | 0 .../kbase/auth2/lib/TemporarySessionData.java | 0 .../kbase/auth2/lib/TokenCreationContext.java | 0 .../us/kbase/auth2/lib/UserDisabledState.java | 0 .../java}/us/kbase/auth2/lib/UserName.java | 0 .../us/kbase/auth2/lib/UserSearchSpec.java | 0 .../java}/us/kbase/auth2/lib/UserUpdate.java | 0 .../java}/us/kbase/auth2/lib/Utils.java | 0 .../us/kbase/auth2/lib/ViewableUser.java | 0 .../us/kbase/auth2/lib/config/AuthConfig.java | 0 .../kbase/auth2/lib/config/AuthConfigSet.java | 0 .../config/AuthConfigSetWithUpdateTime.java | 0 .../auth2/lib/config/AuthConfigUpdate.java | 0 .../lib/config/CollectingExternalConfig.java | 0 .../kbase/auth2/lib/config/ConfigAction.java | 0 .../us/kbase/auth2/lib/config/ConfigItem.java | 0 .../auth2/lib/config/ExternalConfig.java | 0 .../lib/config/ExternalConfigMapper.java | 0 .../kbase/auth2/lib/config/package-info.java | 0 .../auth2/lib/exceptions/AuthException.java | 0 .../exceptions/AuthenticationException.java | 0 .../lib/exceptions/DisabledUserException.java | 0 .../kbase/auth2/lib/exceptions/ErrorType.java | 0 .../ExternalConfigMappingException.java | 0 .../exceptions/IdentityLinkedException.java | 0 .../IdentityProviderErrorException.java | 0 .../IdentityRetrievalException.java | 0 .../exceptions/IllegalParameterException.java | 0 .../exceptions/IllegalPasswordException.java | 0 .../lib/exceptions/InvalidTokenException.java | 0 .../lib/exceptions/LinkFailedException.java | 0 .../exceptions/MissingParameterException.java | 0 .../auth2/lib/exceptions/NoDataException.java | 0 .../NoSuchEnvironmentException.java | 0 .../exceptions/NoSuchIdentityException.java | 0 .../NoSuchIdentityProviderException.java | 0 .../exceptions/NoSuchLocalUserException.java | 0 .../lib/exceptions/NoSuchRoleException.java | 0 .../lib/exceptions/NoSuchTokenException.java | 0 .../lib/exceptions/NoSuchUserException.java | 0 .../exceptions/NoTokenProvidedException.java | 0 .../exceptions/PasswordMismatchException.java | 0 .../lib/exceptions/TestModeException.java | 0 .../lib/exceptions/UnLinkFailedException.java | 0 .../lib/exceptions/UnauthorizedException.java | 0 .../lib/exceptions/UserExistsException.java | 0 .../auth2/lib/identity/IdentityProvider.java | 0 .../lib/identity/IdentityProviderConfig.java | 0 .../lib/identity/IdentityProviderFactory.java | 0 .../auth2/lib/identity/RemoteIdentity.java | 0 .../lib/identity/RemoteIdentityDetails.java | 0 .../auth2/lib/identity/RemoteIdentityID.java | 0 .../kbase/auth2/lib/storage/AuthStorage.java | 0 .../exceptions/AuthStorageException.java | 0 .../exceptions/StorageInitException.java | 0 .../kbase/auth2/lib/storage/mongo/Fields.java | 0 .../auth2/lib/storage/mongo/MongoStorage.java | 0 .../auth2/lib/token/IncomingHashedToken.java | 0 .../kbase/auth2/lib/token/IncomingToken.java | 0 .../us/kbase/auth2/lib/token/NewToken.java | 0 .../us/kbase/auth2/lib/token/StoredToken.java | 0 .../kbase/auth2/lib/token/TemporaryToken.java | 0 .../us/kbase/auth2/lib/token/TokenName.java | 0 .../us/kbase/auth2/lib/token/TokenSet.java | 0 .../us/kbase/auth2/lib/token/TokenType.java | 0 .../us/kbase/auth2/lib/user/AuthUser.java | 0 .../us/kbase/auth2/lib/user/LocalUser.java | 0 .../us/kbase/auth2/lib/user/NewUser.java | 0 .../GlobusIdentityProviderFactory.java | 0 .../GoogleIdentityProviderFactory.java | 0 .../OrcIDIdentityProviderFactory.java | 0 .../kbase/auth2/providers/package-info.java | 0 .../kbase/auth2/service/AppEventListener.java | 0 .../auth2/service/AuthAPIStaticConfig.java | 0 .../us/kbase/auth2/service/AuthBuilder.java | 0 .../auth2/service/AuthExternalConfig.java | 0 .../auth2/service/AuthStartupConfig.java | 0 .../auth2/service/AuthenticationService.java | 0 .../us/kbase/auth2/service/ContextFields.java | 0 .../us/kbase/auth2/service/LoggingFilter.java | 0 .../kbase/auth2/service/SLF4JAutoLogger.java | 0 .../kbase/auth2/service/UserAgentParser.java | 0 .../kbase/auth2/service/api/APIConstants.java | 0 .../us/kbase/auth2/service/api/APIPaths.java | 0 .../us/kbase/auth2/service/api/APIToken.java | 0 .../us/kbase/auth2/service/api/Admin.java | 0 .../kbase/auth2/service/api/LegacyGlobus.java | 0 .../kbase/auth2/service/api/LegacyKBase.java | 0 .../java}/us/kbase/auth2/service/api/Me.java | 0 .../kbase/auth2/service/api/NewAPIToken.java | 0 .../us/kbase/auth2/service/api/TestMode.java | 0 .../us/kbase/auth2/service/api/Token.java | 0 .../us/kbase/auth2/service/api/Users.java | 0 .../auth2/service/common/ExternalToken.java | 0 .../us/kbase/auth2/service/common/Fields.java | 0 .../auth2/service/common/IncomingJSON.java | 0 .../auth2/service/common/ServiceCommon.java | 0 .../AuthConfigurationException.java | 0 .../service/exceptions/ErrorMessage.java | 0 .../service/exceptions/ExceptionHandler.java | 0 .../service/template/TemplateProcessor.java | 0 .../template/mustache/MustacheProcessor.java | 0 .../us/kbase/auth2/service/ui/Admin.java | 0 .../kbase/auth2/service/ui/CustomRoles.java | 0 .../java}/us/kbase/auth2/service/ui/Link.java | 0 .../kbase/auth2/service/ui/LocalAccounts.java | 0 .../us/kbase/auth2/service/ui/Login.java | 0 .../us/kbase/auth2/service/ui/Logout.java | 0 .../java}/us/kbase/auth2/service/ui/Me.java | 0 .../us/kbase/auth2/service/ui/NewUIToken.java | 0 .../java}/us/kbase/auth2/service/ui/Root.java | 0 .../us/kbase/auth2/service/ui/Tokens.java | 0 .../kbase/auth2/service/ui/UIConstants.java | 0 .../us/kbase/auth2/service/ui/UIPaths.java | 0 .../us/kbase/auth2/service/ui/UIToken.java | 0 .../us/kbase/auth2/service/ui/UIUtils.java | 0 .../java}/us/kbase/test/auth2/MapBuilder.java | 0 .../auth2/MockIdentityProviderFactory.java | 0 .../test/auth2/MongoStorageTestManager.java | 0 .../test/auth2/StandaloneAuthServer.java | 0 .../java}/us/kbase/test/auth2/TestCommon.java | 0 .../us/kbase/test/auth2/TestConfigurator.java | 0 .../auth2/authcontroller/AuthController.java | 0 .../us/kbase/test/auth2/cli/AuthCLITest.java | 0 .../test/auth2/cryptutils/CryptUtilsTest.java | 0 .../SHA1RandomDataGeneratorTest.java | 0 .../test/auth2/kbase/KBaseAuthConfigTest.java | 0 .../lib/AuthenticationAnonymousIDsTest.java | 0 .../auth2/lib/AuthenticationConfigTest.java | 0 .../lib/AuthenticationConstructorTest.java | 0 .../AuthenticationCreateLocalUserTest.java | 0 .../lib/AuthenticationCreateRootTest.java | 0 .../lib/AuthenticationCustomRoleTest.java | 0 .../lib/AuthenticationDisableUserTest.java | 0 ...uthenticationGetAvailableUserNameTest.java | 0 ...AuthenticationGetUserDisplayNamesTest.java | 0 .../auth2/lib/AuthenticationGetUserTest.java | 0 .../AuthenticationIdentityProviderTest.java | 0 .../lib/AuthenticationImportUserTest.java | 0 .../auth2/lib/AuthenticationLinkTest.java | 0 .../auth2/lib/AuthenticationLoginTest.java | 0 .../lib/AuthenticationPasswordLoginTest.java | 0 .../auth2/lib/AuthenticationPolicyIDTest.java | 0 .../auth2/lib/AuthenticationRoleTest.java | 0 ...cationTestModeGetUserDisplayNamesTest.java | 0 .../lib/AuthenticationTestModeRoleTest.java | 0 .../lib/AuthenticationTestModeTokenTest.java | 0 .../lib/AuthenticationTestModeUserTest.java | 0 .../test/auth2/lib/AuthenticationTester.java | 0 .../auth2/lib/AuthenticationTokenTest.java | 0 .../lib/AuthenticationUserUpdateTest.java | 0 .../kbase/test/auth2/lib/CustomRoleTest.java | 0 .../kbase/test/auth2/lib/DisplayNameTest.java | 0 .../test/auth2/lib/EmailAddressTest.java | 0 .../test/auth2/lib/LinkIdentitiesTest.java | 0 .../kbase/test/auth2/lib/LinkTokenTest.java | 0 .../test/auth2/lib/LocalLoginResultTest.java | 0 .../kbase/test/auth2/lib/LoginStateTest.java | 0 .../kbase/test/auth2/lib/LoginTokenTest.java | 0 .../us/kbase/test/auth2/lib/NameTest.java | 0 .../test/auth2/lib/OAuth2StartDataTest.java | 0 .../auth2/lib/PasswordHashAndSaltTest.java | 0 .../us/kbase/test/auth2/lib/PasswordTest.java | 0 .../us/kbase/test/auth2/lib/PolicyIDTest.java | 0 .../us/kbase/test/auth2/lib/RoleTest.java | 0 .../auth2/lib/TemporarySessionDataTest.java | 0 .../auth2/lib/TokenCreationContextTest.java | 0 .../test/auth2/lib/UserDisabledStateTest.java | 0 .../us/kbase/test/auth2/lib/UserNameTest.java | 0 .../test/auth2/lib/UserSearchSpecTest.java | 0 .../kbase/test/auth2/lib/UserUpdateTest.java | 0 .../us/kbase/test/auth2/lib/UtilsTest.java | 0 .../test/auth2/lib/ViewableUserTest.java | 0 .../test/auth2/lib/config/AuthConfigTest.java | 0 .../config/CollectingExternalConfigTest.java | 0 .../test/auth2/lib/config/FailConfig.java | 0 .../auth2/lib/config/TestExternalConfig.java | 0 .../auth2/lib/exceptions/ExceptionTest.java | 0 .../identity/IdentityProviderConfigTest.java | 0 .../lib/identity/RemoteIdentityTest.java | 0 .../mongo/MongoStorageAnonymousIDTest.java | 0 .../storage/mongo/MongoStorageConfigTest.java | 0 .../mongo/MongoStorageCustomRoleTest.java | 0 .../mongo/MongoStorageDisableAccountTest.java | 0 .../MongoStorageDuplicateKeyCheckerTest.java | 0 .../MongoStorageGetDisplayNamesTest.java | 0 .../mongo/MongoStorageInvalidDBDataTest.java | 0 .../storage/mongo/MongoStorageLinkTest.java | 0 .../mongo/MongoStoragePasswordTest.java | 0 .../MongoStorageRecanonicalizationTest.java | 0 .../storage/mongo/MongoStorageRolesTest.java | 0 .../mongo/MongoStorageStartUpTest.java | 0 .../MongoStorageTempSessionDataTest.java | 0 .../storage/mongo/MongoStorageTestCommon.java | 0 .../MongoStorageTestGetDisplayNamesTest.java | 0 .../mongo/MongoStorageTestRoleTest.java | 0 .../mongo/MongoStorageTestTokensTest.java | 0 .../MongoStorageTestUserCreateGetTest.java | 0 .../lib/storage/mongo/MongoStorageTester.java | 0 .../storage/mongo/MongoStorageTokensTest.java | 0 .../MongoStorageUpdateUserFieldsTest.java | 0 .../mongo/MongoStorageUserCreateGetTest.java | 0 .../test/auth2/lib/token/TokenNameTest.java | 0 .../kbase/test/auth2/lib/token/TokenTest.java | 0 .../test/auth2/lib/user/AuthUserTest.java | 0 .../test/auth2/lib/user/LocalUserTest.java | 0 .../test/auth2/lib/user/NewUserTest.java | 0 .../providers/GlobusIdentityProviderTest.java | 0 .../providers/GoogleIdentityProviderTest.java | 0 .../providers/OrcIDIdentityProviderTest.java | 0 .../auth2/service/AuthExternalConfigTest.java | 0 .../test/auth2/service/LoggingFilterTest.java | 0 .../test/auth2/service/ServiceTestUtils.java | 0 .../test/auth2/service/api/APITokenTest.java | 0 .../service/api/AdminIntegrationTest.java | 0 .../test/auth2/service/api/AdminTest.java | 0 .../service/api/TestModeIntegrationTest.java | 0 .../test/auth2/service/api/TestModeTest.java | 0 .../auth2/service/api/TokenEndpointTest.java | 0 .../auth2/service/api/UserEndpointTest.java | 0 .../test/auth2/service/api/package-info.java | 0 .../service/common/ExternalTokenTest.java | 0 .../service/common/FailOnInstantiation.java | 0 ...ilOnInstantiationNoNullaryConstructor.java | 0 ...FailOnInstantiationPrivateConstructor.java | 0 .../service/common/IncomingJSONTest.java | 0 .../service/common/ServiceCommonTest.java | 0 .../auth2/service/common/package-info.java | 0 .../service/ui/AdminIntegrationTest.java | 0 .../test/auth2/service/ui/AdminTest.java | 0 .../kbase/test/auth2/service/ui/LinkTest.java | 0 .../test/auth2/service/ui/LoginTest.java | 0 .../kbase/test/auth2/service/ui/MeTest.java | 0 .../service/ui/PKCEChallengeMatcher.java | 0 .../auth2/service/ui/SimpleEndpointsTest.java | 0 .../test/auth2/service/ui/StateMatcher.java | 0 .../test/auth2/service/ui/TokensTest.java | 0 .../test/auth2/service/ui/UITokensTest.java | 0 .../test/auth2/service/ui/UIUtilsTest.java | 0 .../test/auth2/service/ui/package-info.java | 0 .../AdminIntegrationTest_userDisplay.testdata | 0 .../ui/LinkTest_linkChoiceHTML.testdata | 0 .../LinkTest_linkChoiceHTMLNoLinks.testdata | 0 .../LinkTest_linkChoiceHTMLOnlyLinks.testdata | 0 ...kTest_linkChoiceHTMLTrailingSlash.testdata | 0 .../LinkTest_linkDisplayNoProviders.testdata | 0 ...LinkTest_linkDisplayWithLocalUser.testdata | 0 ...nkTest_linkDisplayWithOneProvider.testdata | 0 ...kTest_linkDisplayWithTwoProviders.testdata | 0 ...oginChoice2CreateAndLoginDisabled.testdata | 0 ...loginChoice2CreateWithRedirectURL.testdata | 0 ...LoginWithRedirectAndLoginDisabled.testdata | 0 ...oginTest_loginChoice3Create2Login.testdata | 0 ...ginTest_startDisplayLoginDisabled.testdata | 0 ...nTest_startDisplayWithOneProvider.testdata | 0 ...Test_startDisplayWithTwoProviders.testdata | 0 .../ui/MeTest_getMeMaximalInput.testdata | 0 .../ui/MeTest_getMeMinimalInput.testdata | 0 ...pleEndpointsTest_customRolesEmpty.testdata | 0 ...mpleEndpointsTest_customRolesFull.testdata | 0 ...leEndpointsTest_localLoginDisplay.testdata | 0 ...Test_localLoginResetDisplayNoUser.testdata | 0 ...st_localLoginResetDisplayWithUser.testdata | 0 ...SimpleEndpointsTest_logoutDisplay.testdata | 0 ...pointsTest_logoutWithBadTokenHTML.testdata | 0 ...ointsTest_logoutWithGoodTokenHTML.testdata | 0 .../ui/SimpleEndpointsTest_rootHTML.testdata | 0 ...okensTest_createTokenMaximalInput.testdata | 0 ...okensTest_createTokenMinimalInput.testdata | 0 .../TokensTest_getTokensMaximalInput.testdata | 0 .../TokensTest_getTokensMinimalInput.testdata | 0 296 files changed, 2 insertions(+), 105 deletions(-) delete mode 100644 .classpath rename src/{ => main/java}/us/kbase/auth2/GitCommit.java (100%) rename src/{ => main/java}/us/kbase/auth2/Version.java (100%) rename src/{ => main/java}/us/kbase/auth2/cli/AuthCLI.java (100%) rename src/{ => main/java}/us/kbase/auth2/cli/package-info.java (100%) rename src/{ => main/java}/us/kbase/auth2/cryptutils/PasswordCrypt.java (100%) rename src/{ => main/java}/us/kbase/auth2/cryptutils/RandomDataGenerator.java (100%) rename src/{ => main/java}/us/kbase/auth2/cryptutils/SHA1RandomDataGenerator.java (100%) rename src/{ => main/java}/us/kbase/auth2/kbase/KBaseAuthConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/Authentication.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/CustomRole.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/DisplayName.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/EmailAddress.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/LinkIdentities.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/LinkToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/LocalLoginResult.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/LoginState.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/LoginToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/Name.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/OAuth2StartData.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/Password.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/PasswordHashAndSalt.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/PolicyID.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/Role.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/TemporarySessionData.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/TokenCreationContext.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/UserDisabledState.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/UserName.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/UserSearchSpec.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/UserUpdate.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/Utils.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/ViewableUser.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/AuthConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/AuthConfigSet.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/AuthConfigSetWithUpdateTime.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/AuthConfigUpdate.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/CollectingExternalConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/ConfigAction.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/ConfigItem.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/ExternalConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/ExternalConfigMapper.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/config/package-info.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/AuthException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/AuthenticationException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/DisabledUserException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/ErrorType.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/ExternalConfigMappingException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/IdentityLinkedException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/IdentityProviderErrorException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/IdentityRetrievalException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/IllegalParameterException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/IllegalPasswordException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/InvalidTokenException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/LinkFailedException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/MissingParameterException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoDataException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchEnvironmentException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchIdentityException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchIdentityProviderException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchLocalUserException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchRoleException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchTokenException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoSuchUserException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/NoTokenProvidedException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/PasswordMismatchException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/TestModeException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/UnLinkFailedException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/UnauthorizedException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/exceptions/UserExistsException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/IdentityProvider.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/IdentityProviderConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/IdentityProviderFactory.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/RemoteIdentity.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/RemoteIdentityDetails.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/identity/RemoteIdentityID.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/storage/AuthStorage.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/storage/exceptions/AuthStorageException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/storage/exceptions/StorageInitException.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/storage/mongo/Fields.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/storage/mongo/MongoStorage.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/IncomingHashedToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/IncomingToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/NewToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/StoredToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/TemporaryToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/TokenName.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/TokenSet.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/token/TokenType.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/user/AuthUser.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/user/LocalUser.java (100%) rename src/{ => main/java}/us/kbase/auth2/lib/user/NewUser.java (100%) rename src/{ => main/java}/us/kbase/auth2/providers/GlobusIdentityProviderFactory.java (100%) rename src/{ => main/java}/us/kbase/auth2/providers/GoogleIdentityProviderFactory.java (100%) rename src/{ => main/java}/us/kbase/auth2/providers/OrcIDIdentityProviderFactory.java (100%) rename src/{ => main/java}/us/kbase/auth2/providers/package-info.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AppEventListener.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AuthAPIStaticConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AuthBuilder.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AuthExternalConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AuthStartupConfig.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/AuthenticationService.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ContextFields.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/LoggingFilter.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/SLF4JAutoLogger.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/UserAgentParser.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/APIConstants.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/APIPaths.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/APIToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/Admin.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/LegacyGlobus.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/LegacyKBase.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/Me.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/NewAPIToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/TestMode.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/Token.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/api/Users.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/common/ExternalToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/common/Fields.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/common/IncomingJSON.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/common/ServiceCommon.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/exceptions/AuthConfigurationException.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/exceptions/ErrorMessage.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/exceptions/ExceptionHandler.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/template/TemplateProcessor.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/template/mustache/MustacheProcessor.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Admin.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/CustomRoles.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Link.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/LocalAccounts.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Login.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Logout.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Me.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/NewUIToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Root.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/Tokens.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/UIConstants.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/UIPaths.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/UIToken.java (100%) rename src/{ => main/java}/us/kbase/auth2/service/ui/UIUtils.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/MapBuilder.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/MockIdentityProviderFactory.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/MongoStorageTestManager.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/StandaloneAuthServer.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/TestCommon.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/TestConfigurator.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/authcontroller/AuthController.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/cli/AuthCLITest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/cryptutils/CryptUtilsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/cryptutils/SHA1RandomDataGeneratorTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationConstructorTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationCreateLocalUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationCreateRootTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationCustomRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationDisableUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationGetAvailableUserNameTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationGetUserDisplayNamesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationGetUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationIdentityProviderTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationImportUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationLinkTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationLoginTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationPasswordLoginTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationPolicyIDTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTestModeGetUserDisplayNamesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTestModeRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTestModeTokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTestModeUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTester.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationTokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/AuthenticationUserUpdateTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/CustomRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/DisplayNameTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/EmailAddressTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/LinkIdentitiesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/LinkTokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/LocalLoginResultTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/LoginStateTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/LoginTokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/NameTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/OAuth2StartDataTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/PasswordHashAndSaltTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/PasswordTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/PolicyIDTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/RoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/TemporarySessionDataTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/TokenCreationContextTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/UserDisabledStateTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/UserNameTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/UserSearchSpecTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/UserUpdateTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/UtilsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/ViewableUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/config/AuthConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/config/CollectingExternalConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/config/FailConfig.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/config/TestExternalConfig.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/exceptions/ExceptionTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/identity/IdentityProviderConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/identity/RemoteIdentityTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageAnonymousIDTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageCustomRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDisableAccountTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageGetDisplayNamesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageInvalidDBDataTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageLinkTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStoragePasswordTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRecanonicalizationTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRolesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTempSessionDataTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestCommon.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestGetDisplayNamesTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestRoleTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestTokensTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestUserCreateGetTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTokensTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUpdateUserFieldsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUserCreateGetTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/token/TokenNameTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/token/TokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/user/AuthUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/user/LocalUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/lib/user/NewUserTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/providers/GlobusIdentityProviderTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/providers/GoogleIdentityProviderTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/providers/OrcIDIdentityProviderTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/AuthExternalConfigTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/LoggingFilterTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ServiceTestUtils.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/APITokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/AdminIntegrationTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/AdminTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/TestModeIntegrationTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/TestModeTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/TokenEndpointTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/UserEndpointTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/api/package-info.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/ExternalTokenTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/FailOnInstantiation.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/FailOnInstantiationNoNullaryConstructor.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/FailOnInstantiationPrivateConstructor.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/IncomingJSONTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/ServiceCommonTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/common/package-info.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/AdminIntegrationTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/AdminTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/LinkTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/LoginTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/MeTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/PKCEChallengeMatcher.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/StateMatcher.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/TokensTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/UITokensTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/UIUtilsTest.java (100%) rename src/{ => test/java}/us/kbase/test/auth2/service/ui/package-info.java (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/AdminIntegrationTest_userDisplay.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTML.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLNoLinks.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLOnlyLinks.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLTrailingSlash.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayNoProviders.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithLocalUser.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithOneProvider.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithTwoProviders.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateAndLoginDisabled.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateWithRedirectURL.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2LoginWithRedirectAndLoginDisabled.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_loginChoice3Create2Login.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_startDisplayLoginDisabled.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithOneProvider.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithTwoProviders.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/MeTest_getMeMaximalInput.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/MeTest_getMeMinimalInput.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesEmpty.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesFull.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginDisplay.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayNoUser.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayWithUser.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutDisplay.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithBadTokenHTML.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithGoodTokenHTML.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_rootHTML.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/TokensTest_createTokenMaximalInput.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/TokensTest_createTokenMinimalInput.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/TokensTest_getTokensMaximalInput.testdata (100%) rename src/{ => test/resources}/us/kbase/test/auth2/service/ui/TokensTest_getTokensMinimalInput.testdata (100%) diff --git a/.classpath b/.classpath deleted file mode 100644 index b75439e9..00000000 --- a/.classpath +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/README.md b/README.md index 43eb77a8..0e692dae 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,7 @@ Omit the stop key to have jetty generate one for you. * The master branch is the stable branch. Releases are made from the develop branch to the master branch. * Update the version as per the semantic version rules in - `src/us/kbase/auth2/service/common/ServiceCommon.java`. + `src/main/java/us/kbase/auth2/Version.java`. * Tag the version in git and github. ### Running tests diff --git a/build.gradle b/build.gradle index 6c7fd7a4..b03a2b6d 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ def TEMPLATE_LIST_FILE_NAME = "templates.manifest" task buildGitCommitFile { doLast { def commitId = grgit.head().id - // is there a varible for builddir/classe/java/main? + // is there a variable for builddir/classe/java/main? file("$buildDir/classes/java/main/us/kbase/auth2/gitcommit").text = commitId } } @@ -69,27 +69,10 @@ jacocoTestReport { } } -// TODO NOW run tests from Eclipse w/o specifying classpath manually - // Custom java project layout sourceSets { - main { - java { - srcDirs = ["src"] - exclude '**/test/**' - } - resources { - srcDirs = ["src"] - include "**/gitcommit" - } - } test { - java { - srcDirs = ["src"] - include '**/test/**' - } resources { - srcDirs = ["src"] include "**/*.testdata" } } @@ -105,8 +88,6 @@ javadoc { war { webXml = file('war/web.xml') - // For some reason the test directories are included in the war file - // although the files are gone. Not sure why } configurations { diff --git a/src/us/kbase/auth2/GitCommit.java b/src/main/java/us/kbase/auth2/GitCommit.java similarity index 100% rename from src/us/kbase/auth2/GitCommit.java rename to src/main/java/us/kbase/auth2/GitCommit.java diff --git a/src/us/kbase/auth2/Version.java b/src/main/java/us/kbase/auth2/Version.java similarity index 100% rename from src/us/kbase/auth2/Version.java rename to src/main/java/us/kbase/auth2/Version.java diff --git a/src/us/kbase/auth2/cli/AuthCLI.java b/src/main/java/us/kbase/auth2/cli/AuthCLI.java similarity index 100% rename from src/us/kbase/auth2/cli/AuthCLI.java rename to src/main/java/us/kbase/auth2/cli/AuthCLI.java diff --git a/src/us/kbase/auth2/cli/package-info.java b/src/main/java/us/kbase/auth2/cli/package-info.java similarity index 100% rename from src/us/kbase/auth2/cli/package-info.java rename to src/main/java/us/kbase/auth2/cli/package-info.java diff --git a/src/us/kbase/auth2/cryptutils/PasswordCrypt.java b/src/main/java/us/kbase/auth2/cryptutils/PasswordCrypt.java similarity index 100% rename from src/us/kbase/auth2/cryptutils/PasswordCrypt.java rename to src/main/java/us/kbase/auth2/cryptutils/PasswordCrypt.java diff --git a/src/us/kbase/auth2/cryptutils/RandomDataGenerator.java b/src/main/java/us/kbase/auth2/cryptutils/RandomDataGenerator.java similarity index 100% rename from src/us/kbase/auth2/cryptutils/RandomDataGenerator.java rename to src/main/java/us/kbase/auth2/cryptutils/RandomDataGenerator.java diff --git a/src/us/kbase/auth2/cryptutils/SHA1RandomDataGenerator.java b/src/main/java/us/kbase/auth2/cryptutils/SHA1RandomDataGenerator.java similarity index 100% rename from src/us/kbase/auth2/cryptutils/SHA1RandomDataGenerator.java rename to src/main/java/us/kbase/auth2/cryptutils/SHA1RandomDataGenerator.java diff --git a/src/us/kbase/auth2/kbase/KBaseAuthConfig.java b/src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java similarity index 100% rename from src/us/kbase/auth2/kbase/KBaseAuthConfig.java rename to src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java diff --git a/src/us/kbase/auth2/lib/Authentication.java b/src/main/java/us/kbase/auth2/lib/Authentication.java similarity index 100% rename from src/us/kbase/auth2/lib/Authentication.java rename to src/main/java/us/kbase/auth2/lib/Authentication.java diff --git a/src/us/kbase/auth2/lib/CustomRole.java b/src/main/java/us/kbase/auth2/lib/CustomRole.java similarity index 100% rename from src/us/kbase/auth2/lib/CustomRole.java rename to src/main/java/us/kbase/auth2/lib/CustomRole.java diff --git a/src/us/kbase/auth2/lib/DisplayName.java b/src/main/java/us/kbase/auth2/lib/DisplayName.java similarity index 100% rename from src/us/kbase/auth2/lib/DisplayName.java rename to src/main/java/us/kbase/auth2/lib/DisplayName.java diff --git a/src/us/kbase/auth2/lib/EmailAddress.java b/src/main/java/us/kbase/auth2/lib/EmailAddress.java similarity index 100% rename from src/us/kbase/auth2/lib/EmailAddress.java rename to src/main/java/us/kbase/auth2/lib/EmailAddress.java diff --git a/src/us/kbase/auth2/lib/LinkIdentities.java b/src/main/java/us/kbase/auth2/lib/LinkIdentities.java similarity index 100% rename from src/us/kbase/auth2/lib/LinkIdentities.java rename to src/main/java/us/kbase/auth2/lib/LinkIdentities.java diff --git a/src/us/kbase/auth2/lib/LinkToken.java b/src/main/java/us/kbase/auth2/lib/LinkToken.java similarity index 100% rename from src/us/kbase/auth2/lib/LinkToken.java rename to src/main/java/us/kbase/auth2/lib/LinkToken.java diff --git a/src/us/kbase/auth2/lib/LocalLoginResult.java b/src/main/java/us/kbase/auth2/lib/LocalLoginResult.java similarity index 100% rename from src/us/kbase/auth2/lib/LocalLoginResult.java rename to src/main/java/us/kbase/auth2/lib/LocalLoginResult.java diff --git a/src/us/kbase/auth2/lib/LoginState.java b/src/main/java/us/kbase/auth2/lib/LoginState.java similarity index 100% rename from src/us/kbase/auth2/lib/LoginState.java rename to src/main/java/us/kbase/auth2/lib/LoginState.java diff --git a/src/us/kbase/auth2/lib/LoginToken.java b/src/main/java/us/kbase/auth2/lib/LoginToken.java similarity index 100% rename from src/us/kbase/auth2/lib/LoginToken.java rename to src/main/java/us/kbase/auth2/lib/LoginToken.java diff --git a/src/us/kbase/auth2/lib/Name.java b/src/main/java/us/kbase/auth2/lib/Name.java similarity index 100% rename from src/us/kbase/auth2/lib/Name.java rename to src/main/java/us/kbase/auth2/lib/Name.java diff --git a/src/us/kbase/auth2/lib/OAuth2StartData.java b/src/main/java/us/kbase/auth2/lib/OAuth2StartData.java similarity index 100% rename from src/us/kbase/auth2/lib/OAuth2StartData.java rename to src/main/java/us/kbase/auth2/lib/OAuth2StartData.java diff --git a/src/us/kbase/auth2/lib/Password.java b/src/main/java/us/kbase/auth2/lib/Password.java similarity index 100% rename from src/us/kbase/auth2/lib/Password.java rename to src/main/java/us/kbase/auth2/lib/Password.java diff --git a/src/us/kbase/auth2/lib/PasswordHashAndSalt.java b/src/main/java/us/kbase/auth2/lib/PasswordHashAndSalt.java similarity index 100% rename from src/us/kbase/auth2/lib/PasswordHashAndSalt.java rename to src/main/java/us/kbase/auth2/lib/PasswordHashAndSalt.java diff --git a/src/us/kbase/auth2/lib/PolicyID.java b/src/main/java/us/kbase/auth2/lib/PolicyID.java similarity index 100% rename from src/us/kbase/auth2/lib/PolicyID.java rename to src/main/java/us/kbase/auth2/lib/PolicyID.java diff --git a/src/us/kbase/auth2/lib/Role.java b/src/main/java/us/kbase/auth2/lib/Role.java similarity index 100% rename from src/us/kbase/auth2/lib/Role.java rename to src/main/java/us/kbase/auth2/lib/Role.java diff --git a/src/us/kbase/auth2/lib/TemporarySessionData.java b/src/main/java/us/kbase/auth2/lib/TemporarySessionData.java similarity index 100% rename from src/us/kbase/auth2/lib/TemporarySessionData.java rename to src/main/java/us/kbase/auth2/lib/TemporarySessionData.java diff --git a/src/us/kbase/auth2/lib/TokenCreationContext.java b/src/main/java/us/kbase/auth2/lib/TokenCreationContext.java similarity index 100% rename from src/us/kbase/auth2/lib/TokenCreationContext.java rename to src/main/java/us/kbase/auth2/lib/TokenCreationContext.java diff --git a/src/us/kbase/auth2/lib/UserDisabledState.java b/src/main/java/us/kbase/auth2/lib/UserDisabledState.java similarity index 100% rename from src/us/kbase/auth2/lib/UserDisabledState.java rename to src/main/java/us/kbase/auth2/lib/UserDisabledState.java diff --git a/src/us/kbase/auth2/lib/UserName.java b/src/main/java/us/kbase/auth2/lib/UserName.java similarity index 100% rename from src/us/kbase/auth2/lib/UserName.java rename to src/main/java/us/kbase/auth2/lib/UserName.java diff --git a/src/us/kbase/auth2/lib/UserSearchSpec.java b/src/main/java/us/kbase/auth2/lib/UserSearchSpec.java similarity index 100% rename from src/us/kbase/auth2/lib/UserSearchSpec.java rename to src/main/java/us/kbase/auth2/lib/UserSearchSpec.java diff --git a/src/us/kbase/auth2/lib/UserUpdate.java b/src/main/java/us/kbase/auth2/lib/UserUpdate.java similarity index 100% rename from src/us/kbase/auth2/lib/UserUpdate.java rename to src/main/java/us/kbase/auth2/lib/UserUpdate.java diff --git a/src/us/kbase/auth2/lib/Utils.java b/src/main/java/us/kbase/auth2/lib/Utils.java similarity index 100% rename from src/us/kbase/auth2/lib/Utils.java rename to src/main/java/us/kbase/auth2/lib/Utils.java diff --git a/src/us/kbase/auth2/lib/ViewableUser.java b/src/main/java/us/kbase/auth2/lib/ViewableUser.java similarity index 100% rename from src/us/kbase/auth2/lib/ViewableUser.java rename to src/main/java/us/kbase/auth2/lib/ViewableUser.java diff --git a/src/us/kbase/auth2/lib/config/AuthConfig.java b/src/main/java/us/kbase/auth2/lib/config/AuthConfig.java similarity index 100% rename from src/us/kbase/auth2/lib/config/AuthConfig.java rename to src/main/java/us/kbase/auth2/lib/config/AuthConfig.java diff --git a/src/us/kbase/auth2/lib/config/AuthConfigSet.java b/src/main/java/us/kbase/auth2/lib/config/AuthConfigSet.java similarity index 100% rename from src/us/kbase/auth2/lib/config/AuthConfigSet.java rename to src/main/java/us/kbase/auth2/lib/config/AuthConfigSet.java diff --git a/src/us/kbase/auth2/lib/config/AuthConfigSetWithUpdateTime.java b/src/main/java/us/kbase/auth2/lib/config/AuthConfigSetWithUpdateTime.java similarity index 100% rename from src/us/kbase/auth2/lib/config/AuthConfigSetWithUpdateTime.java rename to src/main/java/us/kbase/auth2/lib/config/AuthConfigSetWithUpdateTime.java diff --git a/src/us/kbase/auth2/lib/config/AuthConfigUpdate.java b/src/main/java/us/kbase/auth2/lib/config/AuthConfigUpdate.java similarity index 100% rename from src/us/kbase/auth2/lib/config/AuthConfigUpdate.java rename to src/main/java/us/kbase/auth2/lib/config/AuthConfigUpdate.java diff --git a/src/us/kbase/auth2/lib/config/CollectingExternalConfig.java b/src/main/java/us/kbase/auth2/lib/config/CollectingExternalConfig.java similarity index 100% rename from src/us/kbase/auth2/lib/config/CollectingExternalConfig.java rename to src/main/java/us/kbase/auth2/lib/config/CollectingExternalConfig.java diff --git a/src/us/kbase/auth2/lib/config/ConfigAction.java b/src/main/java/us/kbase/auth2/lib/config/ConfigAction.java similarity index 100% rename from src/us/kbase/auth2/lib/config/ConfigAction.java rename to src/main/java/us/kbase/auth2/lib/config/ConfigAction.java diff --git a/src/us/kbase/auth2/lib/config/ConfigItem.java b/src/main/java/us/kbase/auth2/lib/config/ConfigItem.java similarity index 100% rename from src/us/kbase/auth2/lib/config/ConfigItem.java rename to src/main/java/us/kbase/auth2/lib/config/ConfigItem.java diff --git a/src/us/kbase/auth2/lib/config/ExternalConfig.java b/src/main/java/us/kbase/auth2/lib/config/ExternalConfig.java similarity index 100% rename from src/us/kbase/auth2/lib/config/ExternalConfig.java rename to src/main/java/us/kbase/auth2/lib/config/ExternalConfig.java diff --git a/src/us/kbase/auth2/lib/config/ExternalConfigMapper.java b/src/main/java/us/kbase/auth2/lib/config/ExternalConfigMapper.java similarity index 100% rename from src/us/kbase/auth2/lib/config/ExternalConfigMapper.java rename to src/main/java/us/kbase/auth2/lib/config/ExternalConfigMapper.java diff --git a/src/us/kbase/auth2/lib/config/package-info.java b/src/main/java/us/kbase/auth2/lib/config/package-info.java similarity index 100% rename from src/us/kbase/auth2/lib/config/package-info.java rename to src/main/java/us/kbase/auth2/lib/config/package-info.java diff --git a/src/us/kbase/auth2/lib/exceptions/AuthException.java b/src/main/java/us/kbase/auth2/lib/exceptions/AuthException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/AuthException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/AuthException.java diff --git a/src/us/kbase/auth2/lib/exceptions/AuthenticationException.java b/src/main/java/us/kbase/auth2/lib/exceptions/AuthenticationException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/AuthenticationException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/AuthenticationException.java diff --git a/src/us/kbase/auth2/lib/exceptions/DisabledUserException.java b/src/main/java/us/kbase/auth2/lib/exceptions/DisabledUserException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/DisabledUserException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/DisabledUserException.java diff --git a/src/us/kbase/auth2/lib/exceptions/ErrorType.java b/src/main/java/us/kbase/auth2/lib/exceptions/ErrorType.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/ErrorType.java rename to src/main/java/us/kbase/auth2/lib/exceptions/ErrorType.java diff --git a/src/us/kbase/auth2/lib/exceptions/ExternalConfigMappingException.java b/src/main/java/us/kbase/auth2/lib/exceptions/ExternalConfigMappingException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/ExternalConfigMappingException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/ExternalConfigMappingException.java diff --git a/src/us/kbase/auth2/lib/exceptions/IdentityLinkedException.java b/src/main/java/us/kbase/auth2/lib/exceptions/IdentityLinkedException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/IdentityLinkedException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/IdentityLinkedException.java diff --git a/src/us/kbase/auth2/lib/exceptions/IdentityProviderErrorException.java b/src/main/java/us/kbase/auth2/lib/exceptions/IdentityProviderErrorException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/IdentityProviderErrorException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/IdentityProviderErrorException.java diff --git a/src/us/kbase/auth2/lib/exceptions/IdentityRetrievalException.java b/src/main/java/us/kbase/auth2/lib/exceptions/IdentityRetrievalException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/IdentityRetrievalException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/IdentityRetrievalException.java diff --git a/src/us/kbase/auth2/lib/exceptions/IllegalParameterException.java b/src/main/java/us/kbase/auth2/lib/exceptions/IllegalParameterException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/IllegalParameterException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/IllegalParameterException.java diff --git a/src/us/kbase/auth2/lib/exceptions/IllegalPasswordException.java b/src/main/java/us/kbase/auth2/lib/exceptions/IllegalPasswordException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/IllegalPasswordException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/IllegalPasswordException.java diff --git a/src/us/kbase/auth2/lib/exceptions/InvalidTokenException.java b/src/main/java/us/kbase/auth2/lib/exceptions/InvalidTokenException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/InvalidTokenException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/InvalidTokenException.java diff --git a/src/us/kbase/auth2/lib/exceptions/LinkFailedException.java b/src/main/java/us/kbase/auth2/lib/exceptions/LinkFailedException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/LinkFailedException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/LinkFailedException.java diff --git a/src/us/kbase/auth2/lib/exceptions/MissingParameterException.java b/src/main/java/us/kbase/auth2/lib/exceptions/MissingParameterException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/MissingParameterException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/MissingParameterException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoDataException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoDataException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoDataException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoDataException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchEnvironmentException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchEnvironmentException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchEnvironmentException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchEnvironmentException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchIdentityException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchIdentityException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchIdentityException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchIdentityException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchIdentityProviderException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchIdentityProviderException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchIdentityProviderException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchIdentityProviderException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchLocalUserException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchLocalUserException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchLocalUserException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchLocalUserException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchRoleException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchRoleException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchRoleException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchRoleException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchTokenException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchTokenException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchTokenException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchTokenException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoSuchUserException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoSuchUserException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoSuchUserException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoSuchUserException.java diff --git a/src/us/kbase/auth2/lib/exceptions/NoTokenProvidedException.java b/src/main/java/us/kbase/auth2/lib/exceptions/NoTokenProvidedException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/NoTokenProvidedException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/NoTokenProvidedException.java diff --git a/src/us/kbase/auth2/lib/exceptions/PasswordMismatchException.java b/src/main/java/us/kbase/auth2/lib/exceptions/PasswordMismatchException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/PasswordMismatchException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/PasswordMismatchException.java diff --git a/src/us/kbase/auth2/lib/exceptions/TestModeException.java b/src/main/java/us/kbase/auth2/lib/exceptions/TestModeException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/TestModeException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/TestModeException.java diff --git a/src/us/kbase/auth2/lib/exceptions/UnLinkFailedException.java b/src/main/java/us/kbase/auth2/lib/exceptions/UnLinkFailedException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/UnLinkFailedException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/UnLinkFailedException.java diff --git a/src/us/kbase/auth2/lib/exceptions/UnauthorizedException.java b/src/main/java/us/kbase/auth2/lib/exceptions/UnauthorizedException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/UnauthorizedException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/UnauthorizedException.java diff --git a/src/us/kbase/auth2/lib/exceptions/UserExistsException.java b/src/main/java/us/kbase/auth2/lib/exceptions/UserExistsException.java similarity index 100% rename from src/us/kbase/auth2/lib/exceptions/UserExistsException.java rename to src/main/java/us/kbase/auth2/lib/exceptions/UserExistsException.java diff --git a/src/us/kbase/auth2/lib/identity/IdentityProvider.java b/src/main/java/us/kbase/auth2/lib/identity/IdentityProvider.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/IdentityProvider.java rename to src/main/java/us/kbase/auth2/lib/identity/IdentityProvider.java diff --git a/src/us/kbase/auth2/lib/identity/IdentityProviderConfig.java b/src/main/java/us/kbase/auth2/lib/identity/IdentityProviderConfig.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/IdentityProviderConfig.java rename to src/main/java/us/kbase/auth2/lib/identity/IdentityProviderConfig.java diff --git a/src/us/kbase/auth2/lib/identity/IdentityProviderFactory.java b/src/main/java/us/kbase/auth2/lib/identity/IdentityProviderFactory.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/IdentityProviderFactory.java rename to src/main/java/us/kbase/auth2/lib/identity/IdentityProviderFactory.java diff --git a/src/us/kbase/auth2/lib/identity/RemoteIdentity.java b/src/main/java/us/kbase/auth2/lib/identity/RemoteIdentity.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/RemoteIdentity.java rename to src/main/java/us/kbase/auth2/lib/identity/RemoteIdentity.java diff --git a/src/us/kbase/auth2/lib/identity/RemoteIdentityDetails.java b/src/main/java/us/kbase/auth2/lib/identity/RemoteIdentityDetails.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/RemoteIdentityDetails.java rename to src/main/java/us/kbase/auth2/lib/identity/RemoteIdentityDetails.java diff --git a/src/us/kbase/auth2/lib/identity/RemoteIdentityID.java b/src/main/java/us/kbase/auth2/lib/identity/RemoteIdentityID.java similarity index 100% rename from src/us/kbase/auth2/lib/identity/RemoteIdentityID.java rename to src/main/java/us/kbase/auth2/lib/identity/RemoteIdentityID.java diff --git a/src/us/kbase/auth2/lib/storage/AuthStorage.java b/src/main/java/us/kbase/auth2/lib/storage/AuthStorage.java similarity index 100% rename from src/us/kbase/auth2/lib/storage/AuthStorage.java rename to src/main/java/us/kbase/auth2/lib/storage/AuthStorage.java diff --git a/src/us/kbase/auth2/lib/storage/exceptions/AuthStorageException.java b/src/main/java/us/kbase/auth2/lib/storage/exceptions/AuthStorageException.java similarity index 100% rename from src/us/kbase/auth2/lib/storage/exceptions/AuthStorageException.java rename to src/main/java/us/kbase/auth2/lib/storage/exceptions/AuthStorageException.java diff --git a/src/us/kbase/auth2/lib/storage/exceptions/StorageInitException.java b/src/main/java/us/kbase/auth2/lib/storage/exceptions/StorageInitException.java similarity index 100% rename from src/us/kbase/auth2/lib/storage/exceptions/StorageInitException.java rename to src/main/java/us/kbase/auth2/lib/storage/exceptions/StorageInitException.java diff --git a/src/us/kbase/auth2/lib/storage/mongo/Fields.java b/src/main/java/us/kbase/auth2/lib/storage/mongo/Fields.java similarity index 100% rename from src/us/kbase/auth2/lib/storage/mongo/Fields.java rename to src/main/java/us/kbase/auth2/lib/storage/mongo/Fields.java diff --git a/src/us/kbase/auth2/lib/storage/mongo/MongoStorage.java b/src/main/java/us/kbase/auth2/lib/storage/mongo/MongoStorage.java similarity index 100% rename from src/us/kbase/auth2/lib/storage/mongo/MongoStorage.java rename to src/main/java/us/kbase/auth2/lib/storage/mongo/MongoStorage.java diff --git a/src/us/kbase/auth2/lib/token/IncomingHashedToken.java b/src/main/java/us/kbase/auth2/lib/token/IncomingHashedToken.java similarity index 100% rename from src/us/kbase/auth2/lib/token/IncomingHashedToken.java rename to src/main/java/us/kbase/auth2/lib/token/IncomingHashedToken.java diff --git a/src/us/kbase/auth2/lib/token/IncomingToken.java b/src/main/java/us/kbase/auth2/lib/token/IncomingToken.java similarity index 100% rename from src/us/kbase/auth2/lib/token/IncomingToken.java rename to src/main/java/us/kbase/auth2/lib/token/IncomingToken.java diff --git a/src/us/kbase/auth2/lib/token/NewToken.java b/src/main/java/us/kbase/auth2/lib/token/NewToken.java similarity index 100% rename from src/us/kbase/auth2/lib/token/NewToken.java rename to src/main/java/us/kbase/auth2/lib/token/NewToken.java diff --git a/src/us/kbase/auth2/lib/token/StoredToken.java b/src/main/java/us/kbase/auth2/lib/token/StoredToken.java similarity index 100% rename from src/us/kbase/auth2/lib/token/StoredToken.java rename to src/main/java/us/kbase/auth2/lib/token/StoredToken.java diff --git a/src/us/kbase/auth2/lib/token/TemporaryToken.java b/src/main/java/us/kbase/auth2/lib/token/TemporaryToken.java similarity index 100% rename from src/us/kbase/auth2/lib/token/TemporaryToken.java rename to src/main/java/us/kbase/auth2/lib/token/TemporaryToken.java diff --git a/src/us/kbase/auth2/lib/token/TokenName.java b/src/main/java/us/kbase/auth2/lib/token/TokenName.java similarity index 100% rename from src/us/kbase/auth2/lib/token/TokenName.java rename to src/main/java/us/kbase/auth2/lib/token/TokenName.java diff --git a/src/us/kbase/auth2/lib/token/TokenSet.java b/src/main/java/us/kbase/auth2/lib/token/TokenSet.java similarity index 100% rename from src/us/kbase/auth2/lib/token/TokenSet.java rename to src/main/java/us/kbase/auth2/lib/token/TokenSet.java diff --git a/src/us/kbase/auth2/lib/token/TokenType.java b/src/main/java/us/kbase/auth2/lib/token/TokenType.java similarity index 100% rename from src/us/kbase/auth2/lib/token/TokenType.java rename to src/main/java/us/kbase/auth2/lib/token/TokenType.java diff --git a/src/us/kbase/auth2/lib/user/AuthUser.java b/src/main/java/us/kbase/auth2/lib/user/AuthUser.java similarity index 100% rename from src/us/kbase/auth2/lib/user/AuthUser.java rename to src/main/java/us/kbase/auth2/lib/user/AuthUser.java diff --git a/src/us/kbase/auth2/lib/user/LocalUser.java b/src/main/java/us/kbase/auth2/lib/user/LocalUser.java similarity index 100% rename from src/us/kbase/auth2/lib/user/LocalUser.java rename to src/main/java/us/kbase/auth2/lib/user/LocalUser.java diff --git a/src/us/kbase/auth2/lib/user/NewUser.java b/src/main/java/us/kbase/auth2/lib/user/NewUser.java similarity index 100% rename from src/us/kbase/auth2/lib/user/NewUser.java rename to src/main/java/us/kbase/auth2/lib/user/NewUser.java diff --git a/src/us/kbase/auth2/providers/GlobusIdentityProviderFactory.java b/src/main/java/us/kbase/auth2/providers/GlobusIdentityProviderFactory.java similarity index 100% rename from src/us/kbase/auth2/providers/GlobusIdentityProviderFactory.java rename to src/main/java/us/kbase/auth2/providers/GlobusIdentityProviderFactory.java diff --git a/src/us/kbase/auth2/providers/GoogleIdentityProviderFactory.java b/src/main/java/us/kbase/auth2/providers/GoogleIdentityProviderFactory.java similarity index 100% rename from src/us/kbase/auth2/providers/GoogleIdentityProviderFactory.java rename to src/main/java/us/kbase/auth2/providers/GoogleIdentityProviderFactory.java diff --git a/src/us/kbase/auth2/providers/OrcIDIdentityProviderFactory.java b/src/main/java/us/kbase/auth2/providers/OrcIDIdentityProviderFactory.java similarity index 100% rename from src/us/kbase/auth2/providers/OrcIDIdentityProviderFactory.java rename to src/main/java/us/kbase/auth2/providers/OrcIDIdentityProviderFactory.java diff --git a/src/us/kbase/auth2/providers/package-info.java b/src/main/java/us/kbase/auth2/providers/package-info.java similarity index 100% rename from src/us/kbase/auth2/providers/package-info.java rename to src/main/java/us/kbase/auth2/providers/package-info.java diff --git a/src/us/kbase/auth2/service/AppEventListener.java b/src/main/java/us/kbase/auth2/service/AppEventListener.java similarity index 100% rename from src/us/kbase/auth2/service/AppEventListener.java rename to src/main/java/us/kbase/auth2/service/AppEventListener.java diff --git a/src/us/kbase/auth2/service/AuthAPIStaticConfig.java b/src/main/java/us/kbase/auth2/service/AuthAPIStaticConfig.java similarity index 100% rename from src/us/kbase/auth2/service/AuthAPIStaticConfig.java rename to src/main/java/us/kbase/auth2/service/AuthAPIStaticConfig.java diff --git a/src/us/kbase/auth2/service/AuthBuilder.java b/src/main/java/us/kbase/auth2/service/AuthBuilder.java similarity index 100% rename from src/us/kbase/auth2/service/AuthBuilder.java rename to src/main/java/us/kbase/auth2/service/AuthBuilder.java diff --git a/src/us/kbase/auth2/service/AuthExternalConfig.java b/src/main/java/us/kbase/auth2/service/AuthExternalConfig.java similarity index 100% rename from src/us/kbase/auth2/service/AuthExternalConfig.java rename to src/main/java/us/kbase/auth2/service/AuthExternalConfig.java diff --git a/src/us/kbase/auth2/service/AuthStartupConfig.java b/src/main/java/us/kbase/auth2/service/AuthStartupConfig.java similarity index 100% rename from src/us/kbase/auth2/service/AuthStartupConfig.java rename to src/main/java/us/kbase/auth2/service/AuthStartupConfig.java diff --git a/src/us/kbase/auth2/service/AuthenticationService.java b/src/main/java/us/kbase/auth2/service/AuthenticationService.java similarity index 100% rename from src/us/kbase/auth2/service/AuthenticationService.java rename to src/main/java/us/kbase/auth2/service/AuthenticationService.java diff --git a/src/us/kbase/auth2/service/ContextFields.java b/src/main/java/us/kbase/auth2/service/ContextFields.java similarity index 100% rename from src/us/kbase/auth2/service/ContextFields.java rename to src/main/java/us/kbase/auth2/service/ContextFields.java diff --git a/src/us/kbase/auth2/service/LoggingFilter.java b/src/main/java/us/kbase/auth2/service/LoggingFilter.java similarity index 100% rename from src/us/kbase/auth2/service/LoggingFilter.java rename to src/main/java/us/kbase/auth2/service/LoggingFilter.java diff --git a/src/us/kbase/auth2/service/SLF4JAutoLogger.java b/src/main/java/us/kbase/auth2/service/SLF4JAutoLogger.java similarity index 100% rename from src/us/kbase/auth2/service/SLF4JAutoLogger.java rename to src/main/java/us/kbase/auth2/service/SLF4JAutoLogger.java diff --git a/src/us/kbase/auth2/service/UserAgentParser.java b/src/main/java/us/kbase/auth2/service/UserAgentParser.java similarity index 100% rename from src/us/kbase/auth2/service/UserAgentParser.java rename to src/main/java/us/kbase/auth2/service/UserAgentParser.java diff --git a/src/us/kbase/auth2/service/api/APIConstants.java b/src/main/java/us/kbase/auth2/service/api/APIConstants.java similarity index 100% rename from src/us/kbase/auth2/service/api/APIConstants.java rename to src/main/java/us/kbase/auth2/service/api/APIConstants.java diff --git a/src/us/kbase/auth2/service/api/APIPaths.java b/src/main/java/us/kbase/auth2/service/api/APIPaths.java similarity index 100% rename from src/us/kbase/auth2/service/api/APIPaths.java rename to src/main/java/us/kbase/auth2/service/api/APIPaths.java diff --git a/src/us/kbase/auth2/service/api/APIToken.java b/src/main/java/us/kbase/auth2/service/api/APIToken.java similarity index 100% rename from src/us/kbase/auth2/service/api/APIToken.java rename to src/main/java/us/kbase/auth2/service/api/APIToken.java diff --git a/src/us/kbase/auth2/service/api/Admin.java b/src/main/java/us/kbase/auth2/service/api/Admin.java similarity index 100% rename from src/us/kbase/auth2/service/api/Admin.java rename to src/main/java/us/kbase/auth2/service/api/Admin.java diff --git a/src/us/kbase/auth2/service/api/LegacyGlobus.java b/src/main/java/us/kbase/auth2/service/api/LegacyGlobus.java similarity index 100% rename from src/us/kbase/auth2/service/api/LegacyGlobus.java rename to src/main/java/us/kbase/auth2/service/api/LegacyGlobus.java diff --git a/src/us/kbase/auth2/service/api/LegacyKBase.java b/src/main/java/us/kbase/auth2/service/api/LegacyKBase.java similarity index 100% rename from src/us/kbase/auth2/service/api/LegacyKBase.java rename to src/main/java/us/kbase/auth2/service/api/LegacyKBase.java diff --git a/src/us/kbase/auth2/service/api/Me.java b/src/main/java/us/kbase/auth2/service/api/Me.java similarity index 100% rename from src/us/kbase/auth2/service/api/Me.java rename to src/main/java/us/kbase/auth2/service/api/Me.java diff --git a/src/us/kbase/auth2/service/api/NewAPIToken.java b/src/main/java/us/kbase/auth2/service/api/NewAPIToken.java similarity index 100% rename from src/us/kbase/auth2/service/api/NewAPIToken.java rename to src/main/java/us/kbase/auth2/service/api/NewAPIToken.java diff --git a/src/us/kbase/auth2/service/api/TestMode.java b/src/main/java/us/kbase/auth2/service/api/TestMode.java similarity index 100% rename from src/us/kbase/auth2/service/api/TestMode.java rename to src/main/java/us/kbase/auth2/service/api/TestMode.java diff --git a/src/us/kbase/auth2/service/api/Token.java b/src/main/java/us/kbase/auth2/service/api/Token.java similarity index 100% rename from src/us/kbase/auth2/service/api/Token.java rename to src/main/java/us/kbase/auth2/service/api/Token.java diff --git a/src/us/kbase/auth2/service/api/Users.java b/src/main/java/us/kbase/auth2/service/api/Users.java similarity index 100% rename from src/us/kbase/auth2/service/api/Users.java rename to src/main/java/us/kbase/auth2/service/api/Users.java diff --git a/src/us/kbase/auth2/service/common/ExternalToken.java b/src/main/java/us/kbase/auth2/service/common/ExternalToken.java similarity index 100% rename from src/us/kbase/auth2/service/common/ExternalToken.java rename to src/main/java/us/kbase/auth2/service/common/ExternalToken.java diff --git a/src/us/kbase/auth2/service/common/Fields.java b/src/main/java/us/kbase/auth2/service/common/Fields.java similarity index 100% rename from src/us/kbase/auth2/service/common/Fields.java rename to src/main/java/us/kbase/auth2/service/common/Fields.java diff --git a/src/us/kbase/auth2/service/common/IncomingJSON.java b/src/main/java/us/kbase/auth2/service/common/IncomingJSON.java similarity index 100% rename from src/us/kbase/auth2/service/common/IncomingJSON.java rename to src/main/java/us/kbase/auth2/service/common/IncomingJSON.java diff --git a/src/us/kbase/auth2/service/common/ServiceCommon.java b/src/main/java/us/kbase/auth2/service/common/ServiceCommon.java similarity index 100% rename from src/us/kbase/auth2/service/common/ServiceCommon.java rename to src/main/java/us/kbase/auth2/service/common/ServiceCommon.java diff --git a/src/us/kbase/auth2/service/exceptions/AuthConfigurationException.java b/src/main/java/us/kbase/auth2/service/exceptions/AuthConfigurationException.java similarity index 100% rename from src/us/kbase/auth2/service/exceptions/AuthConfigurationException.java rename to src/main/java/us/kbase/auth2/service/exceptions/AuthConfigurationException.java diff --git a/src/us/kbase/auth2/service/exceptions/ErrorMessage.java b/src/main/java/us/kbase/auth2/service/exceptions/ErrorMessage.java similarity index 100% rename from src/us/kbase/auth2/service/exceptions/ErrorMessage.java rename to src/main/java/us/kbase/auth2/service/exceptions/ErrorMessage.java diff --git a/src/us/kbase/auth2/service/exceptions/ExceptionHandler.java b/src/main/java/us/kbase/auth2/service/exceptions/ExceptionHandler.java similarity index 100% rename from src/us/kbase/auth2/service/exceptions/ExceptionHandler.java rename to src/main/java/us/kbase/auth2/service/exceptions/ExceptionHandler.java diff --git a/src/us/kbase/auth2/service/template/TemplateProcessor.java b/src/main/java/us/kbase/auth2/service/template/TemplateProcessor.java similarity index 100% rename from src/us/kbase/auth2/service/template/TemplateProcessor.java rename to src/main/java/us/kbase/auth2/service/template/TemplateProcessor.java diff --git a/src/us/kbase/auth2/service/template/mustache/MustacheProcessor.java b/src/main/java/us/kbase/auth2/service/template/mustache/MustacheProcessor.java similarity index 100% rename from src/us/kbase/auth2/service/template/mustache/MustacheProcessor.java rename to src/main/java/us/kbase/auth2/service/template/mustache/MustacheProcessor.java diff --git a/src/us/kbase/auth2/service/ui/Admin.java b/src/main/java/us/kbase/auth2/service/ui/Admin.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Admin.java rename to src/main/java/us/kbase/auth2/service/ui/Admin.java diff --git a/src/us/kbase/auth2/service/ui/CustomRoles.java b/src/main/java/us/kbase/auth2/service/ui/CustomRoles.java similarity index 100% rename from src/us/kbase/auth2/service/ui/CustomRoles.java rename to src/main/java/us/kbase/auth2/service/ui/CustomRoles.java diff --git a/src/us/kbase/auth2/service/ui/Link.java b/src/main/java/us/kbase/auth2/service/ui/Link.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Link.java rename to src/main/java/us/kbase/auth2/service/ui/Link.java diff --git a/src/us/kbase/auth2/service/ui/LocalAccounts.java b/src/main/java/us/kbase/auth2/service/ui/LocalAccounts.java similarity index 100% rename from src/us/kbase/auth2/service/ui/LocalAccounts.java rename to src/main/java/us/kbase/auth2/service/ui/LocalAccounts.java diff --git a/src/us/kbase/auth2/service/ui/Login.java b/src/main/java/us/kbase/auth2/service/ui/Login.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Login.java rename to src/main/java/us/kbase/auth2/service/ui/Login.java diff --git a/src/us/kbase/auth2/service/ui/Logout.java b/src/main/java/us/kbase/auth2/service/ui/Logout.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Logout.java rename to src/main/java/us/kbase/auth2/service/ui/Logout.java diff --git a/src/us/kbase/auth2/service/ui/Me.java b/src/main/java/us/kbase/auth2/service/ui/Me.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Me.java rename to src/main/java/us/kbase/auth2/service/ui/Me.java diff --git a/src/us/kbase/auth2/service/ui/NewUIToken.java b/src/main/java/us/kbase/auth2/service/ui/NewUIToken.java similarity index 100% rename from src/us/kbase/auth2/service/ui/NewUIToken.java rename to src/main/java/us/kbase/auth2/service/ui/NewUIToken.java diff --git a/src/us/kbase/auth2/service/ui/Root.java b/src/main/java/us/kbase/auth2/service/ui/Root.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Root.java rename to src/main/java/us/kbase/auth2/service/ui/Root.java diff --git a/src/us/kbase/auth2/service/ui/Tokens.java b/src/main/java/us/kbase/auth2/service/ui/Tokens.java similarity index 100% rename from src/us/kbase/auth2/service/ui/Tokens.java rename to src/main/java/us/kbase/auth2/service/ui/Tokens.java diff --git a/src/us/kbase/auth2/service/ui/UIConstants.java b/src/main/java/us/kbase/auth2/service/ui/UIConstants.java similarity index 100% rename from src/us/kbase/auth2/service/ui/UIConstants.java rename to src/main/java/us/kbase/auth2/service/ui/UIConstants.java diff --git a/src/us/kbase/auth2/service/ui/UIPaths.java b/src/main/java/us/kbase/auth2/service/ui/UIPaths.java similarity index 100% rename from src/us/kbase/auth2/service/ui/UIPaths.java rename to src/main/java/us/kbase/auth2/service/ui/UIPaths.java diff --git a/src/us/kbase/auth2/service/ui/UIToken.java b/src/main/java/us/kbase/auth2/service/ui/UIToken.java similarity index 100% rename from src/us/kbase/auth2/service/ui/UIToken.java rename to src/main/java/us/kbase/auth2/service/ui/UIToken.java diff --git a/src/us/kbase/auth2/service/ui/UIUtils.java b/src/main/java/us/kbase/auth2/service/ui/UIUtils.java similarity index 100% rename from src/us/kbase/auth2/service/ui/UIUtils.java rename to src/main/java/us/kbase/auth2/service/ui/UIUtils.java diff --git a/src/us/kbase/test/auth2/MapBuilder.java b/src/test/java/us/kbase/test/auth2/MapBuilder.java similarity index 100% rename from src/us/kbase/test/auth2/MapBuilder.java rename to src/test/java/us/kbase/test/auth2/MapBuilder.java diff --git a/src/us/kbase/test/auth2/MockIdentityProviderFactory.java b/src/test/java/us/kbase/test/auth2/MockIdentityProviderFactory.java similarity index 100% rename from src/us/kbase/test/auth2/MockIdentityProviderFactory.java rename to src/test/java/us/kbase/test/auth2/MockIdentityProviderFactory.java diff --git a/src/us/kbase/test/auth2/MongoStorageTestManager.java b/src/test/java/us/kbase/test/auth2/MongoStorageTestManager.java similarity index 100% rename from src/us/kbase/test/auth2/MongoStorageTestManager.java rename to src/test/java/us/kbase/test/auth2/MongoStorageTestManager.java diff --git a/src/us/kbase/test/auth2/StandaloneAuthServer.java b/src/test/java/us/kbase/test/auth2/StandaloneAuthServer.java similarity index 100% rename from src/us/kbase/test/auth2/StandaloneAuthServer.java rename to src/test/java/us/kbase/test/auth2/StandaloneAuthServer.java diff --git a/src/us/kbase/test/auth2/TestCommon.java b/src/test/java/us/kbase/test/auth2/TestCommon.java similarity index 100% rename from src/us/kbase/test/auth2/TestCommon.java rename to src/test/java/us/kbase/test/auth2/TestCommon.java diff --git a/src/us/kbase/test/auth2/TestConfigurator.java b/src/test/java/us/kbase/test/auth2/TestConfigurator.java similarity index 100% rename from src/us/kbase/test/auth2/TestConfigurator.java rename to src/test/java/us/kbase/test/auth2/TestConfigurator.java diff --git a/src/us/kbase/test/auth2/authcontroller/AuthController.java b/src/test/java/us/kbase/test/auth2/authcontroller/AuthController.java similarity index 100% rename from src/us/kbase/test/auth2/authcontroller/AuthController.java rename to src/test/java/us/kbase/test/auth2/authcontroller/AuthController.java diff --git a/src/us/kbase/test/auth2/cli/AuthCLITest.java b/src/test/java/us/kbase/test/auth2/cli/AuthCLITest.java similarity index 100% rename from src/us/kbase/test/auth2/cli/AuthCLITest.java rename to src/test/java/us/kbase/test/auth2/cli/AuthCLITest.java diff --git a/src/us/kbase/test/auth2/cryptutils/CryptUtilsTest.java b/src/test/java/us/kbase/test/auth2/cryptutils/CryptUtilsTest.java similarity index 100% rename from src/us/kbase/test/auth2/cryptutils/CryptUtilsTest.java rename to src/test/java/us/kbase/test/auth2/cryptutils/CryptUtilsTest.java diff --git a/src/us/kbase/test/auth2/cryptutils/SHA1RandomDataGeneratorTest.java b/src/test/java/us/kbase/test/auth2/cryptutils/SHA1RandomDataGeneratorTest.java similarity index 100% rename from src/us/kbase/test/auth2/cryptutils/SHA1RandomDataGeneratorTest.java rename to src/test/java/us/kbase/test/auth2/cryptutils/SHA1RandomDataGeneratorTest.java diff --git a/src/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java b/src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java rename to src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationConfigTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationConfigTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationConstructorTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationConstructorTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationConstructorTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationConstructorTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationCreateLocalUserTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationCreateLocalUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationCreateLocalUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationCreateLocalUserTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationCreateRootTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationCreateRootTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationCreateRootTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationCreateRootTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationCustomRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationCustomRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationCustomRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationCustomRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationDisableUserTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationDisableUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationDisableUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationDisableUserTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationGetAvailableUserNameTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationGetAvailableUserNameTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationGetAvailableUserNameTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationGetAvailableUserNameTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationGetUserDisplayNamesTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationGetUserDisplayNamesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationGetUserDisplayNamesTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationGetUserDisplayNamesTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationGetUserTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationGetUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationGetUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationGetUserTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationIdentityProviderTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationIdentityProviderTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationIdentityProviderTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationIdentityProviderTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationImportUserTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationImportUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationImportUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationImportUserTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationLinkTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationLinkTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationLinkTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationLinkTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationLoginTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationLoginTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationLoginTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationLoginTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationPasswordLoginTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationPasswordLoginTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationPasswordLoginTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationPasswordLoginTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationPolicyIDTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationPolicyIDTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationPolicyIDTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationPolicyIDTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTestModeGetUserDisplayNamesTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeGetUserDisplayNamesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTestModeGetUserDisplayNamesTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeGetUserDisplayNamesTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTestModeRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTestModeRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTestModeTokenTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeTokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTestModeTokenTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeTokenTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTestModeUserTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTestModeUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTestModeUserTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTester.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTester.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTester.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTester.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationTokenTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationTokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationTokenTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationTokenTest.java diff --git a/src/us/kbase/test/auth2/lib/AuthenticationUserUpdateTest.java b/src/test/java/us/kbase/test/auth2/lib/AuthenticationUserUpdateTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/AuthenticationUserUpdateTest.java rename to src/test/java/us/kbase/test/auth2/lib/AuthenticationUserUpdateTest.java diff --git a/src/us/kbase/test/auth2/lib/CustomRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/CustomRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/CustomRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/CustomRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/DisplayNameTest.java b/src/test/java/us/kbase/test/auth2/lib/DisplayNameTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/DisplayNameTest.java rename to src/test/java/us/kbase/test/auth2/lib/DisplayNameTest.java diff --git a/src/us/kbase/test/auth2/lib/EmailAddressTest.java b/src/test/java/us/kbase/test/auth2/lib/EmailAddressTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/EmailAddressTest.java rename to src/test/java/us/kbase/test/auth2/lib/EmailAddressTest.java diff --git a/src/us/kbase/test/auth2/lib/LinkIdentitiesTest.java b/src/test/java/us/kbase/test/auth2/lib/LinkIdentitiesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/LinkIdentitiesTest.java rename to src/test/java/us/kbase/test/auth2/lib/LinkIdentitiesTest.java diff --git a/src/us/kbase/test/auth2/lib/LinkTokenTest.java b/src/test/java/us/kbase/test/auth2/lib/LinkTokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/LinkTokenTest.java rename to src/test/java/us/kbase/test/auth2/lib/LinkTokenTest.java diff --git a/src/us/kbase/test/auth2/lib/LocalLoginResultTest.java b/src/test/java/us/kbase/test/auth2/lib/LocalLoginResultTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/LocalLoginResultTest.java rename to src/test/java/us/kbase/test/auth2/lib/LocalLoginResultTest.java diff --git a/src/us/kbase/test/auth2/lib/LoginStateTest.java b/src/test/java/us/kbase/test/auth2/lib/LoginStateTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/LoginStateTest.java rename to src/test/java/us/kbase/test/auth2/lib/LoginStateTest.java diff --git a/src/us/kbase/test/auth2/lib/LoginTokenTest.java b/src/test/java/us/kbase/test/auth2/lib/LoginTokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/LoginTokenTest.java rename to src/test/java/us/kbase/test/auth2/lib/LoginTokenTest.java diff --git a/src/us/kbase/test/auth2/lib/NameTest.java b/src/test/java/us/kbase/test/auth2/lib/NameTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/NameTest.java rename to src/test/java/us/kbase/test/auth2/lib/NameTest.java diff --git a/src/us/kbase/test/auth2/lib/OAuth2StartDataTest.java b/src/test/java/us/kbase/test/auth2/lib/OAuth2StartDataTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/OAuth2StartDataTest.java rename to src/test/java/us/kbase/test/auth2/lib/OAuth2StartDataTest.java diff --git a/src/us/kbase/test/auth2/lib/PasswordHashAndSaltTest.java b/src/test/java/us/kbase/test/auth2/lib/PasswordHashAndSaltTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/PasswordHashAndSaltTest.java rename to src/test/java/us/kbase/test/auth2/lib/PasswordHashAndSaltTest.java diff --git a/src/us/kbase/test/auth2/lib/PasswordTest.java b/src/test/java/us/kbase/test/auth2/lib/PasswordTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/PasswordTest.java rename to src/test/java/us/kbase/test/auth2/lib/PasswordTest.java diff --git a/src/us/kbase/test/auth2/lib/PolicyIDTest.java b/src/test/java/us/kbase/test/auth2/lib/PolicyIDTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/PolicyIDTest.java rename to src/test/java/us/kbase/test/auth2/lib/PolicyIDTest.java diff --git a/src/us/kbase/test/auth2/lib/RoleTest.java b/src/test/java/us/kbase/test/auth2/lib/RoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/RoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/RoleTest.java diff --git a/src/us/kbase/test/auth2/lib/TemporarySessionDataTest.java b/src/test/java/us/kbase/test/auth2/lib/TemporarySessionDataTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/TemporarySessionDataTest.java rename to src/test/java/us/kbase/test/auth2/lib/TemporarySessionDataTest.java diff --git a/src/us/kbase/test/auth2/lib/TokenCreationContextTest.java b/src/test/java/us/kbase/test/auth2/lib/TokenCreationContextTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/TokenCreationContextTest.java rename to src/test/java/us/kbase/test/auth2/lib/TokenCreationContextTest.java diff --git a/src/us/kbase/test/auth2/lib/UserDisabledStateTest.java b/src/test/java/us/kbase/test/auth2/lib/UserDisabledStateTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/UserDisabledStateTest.java rename to src/test/java/us/kbase/test/auth2/lib/UserDisabledStateTest.java diff --git a/src/us/kbase/test/auth2/lib/UserNameTest.java b/src/test/java/us/kbase/test/auth2/lib/UserNameTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/UserNameTest.java rename to src/test/java/us/kbase/test/auth2/lib/UserNameTest.java diff --git a/src/us/kbase/test/auth2/lib/UserSearchSpecTest.java b/src/test/java/us/kbase/test/auth2/lib/UserSearchSpecTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/UserSearchSpecTest.java rename to src/test/java/us/kbase/test/auth2/lib/UserSearchSpecTest.java diff --git a/src/us/kbase/test/auth2/lib/UserUpdateTest.java b/src/test/java/us/kbase/test/auth2/lib/UserUpdateTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/UserUpdateTest.java rename to src/test/java/us/kbase/test/auth2/lib/UserUpdateTest.java diff --git a/src/us/kbase/test/auth2/lib/UtilsTest.java b/src/test/java/us/kbase/test/auth2/lib/UtilsTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/UtilsTest.java rename to src/test/java/us/kbase/test/auth2/lib/UtilsTest.java diff --git a/src/us/kbase/test/auth2/lib/ViewableUserTest.java b/src/test/java/us/kbase/test/auth2/lib/ViewableUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/ViewableUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/ViewableUserTest.java diff --git a/src/us/kbase/test/auth2/lib/config/AuthConfigTest.java b/src/test/java/us/kbase/test/auth2/lib/config/AuthConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/config/AuthConfigTest.java rename to src/test/java/us/kbase/test/auth2/lib/config/AuthConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/config/CollectingExternalConfigTest.java b/src/test/java/us/kbase/test/auth2/lib/config/CollectingExternalConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/config/CollectingExternalConfigTest.java rename to src/test/java/us/kbase/test/auth2/lib/config/CollectingExternalConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/config/FailConfig.java b/src/test/java/us/kbase/test/auth2/lib/config/FailConfig.java similarity index 100% rename from src/us/kbase/test/auth2/lib/config/FailConfig.java rename to src/test/java/us/kbase/test/auth2/lib/config/FailConfig.java diff --git a/src/us/kbase/test/auth2/lib/config/TestExternalConfig.java b/src/test/java/us/kbase/test/auth2/lib/config/TestExternalConfig.java similarity index 100% rename from src/us/kbase/test/auth2/lib/config/TestExternalConfig.java rename to src/test/java/us/kbase/test/auth2/lib/config/TestExternalConfig.java diff --git a/src/us/kbase/test/auth2/lib/exceptions/ExceptionTest.java b/src/test/java/us/kbase/test/auth2/lib/exceptions/ExceptionTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/exceptions/ExceptionTest.java rename to src/test/java/us/kbase/test/auth2/lib/exceptions/ExceptionTest.java diff --git a/src/us/kbase/test/auth2/lib/identity/IdentityProviderConfigTest.java b/src/test/java/us/kbase/test/auth2/lib/identity/IdentityProviderConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/identity/IdentityProviderConfigTest.java rename to src/test/java/us/kbase/test/auth2/lib/identity/IdentityProviderConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/identity/RemoteIdentityTest.java b/src/test/java/us/kbase/test/auth2/lib/identity/RemoteIdentityTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/identity/RemoteIdentityTest.java rename to src/test/java/us/kbase/test/auth2/lib/identity/RemoteIdentityTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageAnonymousIDTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageAnonymousIDTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageAnonymousIDTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageAnonymousIDTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageConfigTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageConfigTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageConfigTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageCustomRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageCustomRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageCustomRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageCustomRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDisableAccountTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDisableAccountTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDisableAccountTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDisableAccountTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageDuplicateKeyCheckerTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageGetDisplayNamesTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageGetDisplayNamesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageGetDisplayNamesTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageGetDisplayNamesTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageInvalidDBDataTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageInvalidDBDataTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageInvalidDBDataTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageInvalidDBDataTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageLinkTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageLinkTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageLinkTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageLinkTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStoragePasswordTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStoragePasswordTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStoragePasswordTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStoragePasswordTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRecanonicalizationTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRecanonicalizationTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRecanonicalizationTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRecanonicalizationTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRolesTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRolesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRolesTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageRolesTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageStartUpTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTempSessionDataTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTempSessionDataTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTempSessionDataTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTempSessionDataTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestCommon.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestCommon.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestCommon.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestCommon.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestGetDisplayNamesTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestGetDisplayNamesTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestGetDisplayNamesTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestGetDisplayNamesTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestRoleTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestRoleTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestRoleTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestRoleTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestTokensTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestTokensTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestTokensTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestTokensTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestUserCreateGetTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestUserCreateGetTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestUserCreateGetTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTestUserCreateGetTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTester.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTokensTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTokensTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTokensTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageTokensTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUpdateUserFieldsTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUpdateUserFieldsTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUpdateUserFieldsTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUpdateUserFieldsTest.java diff --git a/src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUserCreateGetTest.java b/src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUserCreateGetTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUserCreateGetTest.java rename to src/test/java/us/kbase/test/auth2/lib/storage/mongo/MongoStorageUserCreateGetTest.java diff --git a/src/us/kbase/test/auth2/lib/token/TokenNameTest.java b/src/test/java/us/kbase/test/auth2/lib/token/TokenNameTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/token/TokenNameTest.java rename to src/test/java/us/kbase/test/auth2/lib/token/TokenNameTest.java diff --git a/src/us/kbase/test/auth2/lib/token/TokenTest.java b/src/test/java/us/kbase/test/auth2/lib/token/TokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/token/TokenTest.java rename to src/test/java/us/kbase/test/auth2/lib/token/TokenTest.java diff --git a/src/us/kbase/test/auth2/lib/user/AuthUserTest.java b/src/test/java/us/kbase/test/auth2/lib/user/AuthUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/user/AuthUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/user/AuthUserTest.java diff --git a/src/us/kbase/test/auth2/lib/user/LocalUserTest.java b/src/test/java/us/kbase/test/auth2/lib/user/LocalUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/user/LocalUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/user/LocalUserTest.java diff --git a/src/us/kbase/test/auth2/lib/user/NewUserTest.java b/src/test/java/us/kbase/test/auth2/lib/user/NewUserTest.java similarity index 100% rename from src/us/kbase/test/auth2/lib/user/NewUserTest.java rename to src/test/java/us/kbase/test/auth2/lib/user/NewUserTest.java diff --git a/src/us/kbase/test/auth2/providers/GlobusIdentityProviderTest.java b/src/test/java/us/kbase/test/auth2/providers/GlobusIdentityProviderTest.java similarity index 100% rename from src/us/kbase/test/auth2/providers/GlobusIdentityProviderTest.java rename to src/test/java/us/kbase/test/auth2/providers/GlobusIdentityProviderTest.java diff --git a/src/us/kbase/test/auth2/providers/GoogleIdentityProviderTest.java b/src/test/java/us/kbase/test/auth2/providers/GoogleIdentityProviderTest.java similarity index 100% rename from src/us/kbase/test/auth2/providers/GoogleIdentityProviderTest.java rename to src/test/java/us/kbase/test/auth2/providers/GoogleIdentityProviderTest.java diff --git a/src/us/kbase/test/auth2/providers/OrcIDIdentityProviderTest.java b/src/test/java/us/kbase/test/auth2/providers/OrcIDIdentityProviderTest.java similarity index 100% rename from src/us/kbase/test/auth2/providers/OrcIDIdentityProviderTest.java rename to src/test/java/us/kbase/test/auth2/providers/OrcIDIdentityProviderTest.java diff --git a/src/us/kbase/test/auth2/service/AuthExternalConfigTest.java b/src/test/java/us/kbase/test/auth2/service/AuthExternalConfigTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/AuthExternalConfigTest.java rename to src/test/java/us/kbase/test/auth2/service/AuthExternalConfigTest.java diff --git a/src/us/kbase/test/auth2/service/LoggingFilterTest.java b/src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/LoggingFilterTest.java rename to src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java diff --git a/src/us/kbase/test/auth2/service/ServiceTestUtils.java b/src/test/java/us/kbase/test/auth2/service/ServiceTestUtils.java similarity index 100% rename from src/us/kbase/test/auth2/service/ServiceTestUtils.java rename to src/test/java/us/kbase/test/auth2/service/ServiceTestUtils.java diff --git a/src/us/kbase/test/auth2/service/api/APITokenTest.java b/src/test/java/us/kbase/test/auth2/service/api/APITokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/APITokenTest.java rename to src/test/java/us/kbase/test/auth2/service/api/APITokenTest.java diff --git a/src/us/kbase/test/auth2/service/api/AdminIntegrationTest.java b/src/test/java/us/kbase/test/auth2/service/api/AdminIntegrationTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/AdminIntegrationTest.java rename to src/test/java/us/kbase/test/auth2/service/api/AdminIntegrationTest.java diff --git a/src/us/kbase/test/auth2/service/api/AdminTest.java b/src/test/java/us/kbase/test/auth2/service/api/AdminTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/AdminTest.java rename to src/test/java/us/kbase/test/auth2/service/api/AdminTest.java diff --git a/src/us/kbase/test/auth2/service/api/TestModeIntegrationTest.java b/src/test/java/us/kbase/test/auth2/service/api/TestModeIntegrationTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/TestModeIntegrationTest.java rename to src/test/java/us/kbase/test/auth2/service/api/TestModeIntegrationTest.java diff --git a/src/us/kbase/test/auth2/service/api/TestModeTest.java b/src/test/java/us/kbase/test/auth2/service/api/TestModeTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/TestModeTest.java rename to src/test/java/us/kbase/test/auth2/service/api/TestModeTest.java diff --git a/src/us/kbase/test/auth2/service/api/TokenEndpointTest.java b/src/test/java/us/kbase/test/auth2/service/api/TokenEndpointTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/TokenEndpointTest.java rename to src/test/java/us/kbase/test/auth2/service/api/TokenEndpointTest.java diff --git a/src/us/kbase/test/auth2/service/api/UserEndpointTest.java b/src/test/java/us/kbase/test/auth2/service/api/UserEndpointTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/UserEndpointTest.java rename to src/test/java/us/kbase/test/auth2/service/api/UserEndpointTest.java diff --git a/src/us/kbase/test/auth2/service/api/package-info.java b/src/test/java/us/kbase/test/auth2/service/api/package-info.java similarity index 100% rename from src/us/kbase/test/auth2/service/api/package-info.java rename to src/test/java/us/kbase/test/auth2/service/api/package-info.java diff --git a/src/us/kbase/test/auth2/service/common/ExternalTokenTest.java b/src/test/java/us/kbase/test/auth2/service/common/ExternalTokenTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/ExternalTokenTest.java rename to src/test/java/us/kbase/test/auth2/service/common/ExternalTokenTest.java diff --git a/src/us/kbase/test/auth2/service/common/FailOnInstantiation.java b/src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiation.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/FailOnInstantiation.java rename to src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiation.java diff --git a/src/us/kbase/test/auth2/service/common/FailOnInstantiationNoNullaryConstructor.java b/src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiationNoNullaryConstructor.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/FailOnInstantiationNoNullaryConstructor.java rename to src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiationNoNullaryConstructor.java diff --git a/src/us/kbase/test/auth2/service/common/FailOnInstantiationPrivateConstructor.java b/src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiationPrivateConstructor.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/FailOnInstantiationPrivateConstructor.java rename to src/test/java/us/kbase/test/auth2/service/common/FailOnInstantiationPrivateConstructor.java diff --git a/src/us/kbase/test/auth2/service/common/IncomingJSONTest.java b/src/test/java/us/kbase/test/auth2/service/common/IncomingJSONTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/IncomingJSONTest.java rename to src/test/java/us/kbase/test/auth2/service/common/IncomingJSONTest.java diff --git a/src/us/kbase/test/auth2/service/common/ServiceCommonTest.java b/src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/ServiceCommonTest.java rename to src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java diff --git a/src/us/kbase/test/auth2/service/common/package-info.java b/src/test/java/us/kbase/test/auth2/service/common/package-info.java similarity index 100% rename from src/us/kbase/test/auth2/service/common/package-info.java rename to src/test/java/us/kbase/test/auth2/service/common/package-info.java diff --git a/src/us/kbase/test/auth2/service/ui/AdminIntegrationTest.java b/src/test/java/us/kbase/test/auth2/service/ui/AdminIntegrationTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/AdminIntegrationTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/AdminIntegrationTest.java diff --git a/src/us/kbase/test/auth2/service/ui/AdminTest.java b/src/test/java/us/kbase/test/auth2/service/ui/AdminTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/AdminTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/AdminTest.java diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest.java b/src/test/java/us/kbase/test/auth2/service/ui/LinkTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/LinkTest.java diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest.java b/src/test/java/us/kbase/test/auth2/service/ui/LoginTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/LoginTest.java diff --git a/src/us/kbase/test/auth2/service/ui/MeTest.java b/src/test/java/us/kbase/test/auth2/service/ui/MeTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/MeTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/MeTest.java diff --git a/src/us/kbase/test/auth2/service/ui/PKCEChallengeMatcher.java b/src/test/java/us/kbase/test/auth2/service/ui/PKCEChallengeMatcher.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/PKCEChallengeMatcher.java rename to src/test/java/us/kbase/test/auth2/service/ui/PKCEChallengeMatcher.java diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest.java b/src/test/java/us/kbase/test/auth2/service/ui/SimpleEndpointsTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/SimpleEndpointsTest.java diff --git a/src/us/kbase/test/auth2/service/ui/StateMatcher.java b/src/test/java/us/kbase/test/auth2/service/ui/StateMatcher.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/StateMatcher.java rename to src/test/java/us/kbase/test/auth2/service/ui/StateMatcher.java diff --git a/src/us/kbase/test/auth2/service/ui/TokensTest.java b/src/test/java/us/kbase/test/auth2/service/ui/TokensTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/TokensTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/TokensTest.java diff --git a/src/us/kbase/test/auth2/service/ui/UITokensTest.java b/src/test/java/us/kbase/test/auth2/service/ui/UITokensTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/UITokensTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/UITokensTest.java diff --git a/src/us/kbase/test/auth2/service/ui/UIUtilsTest.java b/src/test/java/us/kbase/test/auth2/service/ui/UIUtilsTest.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/UIUtilsTest.java rename to src/test/java/us/kbase/test/auth2/service/ui/UIUtilsTest.java diff --git a/src/us/kbase/test/auth2/service/ui/package-info.java b/src/test/java/us/kbase/test/auth2/service/ui/package-info.java similarity index 100% rename from src/us/kbase/test/auth2/service/ui/package-info.java rename to src/test/java/us/kbase/test/auth2/service/ui/package-info.java diff --git a/src/us/kbase/test/auth2/service/ui/AdminIntegrationTest_userDisplay.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/AdminIntegrationTest_userDisplay.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/AdminIntegrationTest_userDisplay.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/AdminIntegrationTest_userDisplay.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTML.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTML.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTML.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTML.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLNoLinks.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLNoLinks.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLNoLinks.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLNoLinks.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLOnlyLinks.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLOnlyLinks.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLOnlyLinks.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLOnlyLinks.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLTrailingSlash.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLTrailingSlash.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLTrailingSlash.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkChoiceHTMLTrailingSlash.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayNoProviders.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayNoProviders.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayNoProviders.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayNoProviders.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithLocalUser.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithLocalUser.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithLocalUser.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithLocalUser.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithOneProvider.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithOneProvider.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithOneProvider.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithOneProvider.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithTwoProviders.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithTwoProviders.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithTwoProviders.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LinkTest_linkDisplayWithTwoProviders.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateAndLoginDisabled.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateAndLoginDisabled.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateAndLoginDisabled.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateAndLoginDisabled.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateWithRedirectURL.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateWithRedirectURL.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateWithRedirectURL.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2CreateWithRedirectURL.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2LoginWithRedirectAndLoginDisabled.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2LoginWithRedirectAndLoginDisabled.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2LoginWithRedirectAndLoginDisabled.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice2LoginWithRedirectAndLoginDisabled.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice3Create2Login.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice3Create2Login.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_loginChoice3Create2Login.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_loginChoice3Create2Login.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayLoginDisabled.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayLoginDisabled.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayLoginDisabled.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayLoginDisabled.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithOneProvider.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithOneProvider.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithOneProvider.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithOneProvider.testdata diff --git a/src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithTwoProviders.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithTwoProviders.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithTwoProviders.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/LoginTest_startDisplayWithTwoProviders.testdata diff --git a/src/us/kbase/test/auth2/service/ui/MeTest_getMeMaximalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/MeTest_getMeMaximalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/MeTest_getMeMaximalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/MeTest_getMeMaximalInput.testdata diff --git a/src/us/kbase/test/auth2/service/ui/MeTest_getMeMinimalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/MeTest_getMeMinimalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/MeTest_getMeMinimalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/MeTest_getMeMinimalInput.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesEmpty.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesEmpty.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesEmpty.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesEmpty.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesFull.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesFull.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesFull.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_customRolesFull.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginDisplay.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginDisplay.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginDisplay.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginDisplay.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayNoUser.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayNoUser.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayNoUser.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayNoUser.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayWithUser.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayWithUser.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayWithUser.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_localLoginResetDisplayWithUser.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutDisplay.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutDisplay.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutDisplay.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutDisplay.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithBadTokenHTML.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithBadTokenHTML.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithBadTokenHTML.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithBadTokenHTML.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithGoodTokenHTML.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithGoodTokenHTML.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithGoodTokenHTML.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_logoutWithGoodTokenHTML.testdata diff --git a/src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_rootHTML.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_rootHTML.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_rootHTML.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/SimpleEndpointsTest_rootHTML.testdata diff --git a/src/us/kbase/test/auth2/service/ui/TokensTest_createTokenMaximalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_createTokenMaximalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/TokensTest_createTokenMaximalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_createTokenMaximalInput.testdata diff --git a/src/us/kbase/test/auth2/service/ui/TokensTest_createTokenMinimalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_createTokenMinimalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/TokensTest_createTokenMinimalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_createTokenMinimalInput.testdata diff --git a/src/us/kbase/test/auth2/service/ui/TokensTest_getTokensMaximalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_getTokensMaximalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/TokensTest_getTokensMaximalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_getTokensMaximalInput.testdata diff --git a/src/us/kbase/test/auth2/service/ui/TokensTest_getTokensMinimalInput.testdata b/src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_getTokensMinimalInput.testdata similarity index 100% rename from src/us/kbase/test/auth2/service/ui/TokensTest_getTokensMinimalInput.testdata rename to src/test/resources/us/kbase/test/auth2/service/ui/TokensTest_getTokensMinimalInput.testdata From c7d7ce32a34ae76d68ddc3596e15655f3fb79315 Mon Sep 17 00:00:00 2001 From: Gavin Date: Fri, 9 Feb 2024 19:29:22 -0800 Subject: [PATCH 19/25] Bump version to 0.6.1 --- docker-compose.yml | 1 + src/main/java/us/kbase/auth2/Version.java | 2 +- .../us/kbase/test/auth2/service/common/ServiceCommonTest.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9280439e..1e3187a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: environment: mongo_host: "mongo:27017" test_mode_enabled: "true" + identity_providers: "" command: - "-wait" - "tcp://mongo:27017" diff --git a/src/main/java/us/kbase/auth2/Version.java b/src/main/java/us/kbase/auth2/Version.java index 67d4f7bb..ef29049f 100644 --- a/src/main/java/us/kbase/auth2/Version.java +++ b/src/main/java/us/kbase/auth2/Version.java @@ -5,6 +5,6 @@ public class Version { /** The version of the KBase Auth2 service. */ - public static final String VERSION = "0.6.0"; + public static final String VERSION = "0.6.1"; } diff --git a/src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java b/src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java index 5edda50b..9d8b9eb0 100644 --- a/src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java +++ b/src/test/java/us/kbase/test/auth2/service/common/ServiceCommonTest.java @@ -47,7 +47,7 @@ public class ServiceCommonTest { public static final String SERVICE_NAME = "Authentication Service"; - public static final String SERVER_VER = "0.6.0"; + public static final String SERVER_VER = "0.6.1"; public static final String GIT_ERR = "Missing git commit file gitcommit, should be in us.kbase.auth2"; From 060e222e5e5b6c68a67a3bc6c793430d1e81b42a Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 14 Feb 2024 13:08:40 -0800 Subject: [PATCH 20/25] Remove global token revocation button Too dangerous as a simple button. Also make the URLs in the config page easier to read. --- README.md | 21 +++++++++++++++++++++ build.gradle | 2 +- templates/adminconfig.mustache | 16 ++++++++-------- templates/admingeneral.mustache | 5 ++--- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0e692dae..a322429d 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,27 @@ local account and assign it the create administrator role. That account can then be used to create further administrators (including itself) without needing to login as root. The root account can then be disabled. +### Revoking tokens in an emergency + +The simple HTML only test UI included with the server supports most administration functions, +but revoking all tokens in the service is not included as it has a major impact on systems the +auth server supports, essentially shutting them down. If all tokens are revoked, the tokens for +every single token type (agent, service, etc.) for every single user in the auth system are +removed - the equivalent of clearing the tokens collection in MongoDB. + +This feature is intended to be used in an emergency such as many tokens becoming compromised or +the system needs to come to a near immediate halt (near immediate since services outside the auth +server may cache and accept tokens for some period of time after they're invalidated in auth). + +To revoke all tokens, issue the following request to the server (curl used as an example): + +``` +curl -X POST --cookie "kbase_session=" http:///admin/revokeall +``` + +If the `token-cookie-name` deployment configuration value is not `kbase_session` change +the request to match. + ## Start & stop server w/o a pid `./jettybase$ java -DSTOP.PORT=8079 -DSTOP.KEY=foo -jar ~/jetty/jetty-distribution-9.3.11.v20160721/start.jar` diff --git a/build.gradle b/build.gradle index b03a2b6d..64258ed4 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ def TEMPLATE_LIST_FILE_NAME = "templates.manifest" task buildGitCommitFile { doLast { def commitId = grgit.head().id - // is there a variable for builddir/classe/java/main? + // is there a variable for builddir/classes/java/main? file("$buildDir/classes/java/main/us/kbase/auth2/gitcommit").text = commitId } } diff --git a/templates/adminconfig.mustache b/templates/adminconfig.mustache index 800b5358..6e967912 100644 --- a/templates/adminconfig.mustache +++ b/templates/adminconfig.mustache @@ -28,24 +28,24 @@ The stack trace is always logged.

Allowed post-login redirect URL prefix: -

Redirect URL when a user cannot be logged in immediately after return from 3rd party provider, e.g. a choice of accounts is required or an account must be created. -

Redirect URL after an account link (usually to a user page showing their identities). -

Redirect URL when a user account cannot be linked immediately after return from 3rd party provider, e.g. a choice of accounts is required. -

@@ -87,24 +87,24 @@ provider, e.g. a choice of accounts is required.

Allowed post-login redirect URL prefix: -

Redirect URL when a user cannot be logged in immediately after return from 3rd party provider, e.g. a choice of accounts is required or an account must be created. -

Redirect URL after an account link (usually to a user page showing their identities). -

Redirect URL when a user account cannot be linked immediately after return from 3rd party provider, e.g. a choice of accounts is required. -

diff --git a/templates/admingeneral.mustache b/templates/admingeneral.mustache index c98f2306..986a7fcc 100644 --- a/templates/admingeneral.mustache +++ b/templates/admingeneral.mustache @@ -3,9 +3,6 @@
-
- -
View token:
@@ -34,5 +31,7 @@ {{id}}
{{/customroles}} +
+ \ No newline at end of file From ace6651e8bbe24a248bf285f755702ca15d7c3a8 Mon Sep 17 00:00:00 2001 From: Gavin Date: Mon, 12 Feb 2024 16:34:34 -0800 Subject: [PATCH 21/25] Add documentation re setting up environments Also delete the extremely crufty jetty-config.md and some build.gradle cruft --- README.md | 55 ++++++++++----- build.gradle | 4 -- documentation/Environments.md | 63 +++++++++++++++++ ...st_account_due_to_email_provider_change.md | 2 +- jetty-config.md | 67 ------------------- 5 files changed, 101 insertions(+), 90 deletions(-) create mode 100644 documentation/Environments.md delete mode 100644 jetty-config.md diff --git a/README.md b/README.md index a322429d..301e673c 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,9 @@ the user. `DELETE /testmode/api/V2/testmodeonly/clear` Removes all test mode data from the system. -## Admin notes +## Administration + +### Notes * It is expected that this server always runs behind a reverse proxy (such as nginx) that enforces https / TLS and as such the auth server is configured to @@ -224,6 +226,11 @@ Removes all test mode data from the system. * Get Google OAuth2 creds [here](https://console.developers.google.com/apis) * Get OrcID creds [here](https://orcid.org/content/register-client-application-0) * Note that only the public API has been tested with the auth server. + +#### Migration notes + +##### 0.6.0 + * In version 0.6.0, the canonicalization algorithm for user display names changed and the database needs to be updated. * See the `--recanonicalize-display-names` option for the `manage_auth` script @@ -233,39 +240,46 @@ Removes all test mode data from the system. used to remove flags set on database objects to avoid reprocessing if the recanonicalize process does not complete. -## Requirements +### Requirements -Java 8 (OpenJDK OK) -MongoDB 2.6+ (https://www.mongodb.com/) -Jetty 9.3+ (http://www.eclipse.org/jetty/download.html) - (see jetty-config.md for version used for testing) -This repo (git clone https://github.com/kbase/auth2) +* Java 8 (OpenJDK OK) +* MongoDB 2.6+ (https://www.mongodb.com/) +* Jetty 9.3+ (http://www.eclipse.org/jetty/download.html) +* This repo (git clone https://github.com/kbase/auth2) -## To start server +### Starting the server -Either use `docker-compose --build -d`, which is easier and starts the server in test mode -(which can be configured in the docker-compose file), or: +#### Docker -start mongodb -if using mongo auth, create a mongo user -cd into the auth2 repo +The provided `Dockerfile` can be used to build and run an image. See the deployment template +in `deployment/conf/.templates` for the environment variables available to configure the +service - the `deploy.cfg.example` file provides documentation for these variables. -``` +`docker-compose --build -d` can be used to start a MongoDB instance and the auth server in +test mode (which can be configured via environment variables in the compose file). + +#### Manually + +* Start mongodb +* If using mongo auth, create a mongo user +* `cd` into the auth2 repo + +```shell ./gradlew war mkdir -p jettybase/webapps cp build/libs/auth2.war jettybase/webapps/ROOT.war cp templates jettybase/templates ``` -copy `deploy.cfg.example` to `deploy.cfg` and fill in appropriately +* copy `deploy.cfg.example` to `deploy.cfg` and fill in appropriately -``` +```shell export KB_DEPLOYMENT_CONFIG= cd jettybase ./jettybase$ java -jar -Djetty.port= /start.jar ``` -## Administer the server +### Perform initial setup Create the administration script: @@ -274,11 +288,16 @@ Create the administration script: Set a root password: `build/manage_auth -d -r` +* Note that the `deploy.cfg` file only needs accurate MongoDB connection information for use + with the auth CLI. + Login to a local account as `***ROOT***` with the password you set. Create a local account and assign it the create administrator role. That account can then be used to create further administrators (including itself) without needing to login as root. The root account can then be disabled. +To set up alternate login / link environments, see [Environments](documentation/Environments.md). + ### Revoking tokens in an emergency The simple HTML only test UI included with the server supports most administration functions, @@ -300,7 +319,7 @@ curl -X POST --cookie "kbase_session=" http:///admin/revokeal If the `token-cookie-name` deployment configuration value is not `kbase_session` change the request to match. -## Start & stop server w/o a pid +### Start & stop server w/o a pid `./jettybase$ java -DSTOP.PORT=8079 -DSTOP.KEY=foo -jar ~/jetty/jetty-distribution-9.3.11.v20160721/start.jar` `./jettybase$ java -DSTOP.PORT=8079 -DSTOP.KEY=foo -jar ~/jetty/jetty-distribution-9.3.11.v20160721/start.jar --stop` diff --git a/build.gradle b/build.gradle index 64258ed4..fad8346c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,5 @@ /* * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Java application project to get you started. - * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle - * User Manual available at https://docs.gradle.org/7.4.2/userguide/building_java_projects.html */ plugins { diff --git a/documentation/Environments.md b/documentation/Environments.md new file mode 100644 index 00000000..46366e97 --- /dev/null +++ b/documentation/Environments.md @@ -0,0 +1,63 @@ +# Authentication service environments + +The auth service can support multiple environments, allowing it to service multiple hosts. +Alternate environments only affect account login and linking - all the environments share +tokens and are invisible from the perspective of a service contacting the auth server to look up +users or validate tokens. + +## Setup + +There are several steps to setting up an environment. The following steps assume the the default +or standard environment is `host1.org` and is properly configured in the auth service and +3rd party identity providers (IDPs). The new environment is assumed to be served at `host2.org`. +The environment names, respectively, will be `host1` and `host2`. + +* The login and link redirect URLs for `host2` must be registered with each IDP. + * They are usually going to be the same as the redirect URLs for `host1` with the replacement + of `host1.org` with `host2.org`. However, if URLs are rewritten by the remote proxy in + a different manner between `host1` and `host2` that must be taken into account. +* The login and link redirect URLs must be added to the `deploy.cfg` file as described in + `deploy.cfg.example`. If using the Docker image, they need to be added to the environment + in the `env1` and `auth_base_url_env1` keys or the `additional_config` key. + * For example: + +``` +identity-provider-Google-env-host2-login-redirect-url=https://host2.org/auth/login/complete/google +identity-provider-Google-env-host2-link-redirect-url=https://host2.org/auth/link/complete/google +``` + +* The new environment must be activated in the `deploy.cfg` file, either directly or via + environment variable if using Docker: + +``` +identity-provider-envs=, host2 +``` + +* On login or linking requests, the auth server must know which environment to use. There are two + options for providing this information: + * Send a header to the auth server defining the environment. The name of the header is + defined in the `deploy.cfg` `environment-header` key. For example, if the environment + header was set to `X-AUTH-ENV` the auth service would receive requests with the header + `X-AUTH-ENV: host2` in order to set the environment to `host2`. This is the simplest + and preferred approach, as it generally only needs a one line addition to the reverse + proxy configuration for the host, at which point all requests to the auth service get the + environment header. + * The UI code can specify the environment when contacting the auth2 service to start a login or + link flow via the `environment` form parameter. This requires the UI code to be + environment aware which is generally held to be undesired. +* Once the previous steps have been completed the service can be restarted with the updated + configuration. +* The final step requires an admin to go to the `/admin/config` endpoint of the auth server. + The new environment should now be visible in the configuration. While the IDP relevant + configuration is all specified in the `deploy.cfg` file to avoid passing secrets over the API + and to locate cohesive configuration elements together, the host side configuration is + updateable on the fly via the API. The redirect URLs for login and linking must be updated + appropriately for the host. In many cases, this will be simply replacing `host1.org` with + `host2.org` but see the previously mentioned caveats regarding updating the URLs. + +## Notes + +* When using multiple environments, it may be wise to clear the default environment and use + alternate environments for all the environments. This makes it less likely that a + misconfiguration could cause an alternate environment to use settings from the default + environment mistakenly. \ No newline at end of file diff --git a/documentation/recovering_lost_account_due_to_email_provider_change.md b/documentation/recovering_lost_account_due_to_email_provider_change.md index 54325eb2..e63dff47 100644 --- a/documentation/recovering_lost_account_due_to_email_provider_change.md +++ b/documentation/recovering_lost_account_due_to_email_provider_change.md @@ -101,7 +101,7 @@ should be impossible. 1. Add an option to the Auth command line tool to add identities to users to avoid incorrect DB updates. The CLI could, when possible, query the identity provider to ensure the information - provided command line is correct or pull additional information from the provider. + provided on the command line is correct or pull additional information from the provider. 2. Develop procedures for verifying a user's possession of an account and transferring trusted information needed to restore access to an account. 3. Develop and deploy user documentation recommending that users always have at least one diff --git a/jetty-config.md b/jetty-config.md deleted file mode 100644 index e0c5a5bf..00000000 --- a/jetty-config.md +++ /dev/null @@ -1,67 +0,0 @@ - -Java Environment: ------------------ - java.home = /usr/lib/jvm/java-8-openjdk-amd64/jre - java.vm.vendor = Oracle Corporation - java.vm.version = 25.91-b14 - java.vm.name = OpenJDK 64-Bit Server VM - java.vm.info = mixed mode - java.runtime.name = OpenJDK Runtime Environment - java.runtime.version = 1.8.0_91-8u91-b14-0ubuntu4~14.04-b14 - java.io.tmpdir = /tmp - user.dir = *snip*/jettybase - user.language = en - user.country = US - -Jetty Environment: ------------------ - jetty.version = 9.3.11.v20160721 - jetty.tag.version = master - jetty.home = *snip*/jetty/jetty-distribution-9.3.11.v20160721 - jetty.base = *snip*/jettybase - -Config Search Order: --------------------- - - ${jetty.base} -> *snip*/jettybase - ${jetty.home} -> *snip*/jetty/jetty-distribution-9.3.11.v20160721 - - -JVM Arguments: --------------- - (no jvm args specified) - -System Properties: ------------------- - (no system properties specified) - -Properties: ------------ - java.version = 1.8.0_91 - java.version.major = 1 - java.version.minor = 8 - java.version.revision = 0 - java.version.update = 91 - -Jetty Server Classpath: ------------------------ -Version Information on 11 entries in the classpath. -Note: order presented here is how they would appear on the classpath. - changes to the --module=name command line options will be reflected here. - 0: 3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar - 1: 3.1.0.M0 | ${jetty.home}/lib/jetty-schemas-3.1.jar - 2: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-http-9.3.11.v20160721.jar - 3: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-server-9.3.11.v20160721.jar - 4: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-xml-9.3.11.v20160721.jar - 5: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-util-9.3.11.v20160721.jar - 6: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-io-9.3.11.v20160721.jar - 7: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-security-9.3.11.v20160721.jar - 8: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-servlet-9.3.11.v20160721.jar - 9: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-webapp-9.3.11.v20160721.jar -10: 9.3.11.v20160721 | ${jetty.home}/lib/jetty-deploy-9.3.11.v20160721.jar - -Jetty Active XMLs: ------------------- - ${jetty.home}/etc/jetty.xml - ${jetty.home}/etc/jetty-http.xml - ${jetty.home}/etc/jetty-deploy.xml From 93446ef86dee5e8ada839e06c77edebb1bcc1f9d Mon Sep 17 00:00:00 2001 From: Gavin Date: Thu, 15 Feb 2024 14:58:54 -0800 Subject: [PATCH 22/25] Add mongo-retrywrites to config. The new client by default sets retry writes to true, which doesn't work for single servers. This is painful to test locally so we'll need to test in CI. --- RELEASE_NOTES.md | 2 ++ deploy.cfg.example | 3 +++ deployment/conf/.templates/deployment.cfg.templ | 1 + src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java | 8 ++++++++ src/main/java/us/kbase/auth2/service/AuthBuilder.java | 7 ++++--- .../java/us/kbase/auth2/service/AuthStartupConfig.java | 1 + src/test/java/us/kbase/test/auth2/TestConfigurator.java | 5 +++++ .../us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java | 6 ++++++ .../us/kbase/test/auth2/service/LoggingFilterTest.java | 5 +++++ 9 files changed, 35 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index fdf13f62..f1048bdc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ are now located in the `build` directory, including the `manage_auth` script. * The MongoDB clients have been updated to the most recent version and the service tested against Mongo 7. +* Added the ``mongo-retrywrites`` configuration setting in ``deploy.cfg``, defaulting to + ``false``. * The docker-compose file has been updated to start an auth server in test mode. ## 0.6.0 diff --git a/deploy.cfg.example b/deploy.cfg.example index 2657d40c..c41f6350 100644 --- a/deploy.cfg.example +++ b/deploy.cfg.example @@ -9,6 +9,9 @@ mongo-host = # The name of the mongo database to be used as auth storage. mongo-db = +# Whether to enable ('true') the MongoDB retryWrites parameter or not (anything other than 'true'). +# See https://www.mongodb.com/docs/manual/core/retryable-writes/ +mongo-retrywrites = false # If the mongo database is authenticated, the user name of a read/write account. mongo-user = # If the mongo data base is authenticated, the password for the given username. diff --git a/deployment/conf/.templates/deployment.cfg.templ b/deployment/conf/.templates/deployment.cfg.templ index fe2f4b90..6a90b1d9 100644 --- a/deployment/conf/.templates/deployment.cfg.templ +++ b/deployment/conf/.templates/deployment.cfg.templ @@ -1,6 +1,7 @@ [authserv2] mongo-host={{ default .Env.mongo_host "ci-mongo" }} mongo-db={{ default .Env.mongo_db "auth2" }} +mongo-retrywrites={{ default .Env.mongo_retrywrites "false" }} mongo-user={{ default .Env.mongo_user "" }} mongo-pwd={{ default .Env.mongo_pwd "" }} # The name of the cookie in which tokens should be stored in the browser. diff --git a/src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java b/src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java index 218c9020..81e709c8 100644 --- a/src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java +++ b/src/main/java/us/kbase/auth2/kbase/KBaseAuthConfig.java @@ -41,6 +41,7 @@ public class KBaseAuthConfig implements AuthStartupConfig { private static final String KEY_LOG_NAME = "log-name"; private static final String KEY_MONGO_HOST = "mongo-host"; private static final String KEY_MONGO_DB = "mongo-db"; + private static final String KEY_MONGO_RETRY_WRITES = "mongo-retrywrites"; private static final String KEY_MONGO_USER = "mongo-user"; private static final String KEY_MONGO_PWD = "mongo-pwd"; private static final String KEY_COOKIE_NAME = "token-cookie-name"; @@ -68,6 +69,7 @@ public class KBaseAuthConfig implements AuthStartupConfig { private final SLF4JAutoLogger logger; private final String mongoHost; private final String mongoDB; + private final boolean mongoRetryWrites; private final Optional mongoUser; private final Optional mongoPwd; private final String cookieName; @@ -98,6 +100,7 @@ public KBaseAuthConfig(final Path filepath, final boolean nullLogger) templateDir = Paths.get(getString(KEY_TEMPLATE_DIR, cfg, true)); mongoHost = getString(KEY_MONGO_HOST, cfg, true); mongoDB = getString(KEY_MONGO_DB, cfg, true); + mongoRetryWrites = TRUE.equals(getString(KEY_MONGO_RETRY_WRITES, cfg)); mongoUser = Optional.ofNullable(getString(KEY_MONGO_USER, cfg)); Optional mongop = Optional.ofNullable(getString(KEY_MONGO_PWD, cfg)); if (mongoUser.isPresent() ^ mongop.isPresent()) { @@ -342,6 +345,11 @@ public String getMongoDatabase() { return mongoDB; } + @Override + public boolean getMongoRetryWrites() { + return mongoRetryWrites; + } + @Override public Optional getMongoUser() { return mongoUser; diff --git a/src/main/java/us/kbase/auth2/service/AuthBuilder.java b/src/main/java/us/kbase/auth2/service/AuthBuilder.java index e80de19a..27be79c4 100644 --- a/src/main/java/us/kbase/auth2/service/AuthBuilder.java +++ b/src/main/java/us/kbase/auth2/service/AuthBuilder.java @@ -64,9 +64,10 @@ public AuthBuilder( private MongoClient buildMongo(final AuthStartupConfig c) throws StorageInitException { //TODO ZLATER MONGO handle shards & replica sets - final MongoClientSettings.Builder mongoBuilder = MongoClientSettings.builder().applyToClusterSettings( - builder -> builder.hosts(Arrays.asList(new ServerAddress(c.getMongoHost())))); - + final MongoClientSettings.Builder mongoBuilder = MongoClientSettings.builder() + .retryWrites(c.getMongoRetryWrites()) + .applyToClusterSettings(builder -> builder.hosts( + Arrays.asList(new ServerAddress(c.getMongoHost())))); try { if (c.getMongoUser().isPresent()) { final MongoCredential creds = MongoCredential.createCredential( diff --git a/src/main/java/us/kbase/auth2/service/AuthStartupConfig.java b/src/main/java/us/kbase/auth2/service/AuthStartupConfig.java index 82a7a733..72f5b302 100644 --- a/src/main/java/us/kbase/auth2/service/AuthStartupConfig.java +++ b/src/main/java/us/kbase/auth2/service/AuthStartupConfig.java @@ -14,6 +14,7 @@ public interface AuthStartupConfig { Set getIdentityProviderConfigs(); String getMongoHost(); String getMongoDatabase(); + boolean getMongoRetryWrites(); // note both or neither for user & pwd Optional getMongoUser(); Optional getMongoPwd(); diff --git a/src/test/java/us/kbase/test/auth2/TestConfigurator.java b/src/test/java/us/kbase/test/auth2/TestConfigurator.java index 3b0e3362..437ef331 100644 --- a/src/test/java/us/kbase/test/auth2/TestConfigurator.java +++ b/src/test/java/us/kbase/test/auth2/TestConfigurator.java @@ -133,6 +133,11 @@ public String getMongoHost() { public String getMongoDatabase() { return mongoDatabase == null ? System.getProperty(MONGO_DB_KEY) : mongoDatabase; } + + @Override + public boolean getMongoRetryWrites() { + return false; + } @Override public Optional getMongoUser() { diff --git a/src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java b/src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java index e485d442..5662459e 100644 --- a/src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java +++ b/src/test/java/us/kbase/test/auth2/kbase/KBaseAuthConfigTest.java @@ -78,6 +78,7 @@ public void minimalConfigFromProp() throws Exception { assertThat("incorrect mongo host", cfg.getMongoHost(), is("localhost:50000")); assertThat("incorrect mongo db", cfg.getMongoDatabase(), is("mydb")); + assertThat("incorrect retry writes", cfg.getMongoRetryWrites(), is(false)); assertThat("incorrect mongo user", cfg.getMongoUser(), is(Optional.empty())); assertThat("incorrect mongo pwd", cfg.getMongoPwd(), is(Optional.empty())); assertThat("incorrect test mode", cfg.isTestModeEnabled(), is(false)); @@ -96,6 +97,7 @@ public void maximalConfigFromEnv() throws Exception { "[authserv2]", "mongo-host= localhost:50000 ", "mongo-db = mydb ", + "mongo-retrywrites = \t true \t ", "mongo-user= muser", "mongo-pwd = mpwd", "test-mode-enabled= true", @@ -141,6 +143,7 @@ public void maximalConfigFromEnv() throws Exception { assertThat("incorrect mongo host", cfg.getMongoHost(), is("localhost:50000")); assertThat("incorrect mongo db", cfg.getMongoDatabase(), is("mydb")); + assertThat("incorrect retry writes", cfg.getMongoRetryWrites(), is(true)); assertThat("incorrect mongo user", cfg.getMongoUser(), is(Optional.of("muser"))); assertThat("incorrect mongo pwd", Arrays.equals(cfg.getMongoPwd().get(), "mpwd".toCharArray()), is(true)); @@ -224,6 +227,7 @@ public void minimalConfigFromFileNullLogger() throws Exception { assertThat("incorrect mongo host", cfg.getMongoHost(), is("localhost:50000")); assertThat("incorrect mongo db", cfg.getMongoDatabase(), is("mydb")); + assertThat("incorrect retry writes", cfg.getMongoRetryWrites(), is(false)); assertThat("incorrect mongo user", cfg.getMongoUser(), is(Optional.empty())); assertThat("incorrect mongo pwd", cfg.getMongoPwd(), is(Optional.empty())); assertThat("incorrect test mode", cfg.isTestModeEnabled(), is(false)); @@ -242,6 +246,7 @@ public void maximalConfigFromFileStdLogger() throws Exception { "[authserv2]", "mongo-host= localhost:50000 ", "mongo-db = mydb ", + "mongo-retrywrites=true", "mongo-user= muser", "mongo-pwd = mpwd", "test-mode-enabled= true", @@ -273,6 +278,7 @@ public void maximalConfigFromFileStdLogger() throws Exception { assertThat("incorrect mongo host", cfg.getMongoHost(), is("localhost:50000")); assertThat("incorrect mongo db", cfg.getMongoDatabase(), is("mydb")); + assertThat("incorrect retry writes", cfg.getMongoRetryWrites(), is(true)); assertThat("incorrect mongo user", cfg.getMongoUser(), is(Optional.of("muser"))); assertThat("incorrect mongo pwd", Arrays.equals(cfg.getMongoPwd().get(), "mpwd".toCharArray()), is(true)); diff --git a/src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java b/src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java index 4e9ce017..2c79f2ce 100644 --- a/src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java +++ b/src/test/java/us/kbase/test/auth2/service/LoggingFilterTest.java @@ -87,6 +87,11 @@ public String getMongoHost() { public String getMongoDatabase() { return DB_NAME; } + + @Override + public boolean getMongoRetryWrites() { + return false; + } @Override public Optional getMongoUser() { From 833e4aed898acc512e1264a4652ca224cd8aa036 Mon Sep 17 00:00:00 2001 From: Gavin Date: Sat, 17 Feb 2024 12:24:58 -0800 Subject: [PATCH 23/25] Remove unnecessary resources specification Now that they're separated out no need to specify file types --- build.gradle | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build.gradle b/build.gradle index fad8346c..4fff21ec 100644 --- a/build.gradle +++ b/build.gradle @@ -65,15 +65,6 @@ jacocoTestReport { } } -// Custom java project layout -sourceSets { - test { - resources { - include "**/*.testdata" - } - } -} - javadoc { options { links "https://mongodb.github.io/mongo-java-driver/4.11/apidocs/mongodb-driver-sync/" From 0115527fd981725a134702def21c6de35a7af861 Mon Sep 17 00:00:00 2001 From: Gavin Date: Sun, 18 Feb 2024 12:17:03 -0800 Subject: [PATCH 24/25] Clarifications & expansions re environment docs --- README.md | 3 +++ documentation/Environments.md | 25 ++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 301e673c..ec606e80 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,9 @@ Removes all test mode data from the system. proxy_pass http://localhost:20002/; proxy_cookie_path /login /auth/login; proxy_cookie_path /link /auth/link; + + # If using alternate environments (see below) + add_header X-AUTH-ENV "environment_name"; } * Get Globus creds [here](https://developers.globus.org) diff --git a/documentation/Environments.md b/documentation/Environments.md index 46366e97..3d536679 100644 --- a/documentation/Environments.md +++ b/documentation/Environments.md @@ -1,9 +1,19 @@ # Authentication service environments The auth service can support multiple environments, allowing it to service multiple hosts. -Alternate environments only affect account login and linking - all the environments share -tokens and are invisible from the perspective of a service contacting the auth server to look up -users or validate tokens. +Alternate environments only affect the account login and linking flows and are otherwise +not relevant to the auth system once a login or account link is complete. For example, +a service contacting the auth service to validate a login token does not need to concern +itself about which environment was used to create the token, and the auth service itself +does not record any information about which environment was used to create a login +token or link an account. + +## Recommendations + +* When using multiple environments, it may be wise to clear the default environment and use + alternate environments for all the environments. This makes it less likely that a + misconfiguration could cause an alternate environment to use settings from the default + environment mistakenly. ## Setup @@ -14,7 +24,7 @@ The environment names, respectively, will be `host1` and `host2`. * The login and link redirect URLs for `host2` must be registered with each IDP. * They are usually going to be the same as the redirect URLs for `host1` with the replacement - of `host1.org` with `host2.org`. However, if URLs are rewritten by the remote proxy in + of `host1.org` with `host2.org`. However, if URLs are rewritten by the reverse proxy in a different manner between `host1` and `host2` that must be taken into account. * The login and link redirect URLs must be added to the `deploy.cfg` file as described in `deploy.cfg.example`. If using the Docker image, they need to be added to the environment @@ -54,10 +64,3 @@ identity-provider-envs=, host2 updateable on the fly via the API. The redirect URLs for login and linking must be updated appropriately for the host. In many cases, this will be simply replacing `host1.org` with `host2.org` but see the previously mentioned caveats regarding updating the URLs. - -## Notes - -* When using multiple environments, it may be wise to clear the default environment and use - alternate environments for all the environments. This makes it less likely that a - misconfiguration could cause an alternate environment to use settings from the default - environment mistakenly. \ No newline at end of file From a9d1a4a882b21f8ada53dbce3fb7a010406b1496 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 21 Feb 2024 16:35:20 -0800 Subject: [PATCH 25/25] Clarify what is meant by hosts in environments doc --- documentation/Environments.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/documentation/Environments.md b/documentation/Environments.md index 3d536679..62ad9326 100644 --- a/documentation/Environments.md +++ b/documentation/Environments.md @@ -1,6 +1,10 @@ # Authentication service environments -The auth service can support multiple environments, allowing it to service multiple hosts. +The auth service can support multiple environments, allowing it to service multiple domains - +for a KBase example, `narrative.kbase.us`, `appdev.kbase.us`, and `narrative-dev.kbase.us` +are all served by the same auth service. This allows sharing user accounts and tokens between +environments and deploying alternate versions of UIs running on the same backend. + Alternate environments only affect the account login and linking flows and are otherwise not relevant to the auth system once a login or account link is complete. For example, a service contacting the auth service to validate a login token does not need to concern