diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java index 65a48cf538..eb9c6dddde 100644 --- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java +++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/TokenIDAsHTTPBasicCredsFederationFilterTest.java @@ -523,5 +523,10 @@ private Collection fetchTokens(String userName, boolean createdBy) { }); return tokens; } + + @Override + public boolean isMigrationTarget() { + return false; + } } } diff --git a/gateway-server/pom.xml b/gateway-server/pom.xml index 67e79dc935..f1444ba3eb 100644 --- a/gateway-server/pom.xml +++ b/gateway-server/pom.xml @@ -215,6 +215,10 @@ org.apache.commons commons-lang3 + + org.apache.commons + commons-text + commons-net commons-net @@ -467,19 +471,16 @@ org.apache.knox gateway-shell - test org.apache.derby derby - test org.apache.derby derbynet - test diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java index 3d38489216..65d5f069a0 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java @@ -282,6 +282,11 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig { private static final String TOKEN_STATE_SERVER_MANAGED = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.exp.server-managed"; private static final String USERS_CAN_SEE_ALL_TOKENS = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.management.users.can.see.all.tokens"; + private static final String SKIP_TOKEN_MIGRATION= GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.migration.skip"; + private static final String ARCHIVE_MIGRATED_TOKENS= GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.migration.archive.tokens"; + private static final String MIGRATE_EXPIRED_TOKENS= GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.migration.include.expired.tokens"; + private static final String TOKEN_MIGRATION_PRINTS_VERBOSE_MESSAGES= GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.migration.verbose"; + private static final String TOKEN_MIGRATION_PROGRESS_COUNT= GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.migration.progress.count"; private static final String CLOUDERA_MANAGER_DESCRIPTORS_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.descriptors.monitor.interval"; private static final String CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.advanced.service.discovery.config.monitor.interval"; @@ -311,11 +316,11 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig { private static final String KNOX_INCOMING_XFORWARDED_ENABLED = "gateway.incoming.xforwarded.enabled"; //Gateway Database related properties - private static final String GATEWAY_DATABASE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".database.type"; - private static final String GATEWAY_DATABASE_CONN_URL = GATEWAY_CONFIG_FILE_PREFIX + ".database.connection.url"; - private static final String GATEWAY_DATABASE_HOST = GATEWAY_CONFIG_FILE_PREFIX + ".database.host"; - private static final String GATEWAY_DATABASE_PORT = GATEWAY_CONFIG_FILE_PREFIX + ".database.port"; - private static final String GATEWAY_DATABASE_NAME = GATEWAY_CONFIG_FILE_PREFIX + ".database.name"; + public static final String GATEWAY_DATABASE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".database.type"; + public static final String GATEWAY_DATABASE_CONN_URL = GATEWAY_CONFIG_FILE_PREFIX + ".database.connection.url"; + public static final String GATEWAY_DATABASE_HOST = GATEWAY_CONFIG_FILE_PREFIX + ".database.host"; + public static final String GATEWAY_DATABASE_PORT = GATEWAY_CONFIG_FILE_PREFIX + ".database.port"; + public static final String GATEWAY_DATABASE_NAME = GATEWAY_CONFIG_FILE_PREFIX + ".database.name"; private static final String GATEWAY_DATABASE_SSL_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.enabled"; private static final String GATEWAY_DATABASE_VERIFY_SERVER_CERT = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.verify.server.cert"; private static final String GATEWAY_DATABASE_TRUSTSTORE_FILE = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.truststore.file"; @@ -1512,4 +1517,29 @@ private Map> getPathAliases(String qualifier) { return pathAliases; } + @Override + public boolean skipTokenMigration() { + return getBoolean(SKIP_TOKEN_MIGRATION, false); + } + + @Override + public boolean archiveMigratedTokens() { + return getBoolean(ARCHIVE_MIGRATED_TOKENS, false); + } + + @Override + public boolean migrateExpiredTokens() { + return getBoolean(MIGRATE_EXPIRED_TOKENS, false); + } + + @Override + public boolean printVerboseTokenMigrationMessages() { + return getBoolean(TOKEN_MIGRATION_PRINTS_VERBOSE_MESSAGES, true); + } + + @Override + public int getTokenMigrationProgressCount() { + return getInt(TOKEN_MIGRATION_PROGRESS_COUNT, 10); + } + } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/CLIGatewayServices.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/CLIGatewayServices.java index 4fdbf55487..8725787161 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/CLIGatewayServices.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/CLIGatewayServices.java @@ -53,6 +53,8 @@ public void init(GatewayConfig config, Map options) throws Servic addService(ServiceType.CRYPTO_SERVICE, gatewayServiceFactory.create(this, ServiceType.CRYPTO_SERVICE, config, options)); addService(ServiceType.TOPOLOGY_SERVICE, gatewayServiceFactory.create(this, ServiceType.TOPOLOGY_SERVICE, config, options)); + + addService(ServiceType.TOKEN_STATE_SERVICE, gatewayServiceFactory.create(this, ServiceType.TOKEN_STATE_SERVICE, config, options)); } @Override diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactory.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactory.java index 30a1f06021..002bf7f199 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactory.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactory.java @@ -32,6 +32,7 @@ import org.apache.knox.gateway.services.ServiceType; import org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService; import org.apache.knox.gateway.services.token.impl.DefaultTokenStateService; +import org.apache.knox.gateway.services.token.impl.DerbyDBTokenStateService; import org.apache.knox.gateway.services.token.impl.JDBCTokenStateService; import org.apache.knox.gateway.services.token.impl.JournalBasedTokenStateService; import org.apache.knox.gateway.services.token.impl.ZookeeperTokenStateService; @@ -47,7 +48,7 @@ protected Service createService(GatewayServices gatewayServices, ServiceType ser if (shouldCreateService(implementation)) { if (matchesImplementation(implementation, DefaultTokenStateService.class)) { service = new DefaultTokenStateService(); - } else if (matchesImplementation(implementation, AliasBasedTokenStateService.class, true)) { + } else if (matchesImplementation(implementation, AliasBasedTokenStateService.class)) { service = new AliasBasedTokenStateService(); ((AliasBasedTokenStateService) service).setAliasService(getAliasService(gatewayServices)); } else if (matchesImplementation(implementation, JournalBasedTokenStateService.class)) { @@ -61,17 +62,32 @@ protected Service createService(GatewayServices gatewayServices, ServiceType ser service.init(gatewayConfig, options); } catch (ServiceLifecycleException e) { LOG.errorInitializingService(implementation, e.getMessage(), e); - service = new AliasBasedTokenStateService(); - ((AliasBasedTokenStateService) service).setAliasService(getAliasService(gatewayServices)); + service = useDerbyDatabaseTokenStateService(gatewayServices, gatewayConfig, options); } + } else if (matchesImplementation(implementation, DerbyDBTokenStateService.class, true)) { + service = useDerbyDatabaseTokenStateService(gatewayServices, gatewayConfig, options); } - logServiceUsage(isEmptyDefaultImplementation(implementation) ? AliasBasedTokenStateService.class.getName() : implementation, serviceType); + logServiceUsage(service.getClass().getName(), serviceType); } return service; } + private Service useDerbyDatabaseTokenStateService(GatewayServices gatewayServices, GatewayConfig gatewayConfig, Map options) { + Service service; + try { + service = new DerbyDBTokenStateService(); + ((DerbyDBTokenStateService) service).setAliasService(getAliasService(gatewayServices)); + ((DerbyDBTokenStateService) service).setMasterService(getMasterService(gatewayServices)); + service.init(gatewayConfig, options); + } catch (ServiceLifecycleException e) { + LOG.errorInitializingService(DerbyDBTokenStateService.class.getName(), e.getMessage(), e); + service = new DefaultTokenStateService(); + } + return service; + } + @Override protected ServiceType getServiceType() { return ServiceType.TOKEN_STATE_SERVICE; @@ -80,6 +96,6 @@ protected ServiceType getServiceType() { @Override protected Collection getKnownImplementations() { return unmodifiableList(asList(DefaultTokenStateService.class.getName(), AliasBasedTokenStateService.class.getName(), JournalBasedTokenStateService.class.getName(), - ZookeeperTokenStateService.class.getName(), JDBCTokenStateService.class.getName())); + ZookeeperTokenStateService.class.getName(), JDBCTokenStateService.class.getName(), DerbyDBTokenStateService.class.getName())); } } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java index a05dff08a0..6684e591af 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java @@ -52,6 +52,8 @@ /** * A TokenStateService implementation based on the AliasService. + * + * @deprecated Since 2.1.0 */ public class AliasBasedTokenStateService extends AbstractPersistentTokenStateService implements TokenStatePeristerMonitorListener { diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java index cbb783b38c..5c12433fe1 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateService.java @@ -457,4 +457,9 @@ private Collection fetchTokens(String userName, boolean createdBy) { }); return tokens; } + + @Override + public boolean isMigrationTarget() { + return false; + } } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DerbyDBTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DerbyDBTokenStateService.java new file mode 100644 index 0000000000..47f840ea4b --- /dev/null +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DerbyDBTokenStateService.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you 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 + * + * http://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. + */ +package org.apache.knox.gateway.services.token.impl; + +import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_NAME; +import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_TYPE; +import static org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME; +import static org.apache.knox.gateway.util.JDBCUtils.DATABASE_PASSWORD_ALIAS_NAME; +import static org.apache.knox.gateway.util.JDBCUtils.DATABASE_USER_ALIAS_NAME; +import static org.apache.knox.gateway.util.JDBCUtils.DERBY_DB_TYPE; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.hadoop.conf.Configuration; +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.knox.gateway.services.security.MasterService; +import org.apache.knox.gateway.shell.jdbc.derby.DerbyDatabase; +import org.apache.knox.gateway.util.FileUtils; + +public class DerbyDBTokenStateService extends JDBCTokenStateService { + + public static final String DEFAULT_TOKEN_DB_USER_NAME = "knox"; + public static final String DB_NAME = "tokens"; + + private DerbyDatabase derbyDatabase; + private Path derbyDatabaseFolder; + private MasterService masterService; + + public void setMasterService(MasterService masterService) { + this.masterService = masterService; + } + + @Override + public void init(GatewayConfig config, Map options) throws ServiceLifecycleException { + try { + derbyDatabaseFolder = Paths.get(config.getGatewaySecurityDir(), DB_NAME); + startDerby(); + ((Configuration) config).set(GATEWAY_DATABASE_TYPE, DERBY_DB_TYPE); + ((Configuration) config).set(GATEWAY_DATABASE_NAME, derbyDatabaseFolder.toString()); + getAliasService().addAliasForCluster(NO_CLUSTER_NAME, DATABASE_USER_ALIAS_NAME, getDatabaseUserName()); + getAliasService().addAliasForCluster(NO_CLUSTER_NAME, DATABASE_PASSWORD_ALIAS_NAME, getDatabasePassword()); + super.init(config, options); + + // we need the "x" permission too to be able to browse that folder (600 is not enough) + if (Files.exists(derbyDatabaseFolder)) { + FileUtils.chmod("700", derbyDatabaseFolder.toFile()); + } + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceLifecycleException("Error while initiating DerbyDBTokenStateService: " + e, e); + } + } + + private void startDerby() throws Exception { + derbyDatabase = new DerbyDatabase(derbyDatabaseFolder.toString()); + derbyDatabase.create(); + TimeUnit.SECONDS.sleep(1); // give a bit of time for the server to start + } + + private String getDatabasePassword() throws Exception { + final char[] dbPasswordAliasValue = getAliasService().getPasswordFromAliasForGateway(DATABASE_PASSWORD_ALIAS_NAME); + return dbPasswordAliasValue != null ? new String(dbPasswordAliasValue) : new String(masterService.getMasterSecret()); + } + + private String getDatabaseUserName() throws Exception { + final char[] dbUserAliasValue = getAliasService().getPasswordFromAliasForGateway(DATABASE_USER_ALIAS_NAME); + return dbUserAliasValue != null ? new String(dbUserAliasValue) : DEFAULT_TOKEN_DB_USER_NAME; + } + + @Override + public void stop() throws ServiceLifecycleException { + try { + if (derbyDatabase != null) { + derbyDatabase.shutdown(); + } + } catch (Exception e) { + throw new ServiceLifecycleException("Error while shutting down Derby Database", e); + } + } + +} diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java index 7110325afd..2946cc715f 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java @@ -37,6 +37,7 @@ import org.apache.knox.gateway.services.security.token.TokenStateServiceException; import org.apache.knox.gateway.services.security.token.UnknownTokenException; import org.apache.knox.gateway.util.JDBCUtils; +import org.apache.knox.gateway.util.TokenMigrationTool; import org.apache.knox.gateway.util.Tokens; public class JDBCTokenStateService extends AbstractPersistentTokenStateService { @@ -46,10 +47,20 @@ public class JDBCTokenStateService extends AbstractPersistentTokenStateService { private Lock initLock = new ReentrantLock(true); private Lock addMetadataLock = new ReentrantLock(true); + private boolean skipTokenMigration; + private boolean archiveMigratedTokens; + private boolean migrateExpiredTokens; + private boolean verboseTokenMigration; + private int tokenMigrationProgressCount; + public void setAliasService(AliasService aliasService) { this.aliasService = aliasService; } + protected AliasService getAliasService() { + return aliasService; + } + @Override public void init(GatewayConfig config, Map options) throws ServiceLifecycleException { if (!initialized.get()) { @@ -65,12 +76,33 @@ public void init(GatewayConfig config, Map options) throws Servi } catch (Exception e) { throw new ServiceLifecycleException("Error while initiating JDBCTokenStateService: " + e, e); } + + this.skipTokenMigration = config.skipTokenMigration(); + this.archiveMigratedTokens = config.archiveMigratedTokens(); + this.migrateExpiredTokens = config.migrateExpiredTokens(); + this.verboseTokenMigration = config.printVerboseTokenMigrationMessages(); + this.tokenMigrationProgressCount = config.getTokenMigrationProgressCount(); } finally { initLock.unlock(); } } } + @Override + public void start() throws ServiceLifecycleException { + super.start(); + if (skipTokenMigration) { + log.skipTokenMigration(); + } else { + final TokenMigrationTool tokenMigrationTool = new TokenMigrationTool(aliasService, this, null); + tokenMigrationTool.setArchiveMigratedTokens(archiveMigratedTokens); + tokenMigrationTool.setProgressCount(tokenMigrationProgressCount); + tokenMigrationTool.setVerbose(verboseTokenMigration); + tokenMigrationTool.setMigrateExpiredTokens(migrateExpiredTokens); + tokenMigrationTool.migrateTokensFromGatewayCredentialStore(); + } + } + @Override public void addToken(String tokenId, long issueTime, long expiration, long maxLifetimeDuration) { try { @@ -323,4 +355,9 @@ public Collection getDoAsTokens(String createdBy) { return Collections.emptyList(); } } + + @Override + public boolean isMigrationTarget() { + return true; + } } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java index 6487bd1dd6..117887e334 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java @@ -32,6 +32,9 @@ import java.util.Map; import java.util.Set; +/** + * @deprecated Since 2.1.0 + */ public class JournalBasedTokenStateService extends AbstractPersistentTokenStateService { private TokenStateJournal journal; diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java index 166e89ae73..23e4718669 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java @@ -261,4 +261,11 @@ public interface TokenStateServiceMessages { @Message(level = MessageLevel.ERROR, text = "An error occurred while fetching impersonation tokens for user {0} from the database : {1}") void errorFetchingDoAsTokensForUserFromDatabase(String userName, String errorMessage, @StackTrace(level = MessageLevel.DEBUG) Exception e); + + @Message(level = MessageLevel.INFO, text = "Skipping token migration!") + void skipTokenMigration(); + + @Message(level = MessageLevel.INFO, text = "{0}") + void info(String message); + } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateService.java index 1fce481f4d..f6268cb5dc 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateService.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateService.java @@ -38,6 +38,8 @@ * A Zookeeper Token State Service is actually an Alias based TSS where the 'alias service' happens to be the 'zookeeper' implementation. * This means the only important thing that should be overridden here is the init method where the underlying alias service is configured * properly. + * + * @deprecated Since 2.1.0 */ public class ZookeeperTokenStateService extends AliasBasedTokenStateService implements RemoteTokenStateChangeListener { diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/util/JDBCUtils.java b/gateway-server/src/main/java/org/apache/knox/gateway/util/JDBCUtils.java index 8253f4c5b2..efb2c3ea2b 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/util/JDBCUtils.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/JDBCUtils.java @@ -31,7 +31,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.derby.jdbc.ClientDataSource; +import org.apache.derby.jdbc.EmbeddedDataSource; import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.services.security.AliasServiceException; @@ -111,13 +111,11 @@ private static void configurePostgreSQLSsl(GatewayConfig gatewayConfig, AliasSer } private static DataSource createDerbyDatasource(GatewayConfig gatewayConfig, AliasService aliasService) throws AliasServiceException { - final ClientDataSource derbyDatasource = new ClientDataSource(); - derbyDatasource.setDatabaseName(gatewayConfig.getDatabaseName()); - derbyDatasource.setServerName(gatewayConfig.getDatabaseHost()); - derbyDatasource.setPortNumber(gatewayConfig.getDatabasePort()); - derbyDatasource.setUser(getDatabaseUser(aliasService)); - derbyDatasource.setPassword(getDatabasePassword(aliasService)); - return derbyDatasource; + final EmbeddedDataSource embeddedDataSource = new EmbeddedDataSource(); + embeddedDataSource.setDatabaseName(gatewayConfig.getDatabaseName()); + embeddedDataSource.setUser(getDatabaseUser(aliasService)); + embeddedDataSource.setPassword(getDatabasePassword(aliasService)); + return embeddedDataSource; } @@ -195,8 +193,15 @@ public static boolean isTableExists(String tableName, DataSource dataSource) thr public static void createTable(String createSqlFileName, DataSource dataSource, ClassLoader classLoader) throws Exception { final InputStream is = classLoader.getResourceAsStream(createSqlFileName); String createTableSql = IOUtils.toString(is, UTF_8); + if (isDerbyDatasource(dataSource)) { + createTableSql = createTableSql.replaceAll("IF NOT EXISTS ", ""); + } try (Connection connection = dataSource.getConnection(); Statement createTableStatment = connection.createStatement();) { createTableStatment.execute(createTableSql); } } + + private static boolean isDerbyDatasource(DataSource dataSource) { + return dataSource.getClass().getName().contains("derby"); + } } diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java index d2412fb8cd..04cb68d173 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java @@ -78,6 +78,7 @@ import org.apache.knox.gateway.services.security.KeystoreService; import org.apache.knox.gateway.services.security.KeystoreServiceException; import org.apache.knox.gateway.services.security.MasterService; +import org.apache.knox.gateway.services.security.token.TokenStateService; import org.apache.knox.gateway.services.topology.TopologyService; import org.apache.knox.gateway.topology.Provider; import org.apache.knox.gateway.topology.Topology; @@ -131,7 +132,8 @@ public class KnoxCLI extends Configured implements Tool { " [" + RemoteRegistryDeleteDescriptorCommand.USAGE + "]\n" + " [" + RemoteRegistryGetACLCommand.USAGE + "]\n" + " [" + TopologyConverter.USAGE + "]\n" + - " [" + JWKGenerator.USAGE + "]\n"; + " [" + JWKGenerator.USAGE + "]\n" + + " [" + TokenMigration.USAGE + "]\n"; /** allows stdout to be captured if necessary */ public PrintStream out = System.out; @@ -152,6 +154,10 @@ public class KnoxCLI extends Configured implements Tool { private String pass; private boolean groups; private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.HS256; + private int progressCount = 10; + private boolean archiveMigratedTokens; + private boolean migrateExpiredTokens; + private boolean verbose; private String alias; private String remoteRegistryClient; @@ -529,6 +535,16 @@ private int init(String[] args) throws IOException { } } else if (args[i].equalsIgnoreCase("--saveAlias")) { alias = args[++i]; + } else if (args[i].equalsIgnoreCase("migrate-tokens") ) { + command = new TokenMigration(); + } else if (args[i].equalsIgnoreCase("--progressCount") ) { + progressCount = Integer.parseInt(args[++i]); + } else if (args[i].equalsIgnoreCase("--archiveMigrated") ) { + archiveMigratedTokens = Boolean.parseBoolean(args[++i]); + } else if (args[i].equalsIgnoreCase("--migrateExpiredTokens") ) { + migrateExpiredTokens = Boolean.parseBoolean(args[++i]); + } else if (args[i].equalsIgnoreCase("--verbose") ) { + verbose = Boolean.parseBoolean(args[++i]); } else { printKnoxShellUsage(); return -1; @@ -2429,6 +2445,41 @@ public void execute() throws Exception { } } + public class TokenMigration extends Command { + + static final String USAGE = "migrate-tokens [--progressCount num] [--verbose true|false] [--archivedMigrated true|false] [--migrateExpiredTokens true|false]"; + static final String DESC = + "Migrates previously created Knox Tokens from the Gateway credential store into the configured JDBC TokenStateService backend.\n" + + "Options are as follows: \n" + + "--progressCount (optional) indicates the number of tokens after this tool displays progress on the standard output. Defaults to 10.\n" + + "--archiveMigrated (optional) a boolean flag indicating if migrated tokens should not be removed completely. " + + "Instead, tokens are going to be archived in a separate keystore called __tokens-credentials.jceks. Defaults to false\n" + + "--verbose (optional) a boolean flag that controls of a more verbose output on the STDOUT when processing tokens. Defaults to false.\n" + + "--migrateExpiredTokens (optional) a boolean flag indicating whether already expired tokens should be migrated into the configure TSS backend. Defaults to false"; + + @Override + public void execute() throws Exception { + final TokenStateService tokenStateService = services.getService(ServiceType.TOKEN_STATE_SERVICE); + if (tokenStateService.isMigrationTarget()) { + out.println("Migrating tokens from __gateway credential store into the configured TokenStateService backend..."); + final TokenMigrationTool tokenMigrationTool = new TokenMigrationTool(getAliasService(), tokenStateService, out); + tokenMigrationTool.setArchiveMigratedTokens(archiveMigratedTokens); + tokenMigrationTool.setMigrateExpiredTokens(migrateExpiredTokens); + tokenMigrationTool.setProgressCount(progressCount); + tokenMigrationTool.setVerbose(verbose); + tokenMigrationTool.migrateTokensFromGatewayCredentialStore(); + } else { + out.println("This tool is meant to migrate tokens into a JDBC TokenStateService backend. However, the currently configured one (" + + tokenStateService.getClass().getCanonicalName() + ") does not fulfill this requirement!"); + } + } + + @Override + public String getUsage() { + return USAGE + ":\n\n" + DESC; + } + } + private static Properties loadBuildProperties() { Properties properties = new Properties(); try(InputStream inputStream = KnoxCLI.class.getClassLoader().getResourceAsStream( "build.properties" )) { diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/util/TokenMigrationTool.java b/gateway-server/src/main/java/org/apache/knox/gateway/util/TokenMigrationTool.java new file mode 100644 index 0000000000..f63cfa7d61 --- /dev/null +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/TokenMigrationTool.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +package org.apache.knox.gateway.util; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.knox.gateway.i18n.messages.MessagesFactory; +import org.apache.knox.gateway.services.security.AliasService; +import org.apache.knox.gateway.services.security.AliasServiceException; +import org.apache.knox.gateway.services.security.token.TokenMetadata; +import org.apache.knox.gateway.services.security.token.TokenStateService; +import org.apache.knox.gateway.services.token.impl.TokenStateServiceMessages; + +public class TokenMigrationTool { + + private static final String TOKEN_ALIAS_SUFFIX_DELIM = "--"; + private static final String TOKEN_ISSUE_TIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "iss"; + private static final String TOKEN_MAX_LIFETIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "max"; + private static final String TOKEN_META_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "meta"; + private static final TokenStateServiceMessages LOG = MessagesFactory.get(TokenStateServiceMessages.class); + + private final AliasService aliasService; + private final TokenStateService tokenStateService; + private final PrintStream out; + + private int progressCount = 10; + private boolean archiveMigratedTokens; + private boolean migrateExpiredTokens; + private boolean verbose; + + public TokenMigrationTool(AliasService aliasService, TokenStateService tokenStateService, PrintStream out) { + this.aliasService = aliasService; + this.tokenStateService = tokenStateService; + this.out = out; + } + + public void setProgressCount(int progressCount) { + this.progressCount = progressCount; + } + + public void setArchiveMigratedTokens(boolean archiveMigratedTokens) { + this.archiveMigratedTokens = archiveMigratedTokens; + } + + public void setMigrateExpiredTokens(boolean migrateExpiredTokens) { + this.migrateExpiredTokens = migrateExpiredTokens; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public void migrateTokensFromGatewayCredentialStore() { + try { + final Map tokenDataMap = new ConcurrentHashMap<>(); + final long start = System.currentTimeMillis(); + String logMessage = "Loading token aliases from the __gateway credential store. This could take a while."; + log(logMessage); + final Map passwordAliasMap = aliasService.getPasswordsForGateway(); + log("Token aliases loaded in " + (System.currentTimeMillis() - start) + " milliseconds"); + String alias; + for (Map.Entry passwordAliasMapEntry : passwordAliasMap.entrySet()) { + alias = passwordAliasMapEntry.getKey(); + processAlias(passwordAliasMap, passwordAliasMapEntry, alias, tokenDataMap); + } + + final long migrationStart = System.currentTimeMillis(); + final AtomicInteger count = new AtomicInteger(0); + tokenDataMap.entrySet().forEach(entry -> { + int loggedCount = 0; + saveTokenIfComplete(tokenStateService, entry.getKey(), entry.getValue()); + count.incrementAndGet(); + // log some progress (it's very useful in case a huge amount of token related + // aliases in __gateway-credentials.jceks) + if (count.intValue() > 0 && (count.intValue() % progressCount == 0) && loggedCount != count.intValue()) { + loggedCount = count.intValue(); + logProgress(count.intValue(), System.currentTimeMillis() - migrationStart); + } + }); + + logProgress(count.intValue(), System.currentTimeMillis() - migrationStart); + + archiveTokens(tokenDataMap); + + removeTokenAliasesFromGatewayCredentialStore(tokenDataMap); + } catch (AliasServiceException e) { + throw new RuntimeException("Error while migrating tokens from __gateway credential store: " + e.getMessage(), e); + } + } + + private void log(String message) { + LOG.info(message); + if (out != null) { + out.println(message); + } + } + + private void logProgress(int count, long duration) { + log(String.format(Locale.getDefault(), "Processed %d tokens in %d milliseconds", count, duration)); + } + + /* + * + * The AliasBasedTSS implementation persists 4 aliases in __gateway-credentials.jceks: + * - an alias which maps a token ID to its expiration time + * - an alias with '--max' postfix which maps the maximum lifetime of the token identified by the 1st alias + * - an alias with '--iss' postfix which maps the issue time of the token + * - an alias with '-meta' postfix which maps an arbitrary metadata of the token + * + */ + private void processAlias(final Map passwordAliasMap, Map.Entry passwordAliasMapEntry, String alias, + Map tokenDataMap) { + String tokenId = null; + long expiration, maxLifeTime; + if (alias.endsWith(TOKEN_MAX_LIFETIME_POSTFIX)) { + tokenId = alias.substring(0, alias.indexOf(TOKEN_MAX_LIFETIME_POSTFIX)); + tokenDataMap.putIfAbsent(tokenId, new TokenData()); + expiration = convertCharArrayToLong(passwordAliasMap.get(tokenId)); + maxLifeTime = convertCharArrayToLong(passwordAliasMapEntry.getValue()); + tokenDataMap.get(tokenId).expiration = expiration; + tokenDataMap.get(tokenId).maxLifeTime = maxLifeTime; + } else if (alias.endsWith(TOKEN_META_POSTFIX)) { + tokenId = alias.substring(0, alias.indexOf(TOKEN_META_POSTFIX)); + tokenDataMap.putIfAbsent(tokenId, new TokenData()); + tokenDataMap.get(tokenId).metadata = TokenMetadata.fromJSON(new String(passwordAliasMapEntry.getValue())); + } else if (alias.endsWith(TOKEN_ISSUE_TIME_POSTFIX)) { + tokenId = alias.substring(0, alias.indexOf(TOKEN_ISSUE_TIME_POSTFIX)); + tokenDataMap.putIfAbsent(tokenId, new TokenData()); + tokenDataMap.get(tokenId).issueTime = convertCharArrayToLong(passwordAliasMapEntry.getValue()); + } + } + + private long convertCharArrayToLong(char[] charArray) { + return Long.parseLong(new String(charArray)); + } + + private void saveTokenIfComplete(TokenStateService tokenStateService, String tokenId, TokenData tokenData) { + if (tokenId != null && tokenData.isComplete() && !tokenData.isProcessed()) { + if (migrateToken(tokenData)) { + tokenStateService.addToken(tokenId, tokenData.issueTime, tokenData.expiration, tokenData.maxLifeTime); + tokenStateService.addMetadata(tokenId, tokenData.metadata); + if (verbose) { + log("Migrated token " + Tokens.getTokenIDDisplayText(tokenId) + " into the configured TokenStateService backend."); + } + } else { + if (verbose) { + log("Skipping the migration of expired token with ID = " + Tokens.getTokenIDDisplayText(tokenId)); + } + } + } + tokenData.processed = true; + } + + private boolean migrateToken(TokenData tokenData) { + return tokenData.isExpired() ? migrateExpiredTokens : true; + } + + private void archiveTokens(Map tokenDataMap) throws AliasServiceException { + if (archiveMigratedTokens) { + final String cluster = "__tokens"; + log("Archiving token aliases in the " + cluster + " credential store..."); + final long start = System.currentTimeMillis(); + final Map tokenAliasesToArchive = new HashMap<>(); + tokenDataMap.entrySet().forEach(tokenDataMapEntry -> { + String tokenId = tokenDataMapEntry.getKey(); + tokenDataMapEntry.getValue(); + tokenAliasesToArchive.put(tokenId, String.valueOf(tokenDataMapEntry.getValue().expiration)); + tokenAliasesToArchive.put(tokenId + TOKEN_MAX_LIFETIME_POSTFIX, String.valueOf(tokenDataMapEntry.getValue().maxLifeTime)); + tokenAliasesToArchive.put(tokenId + TOKEN_ISSUE_TIME_POSTFIX, String.valueOf(tokenDataMapEntry.getValue().issueTime)); + tokenAliasesToArchive.put(tokenId + TOKEN_META_POSTFIX, tokenDataMapEntry.getValue().metadata.toJSON()); + }); + aliasService.addAliasesForCluster(cluster, tokenAliasesToArchive); + log("Archived token related aliases in the " + cluster + " credential store in " + (System.currentTimeMillis() - start) + " millsieconds "); + } + } + + private void removeTokenAliasesFromGatewayCredentialStore(Map tokenDataMap) throws AliasServiceException { + log("Removing token aliases from the __gateway credential store..."); + final long start = System.currentTimeMillis(); + final Set tokenAliases = new HashSet<>(); + tokenDataMap.entrySet().forEach(tokenDataMapEntry -> { + String tokenId = tokenDataMapEntry.getKey(); + tokenAliases.addAll(Arrays.asList(tokenId, tokenId + TOKEN_MAX_LIFETIME_POSTFIX, tokenId + TOKEN_ISSUE_TIME_POSTFIX, tokenId + TOKEN_META_POSTFIX)); + }); + aliasService.removeAliasesForCluster(AliasService.NO_CLUSTER_NAME, tokenAliases); + log("Removed token related aliases from the __gateway credential store in " + (System.currentTimeMillis() - start) + " milliseconds"); + } + + private class TokenData { + boolean processed; + long issueTime = -1; + long maxLifeTime = -1; + long expiration = -2; // can be set to '-1' meaning it never expires + TokenMetadata metadata; + + boolean isComplete() { + return issueTime != -1 && maxLifeTime != -1 && expiration != -2 && metadata != null; + } + + boolean isProcessed() { + return processed; + } + + boolean isExpired() { + return expiration == -1 ? false : expiration < System.currentTimeMillis(); + } + } + +} diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/ServiceFactoryTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/ServiceFactoryTest.java index 8a89a2e69a..6c1fa68949 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/ServiceFactoryTest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/ServiceFactoryTest.java @@ -17,6 +17,7 @@ */ package org.apache.knox.gateway.services.factory; +import static org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME; import static org.easymock.EasyMock.anyString; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; @@ -25,13 +26,17 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.reflect.FieldUtils; -import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.config.impl.GatewayConfigImpl; import org.apache.knox.gateway.services.GatewayServices; import org.apache.knox.gateway.services.Service; import org.apache.knox.gateway.services.ServiceLifecycleException; @@ -39,9 +44,14 @@ import org.apache.knox.gateway.services.TestService; import org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClientService; import org.apache.knox.gateway.services.security.AliasService; +import org.apache.knox.gateway.services.security.AliasServiceException; import org.apache.knox.gateway.services.security.KeystoreService; import org.apache.knox.gateway.services.security.MasterService; +import org.apache.knox.gateway.services.token.impl.DerbyDBTokenStateService; +import org.apache.knox.gateway.util.JDBCUtils; +import org.apache.knox.test.TestUtils; import org.easymock.EasyMock; +import org.junit.After; import org.junit.Rule; import org.junit.rules.ExpectedException; @@ -52,15 +62,42 @@ class ServiceFactoryTest { public ExpectedException expectedException = ExpectedException.none(); protected final GatewayServices gatewayServices = EasyMock.createNiceMock(GatewayServices.class); - protected final GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class); + protected final GatewayConfigImpl gatewayConfig = EasyMock.createNiceMock(GatewayConfigImpl.class); protected final Map options = new HashMap<>(); + protected File tempDbFolder; protected void initConfig() { + initConfig(false); + } + + protected void initConfig(boolean expectDbCredentialLookup) { + final String masterSecret = "M4st3RSecret!"; final MasterService masterService = EasyMock.createNiceMock(MasterService.class); + expect(masterService.getMasterSecret()).andReturn(masterSecret.toCharArray()).anyTimes(); + replay(masterService); expect(gatewayServices.getService(ServiceType.MASTER_SERVICE)).andReturn(masterService).anyTimes(); final KeystoreService keystoreservice = EasyMock.createNiceMock(KeystoreService.class); expect(gatewayServices.getService(ServiceType.KEYSTORE_SERVICE)).andReturn(keystoreservice).anyTimes(); final AliasService aliasService = EasyMock.createNiceMock(AliasService.class); + if (expectDbCredentialLookup) { + try { + aliasService.addAliasForCluster(NO_CLUSTER_NAME, JDBCUtils.DATABASE_USER_ALIAS_NAME, DerbyDBTokenStateService.DEFAULT_TOKEN_DB_USER_NAME); + EasyMock.expectLastCall().anyTimes(); + aliasService.addAliasForCluster(NO_CLUSTER_NAME, JDBCUtils.DATABASE_PASSWORD_ALIAS_NAME, masterSecret); + EasyMock.expectLastCall().anyTimes(); + expect(aliasService.getPasswordFromAliasForGateway(JDBCUtils.DATABASE_USER_ALIAS_NAME)).andReturn(DerbyDBTokenStateService.DEFAULT_TOKEN_DB_USER_NAME.toCharArray()).anyTimes(); + expect(aliasService.getPasswordFromAliasForGateway(JDBCUtils.DATABASE_PASSWORD_ALIAS_NAME)).andReturn(masterSecret.toCharArray()).anyTimes(); + + // prepare GatewayConfig + expect(gatewayConfig.getDatabaseType()).andReturn(JDBCUtils.DERBY_DB_TYPE).anyTimes(); + tempDbFolder = TestUtils.createTempDir(this.getClass().getName()); + expect(gatewayConfig.getGatewaySecurityDir()).andReturn(tempDbFolder.getAbsolutePath()).anyTimes(); + expect(gatewayConfig.getDatabaseName()).andReturn(Paths.get(tempDbFolder.getAbsolutePath(), DerbyDBTokenStateService.DB_NAME).toString()).anyTimes(); + } catch (AliasServiceException | IOException e) { + // NOP + } + } + replay(aliasService); expect(gatewayServices.getService(ServiceType.ALIAS_SERVICE)).andReturn(aliasService).anyTimes(); final RemoteConfigurationRegistryClientService registryClientService = EasyMock.createNiceMock(RemoteConfigurationRegistryClientService.class); expect(gatewayServices.getService(ServiceType.REMOTE_REGISTRY_CLIENT_SERVICE)).andReturn(registryClientService).anyTimes(); @@ -69,6 +106,13 @@ protected void initConfig() { replay(gatewayConfig); } + @After + public void tearDown() throws IOException { + if (tempDbFolder != null) { + FileUtils.forceDelete(tempDbFolder); + } + } + protected void testBasics(AbstractServiceFactory serviceFactory, ServiceType nonMatchingServiceType, ServiceType matchingServiceType) throws Exception { shouldReturnCorrectServiceType(serviceFactory, matchingServiceType); shouldReturnNullForNonMatchingServiceType(serviceFactory, nonMatchingServiceType); @@ -110,9 +154,21 @@ protected boolean isAliasServiceSet(Service serviceToCheck) throws Exception { return isServiceSet(serviceToCheck, "aliasService"); } + protected boolean isAliasServiceSetOnParent(Service serviceToCheck) throws Exception { + return isServiceSetOnParent(serviceToCheck, "aliasService"); + } + private boolean isServiceSet(Service serviceToCheck, String expectedServiceName) throws Exception { - final Field aliasServiceField = FieldUtils.getDeclaredField(serviceToCheck.getClass(), expectedServiceName, true); - final Object aliasServiceValue = aliasServiceField.get(serviceToCheck); - return aliasServiceValue != null; + return isServiceSet(serviceToCheck.getClass(), serviceToCheck, expectedServiceName); + } + + private boolean isServiceSetOnParent(Service serviceToCheck, String expectedServiceName) throws Exception { + return isServiceSet(serviceToCheck.getClass().getSuperclass(), serviceToCheck, expectedServiceName); + } + + private boolean isServiceSet(Class clazz, Service serviceToCheck, String expectedServiceName) throws Exception { + final Field expectedServiceField = FieldUtils.getDeclaredField(clazz, expectedServiceName, true); + final Object expectedServiceValue = expectedServiceField.get(serviceToCheck); + return expectedServiceValue != null; } } diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactoryTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactoryTest.java index 5a3675e533..c33ecd22a6 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactoryTest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/factory/TokenStateServiceFactoryTest.java @@ -23,56 +23,81 @@ import org.apache.knox.gateway.services.security.token.TokenStateService; import org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService; import org.apache.knox.gateway.services.token.impl.DefaultTokenStateService; +import org.apache.knox.gateway.services.token.impl.DerbyDBTokenStateService; import org.apache.knox.gateway.services.token.impl.JournalBasedTokenStateService; import org.apache.knox.gateway.services.token.impl.ZookeeperTokenStateService; -import org.junit.Before; import org.junit.Test; public class TokenStateServiceFactoryTest extends ServiceFactoryTest { private final TokenStateServiceFactory serviceFactory = new TokenStateServiceFactory(); - @Before - public void setUp() throws Exception { - initConfig(); - } - @Test public void testBasics() throws Exception { + initConfig(); super.testBasics(serviceFactory, ServiceType.MASTER_SERVICE, ServiceType.TOKEN_STATE_SERVICE); } @Test public void shouldReturnDefaultTokenStateService() throws Exception { + initConfig(); TokenStateService tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, DefaultTokenStateService.class.getName()); assertTrue(tokenStateService instanceof DefaultTokenStateService); } @Test - public void shouldReturnAliasBasedTokenStateServiceByDefault() throws Exception { - TokenStateService tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, ""); - assertTrue(tokenStateService instanceof AliasBasedTokenStateService); - assertTrue(isAliasServiceSet(tokenStateService)); + public void shouldReturnDerbyDBTokenStateServiceByDefault() throws Exception { + TokenStateService tokenStateService = null; + try { + initConfig(true); + tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, ""); + assertTrue(tokenStateService instanceof DerbyDBTokenStateService); + } finally { + if (tokenStateService != null) { + tokenStateService.stop(); + } + } } @Test public void shouldReturnAliasBasedTokenStateService() throws Exception { - final TokenStateService tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, - AliasBasedTokenStateService.class.getName()); + initConfig(); + final TokenStateService tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, + options, AliasBasedTokenStateService.class.getName()); assertTrue(tokenStateService instanceof AliasBasedTokenStateService); assertTrue(isAliasServiceSet(tokenStateService)); } @Test public void shouldReturnJournalTokenStateService() throws Exception { + initConfig(); assertTrue(serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, JournalBasedTokenStateService.class.getName()) instanceof JournalBasedTokenStateService); } @Test public void shouldReturnZookeeperTokenStateService() throws Exception { + initConfig(); assertTrue(serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, ZookeeperTokenStateService.class.getName()) instanceof ZookeeperTokenStateService); } + + @Test + public void shouldReturnDerbyDatabaseTokenStateService() throws Exception { + TokenStateService tokenStateService = null; + try { + initConfig(true); + tokenStateService = (TokenStateService) serviceFactory.create(gatewayServices, ServiceType.TOKEN_STATE_SERVICE, gatewayConfig, options, + DerbyDBTokenStateService.class.getName()); + assertTrue(tokenStateService instanceof DerbyDBTokenStateService); + assertTrue(isAliasServiceSetOnParent(tokenStateService)); + assertTrue(isMasterServiceSet(tokenStateService)); + } finally { + if (tokenStateService != null) { + tokenStateService.stop(); + } + } + + } } diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/util/JDBCUtilsTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/util/JDBCUtilsTest.java index f6bca16228..730827ef57 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/util/JDBCUtilsTest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/util/JDBCUtilsTest.java @@ -24,7 +24,7 @@ import com.mysql.cj.jdbc.MysqlDataSource; -import org.apache.derby.jdbc.ClientDataSource; +import org.apache.derby.jdbc.EmbeddedDataSource; import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.services.security.AliasServiceException; @@ -135,23 +135,19 @@ public void shouldReturnDerbyDataSource() throws Exception { final AliasService aliasService = EasyMock.createNiceMock(AliasService.class); EasyMock.expect(aliasService.getPasswordFromAliasForGateway(EasyMock.anyString())).andReturn(null).anyTimes(); EasyMock.replay(gatewayConfig, aliasService); - assertTrue(JDBCUtils.getDataSource(gatewayConfig, aliasService) instanceof ClientDataSource); + assertTrue(JDBCUtils.getDataSource(gatewayConfig, aliasService) instanceof EmbeddedDataSource); } @Test public void derbyDataSourceShouldHaveProperConnectionProperties() throws Exception { final GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class); EasyMock.expect(gatewayConfig.getDatabaseType()).andReturn(JDBCUtils.DERBY_DB_TYPE).anyTimes(); - EasyMock.expect(gatewayConfig.getDatabaseHost()).andReturn("localhost").anyTimes(); - EasyMock.expect(gatewayConfig.getDatabasePort()).andReturn(1527).anyTimes(); EasyMock.expect(gatewayConfig.getDatabaseName()).andReturn("sampleDatabase"); final AliasService aliasService = EasyMock.createNiceMock(AliasService.class); EasyMock.expect(aliasService.getPasswordFromAliasForGateway(JDBCUtils.DATABASE_USER_ALIAS_NAME)).andReturn("user".toCharArray()).anyTimes(); EasyMock.expect(aliasService.getPasswordFromAliasForGateway(JDBCUtils.DATABASE_PASSWORD_ALIAS_NAME)).andReturn("password".toCharArray()).anyTimes(); EasyMock.replay(gatewayConfig, aliasService); - final ClientDataSource dataSource = (ClientDataSource) JDBCUtils.getDataSource(gatewayConfig, aliasService); - assertEquals("localhost", dataSource.getServerName()); - assertEquals(1527, dataSource.getPortNumber()); + final EmbeddedDataSource dataSource = (EmbeddedDataSource) JDBCUtils.getDataSource(gatewayConfig, aliasService); assertEquals("sampleDatabase", dataSource.getDatabaseName()); assertEquals("user", dataSource.getUser()); assertEquals("password", dataSource.getPassword()); diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java index ca577fac66..a72489eed2 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java @@ -884,6 +884,11 @@ public void testCreateMasterGenerate() throws Exception { assertThat( master2, not( is( master ) ) ); assertThat( rc, is( 0 ) ); assertThat(outContent.toString(StandardCharsets.UTF_8.name()), containsString("Master secret has been persisted to disk.")); + + // Need to delete the master file so that the it will not interfere with other tests + if( masterFile.exists() ) { + assertThat( "Failed to delete existing master file.", masterFile.delete(), is( true ) ); + } } @Test @@ -1320,7 +1325,7 @@ private void testGeneratingJWK(JWSAlgorithm jwkAlgorithm, String alias) throws E } else { assertThat(commandOutput, containsString(alias + " has been successfully created.")); - final AliasService aliasService = KnoxCLI.getGatewayServices().getService(ServiceType.ALIAS_SERVICE); + final AliasService aliasService = cli.getGatewayServices().getService(ServiceType.ALIAS_SERVICE); assertNotNull(new String(aliasService.getPasswordFromAliasForGateway(alias))); } diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java index bc614eb2cd..5c98d9fbac 100644 --- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java +++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java @@ -393,8 +393,8 @@ private void populateAllowedTokenStateBackendForTokenGenApp(final String actualT } } } else { - //if there is no custom configuration in the topology, then we allow keystore and DB back-end for the tokengen application - if ("AliasBasedTokenStateService".equals(actualTokenServiceName) || "JDBCTokenStateService".equals(actualTokenServiceName)) { + //if there is no custom configuration in the topology, then we allow DerbyDB and custom DB back-ends for the tokengen application + if ("DerbyDBTokenStateService".equals(actualTokenServiceName) || "JDBCTokenStateService".equals(actualTokenServiceName)) { tokenStateServiceStatusMap.put(TSS_ALLOWED_BACKEND_FOR_TOKENGEN, "true"); } } diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java index 332d2ce1e9..f1059e2016 100644 --- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java +++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java @@ -1780,6 +1780,11 @@ private Collection fetchTokens(String userName, boolean createdBy) { return tokens; } + @Override + public boolean isMigrationTarget() { + return false; + } + @Override public void init(GatewayConfig config, Map options) throws ServiceLifecycleException { } diff --git a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java index 3162f71402..52446780e7 100644 --- a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java +++ b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java @@ -961,7 +961,7 @@ public Map> getHomePageProfiles() { @Override public String getDatabaseType() { - return null; + return "derbydb"; } @Override @@ -981,10 +981,9 @@ public int getDatabasePort() { @Override public String getDatabaseName() { - return null; + return Paths.get(getGatewaySecurityDir(), "tokens").toString(); } - @Override public boolean isDatabaseSslEnabled() { return false; @@ -1067,4 +1066,29 @@ public Map> getApplicationPathAliases() { return Collections.emptyMap(); } + @Override + public boolean skipTokenMigration() { + return true; + } + + @Override + public boolean archiveMigratedTokens() { + return false; + } + + @Override + public boolean migrateExpiredTokens() { + return false; + } + + @Override + public boolean printVerboseTokenMigrationMessages() { + return false; + } + + @Override + public int getTokenMigrationProgressCount() { + return 1; + } + } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java index 83d1bb9263..c9d8371336 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java @@ -898,4 +898,34 @@ public interface GatewayConfig { Map> getApplicationPathAliases(); + /** + * @return true if token migration must be skipped when a + * JDBC-based TSS starts; false otherwise + */ + boolean skipTokenMigration(); + + /** + * @return true if migrated tokens must be archived when a + * JDBC-based starts; false otherwise + */ + boolean archiveMigratedTokens(); + + /** + * @return true if expired tokens must be migrated when a + * JDBC-based starts; false otherwise + */ + boolean migrateExpiredTokens(); + + /** + * @return true if the token migration tool should print verbose + * messages when a JDBC-based starts; false otherwise + */ + boolean printVerboseTokenMigrationMessages(); + + /** + * @return the number of tokens after the token migration tool displays progress + * in the logs when a JDBC-based TSS starts. + */ + int getTokenMigrationProgressCount(); + } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/impl/CMFMasterService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/impl/CMFMasterService.java index 0aba570b3f..b10c246a5f 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/impl/CMFMasterService.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/impl/CMFMasterService.java @@ -17,7 +17,6 @@ */ package org.apache.knox.gateway.services.security.impl; -import de.thetaphi.forbiddenapis.SuppressForbidden; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.net.ntp.TimeStamp; @@ -146,7 +145,7 @@ protected void persistMaster(char[] master, File masterFile) { FileUtils.writeLines(masterFile, StandardCharsets.UTF_8.name(), lines); // restrict os permissions to only the user running this process - chmod("600", masterFile); + org.apache.knox.gateway.util.FileUtils.chmod("600", masterFile); } catch (IOException e) { LOG.failedToPersistMasterSecret(e); } @@ -176,31 +175,4 @@ protected void initializeFromMaster(File masterFile) throws Exception { throw e; } } - - @SuppressForbidden - private void chmod(String args, File file) throws IOException { - // TODO: move to Java 7 NIO support to add windows as well - // TODO: look into the following for Windows: Runtime.getRuntime().exec("attrib -r myFile"); - if (isUnixEnv()) { - //args and file should never be null. - if (args == null || file == null) { - throw new IllegalArgumentException("nullArg"); - } - if (!file.exists()) { - throw new IOException("fileNotFound"); - } - - // " +" regular expression for 1 or more spaces - final String[] argsString = args.split(" +"); - List cmdList = new ArrayList<>(); - cmdList.add("/bin/chmod"); - cmdList.addAll(Arrays.asList(argsString)); - cmdList.add(file.getAbsolutePath()); - new ProcessBuilder(cmdList).start(); - } - } - - private boolean isUnixEnv() { - return (File.separatorChar == '/'); - } } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java index 2d3ea1b203..e013133ce2 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenStateService.java @@ -217,4 +217,10 @@ public interface TokenStateService extends Service { */ Collection getDoAsTokens(String createdBy); + /** + * @return true if the given TSS implementation could be used as a + * token migration target in KnoxCLI's migrate-token command + */ + boolean isMigrationTarget(); + } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/util/FileUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/util/FileUtils.java new file mode 100644 index 0000000000..3ef28c0eb3 --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/util/FileUtils.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +package org.apache.knox.gateway.util; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import de.thetaphi.forbiddenapis.SuppressForbidden; + +public class FileUtils { + + @SuppressForbidden + public static void chmod(String args, File file) throws IOException { + // TODO: move to Java 7 NIO support to add windows as well + // TODO: look into the following for Windows: Runtime.getRuntime().exec("attrib + // -r myFile"); + if (isUnixEnv()) { + // args and file should never be null. + if (args == null || file == null) { + throw new IllegalArgumentException("nullArg"); + } + if (!file.exists()) { + throw new IOException("fileNotFound"); + } + + // " +" regular expression for 1 or more spaces + final String[] argsString = args.split(" +"); + List cmdList = new ArrayList<>(); + cmdList.add("/bin/chmod"); + cmdList.addAll(Arrays.asList(argsString)); + cmdList.add(file.getAbsolutePath()); + new ProcessBuilder(cmdList).start(); + } + } + + private static boolean isUnixEnv() { + return File.separatorChar == '/'; + } + +} diff --git a/gateway-test-release/pom.xml b/gateway-test-release/pom.xml index 59973a7f0a..abbdb9cc00 100644 --- a/gateway-test-release/pom.xml +++ b/gateway-test-release/pom.xml @@ -345,6 +345,7 @@ false ${project.version} + /dev/null diff --git a/gateway-test-release/webhdfs-kerb-test/pom.xml b/gateway-test-release/webhdfs-kerb-test/pom.xml index c254829114..2c4a46f48e 100644 --- a/gateway-test-release/webhdfs-kerb-test/pom.xml +++ b/gateway-test-release/webhdfs-kerb-test/pom.xml @@ -110,6 +110,7 @@ false ${project.version} + /dev/null diff --git a/gateway-test-release/webhdfs-test/pom.xml b/gateway-test-release/webhdfs-test/pom.xml index b809987e6e..9f29e3e3a2 100644 --- a/gateway-test-release/webhdfs-test/pom.xml +++ b/gateway-test-release/webhdfs-test/pom.xml @@ -102,6 +102,7 @@ false ${project.version} + /dev/null diff --git a/gateway-test/pom.xml b/gateway-test/pom.xml index 2d14fb3a03..c1ead63fa6 100644 --- a/gateway-test/pom.xml +++ b/gateway-test/pom.xml @@ -245,6 +245,12 @@ test + + org.apache.hadoop + hadoop-common + test + + org.apache.zookeeper zookeeper @@ -327,6 +333,7 @@ false ${project.version} + /dev/null diff --git a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayBasicFuncTest.java b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayBasicFuncTest.java index e62b5496f0..e457c46272 100644 --- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayBasicFuncTest.java +++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayBasicFuncTest.java @@ -28,6 +28,7 @@ import io.restassured.specification.ResponseSpecification; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.commons.lang3.ArrayUtils; +import org.apache.hadoop.conf.Configuration; import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -149,6 +150,7 @@ public class GatewayBasicFuncTest { public static void setUpBeforeClass() throws Exception { LOG_ENTER(); GatewayTestConfig config = new GatewayTestConfig(); + driver.config = config; driver.setResourceBase(GatewayBasicFuncTest.class); driver.setupLdap(0); driver.setupService("WEBHDFS", "http://" + TEST_HOST + ":50070/webhdfs", "/cluster/webhdfs", USE_MOCK_SERVICES); @@ -3710,8 +3712,7 @@ public void testCLIServiceTest() throws Exception { String[] args = {"service-test", "--master", "knox", "--cluster", driver.clusterName, "--hostname", gatewayAddress.getHostName(), "--port", gatewayPort, "--u", "kminder","--p", "kminder-password" }; - KnoxCLI cli = new KnoxCLI(); - cli.run(args); + getKnoxCli().run(args); Assume.assumeTrue("Gateway port should not contain status code", !gatewayPort.contains("404") && !gatewayPort.contains("403")); @@ -3724,8 +3725,7 @@ public void testCLIServiceTest() throws Exception { String[] args2 = {"service-test", "--master", "knox", "--cluster", driver.clusterName, "--hostname", gatewayAddress.getHostName(), "--port", gatewayPort}; - cli = new KnoxCLI(); - cli.run(args2); + getKnoxCli().run(args2); assertThat(outContent.toString(StandardCharsets.UTF_8.name()), (containsString("Username and/or password not supplied. Expect HTTP 401 Unauthorized responses."))); outContent.reset(); @@ -3733,8 +3733,7 @@ public void testCLIServiceTest() throws Exception { String[] args3 = {"service-test", "--master", "knox", "--cluster", driver.clusterName, "--hostname", "bad-host", "--port", "0", "--u", "guest", "--p", "guest-password" }; - cli = new KnoxCLI(); - cli.run(args3); + getKnoxCli().run(args3); assertThat(outContent.toString(StandardCharsets.UTF_8.name()).toLowerCase(Locale.ROOT), either(containsString("nodename nor servname provided")).or(containsString("name or service not known")) .or(containsString("//bad-host:0/"))); @@ -3743,8 +3742,7 @@ public void testCLIServiceTest() throws Exception { String[] args4 = {"service-test", "--master", "knox", "--cluster", driver.clusterName, "--hostname", gatewayAddress.getHostName(), "--port", "543", "--u", "mapred", "--p", "mapred-password" }; - cli = new KnoxCLI(); - cli.run(args4); + getKnoxCli().run(args4); assertThat(outContent.toString(StandardCharsets.UTF_8.name()), containsString("failed: Connection refused")); outContent.reset(); @@ -3752,8 +3750,7 @@ public void testCLIServiceTest() throws Exception { String[] args5 = {"service-test", "--master", "knox", "--hostname", gatewayAddress.getHostName(), "--port", "543", "--u", "mapred", "--p", "mapred-password" }; - cli = new KnoxCLI(); - cli.run(args5); + getKnoxCli().run(args5); assertThat(outContent.toString(StandardCharsets.UTF_8.name()), containsString("--cluster argument is required")); outContent.reset(); @@ -3762,6 +3759,12 @@ public void testCLIServiceTest() throws Exception { LOG_EXIT(); } + private KnoxCLI getKnoxCli() { + final KnoxCLI cli = new KnoxCLI(); + cli.setConf((Configuration) driver.config); + return cli; + } + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) public void testSolrRESTAPI() throws Exception { LOG_ENTER(); diff --git a/knox-token-generation-ui/token-generation/app/token-generation.component.ts b/knox-token-generation-ui/token-generation/app/token-generation.component.ts index b3fecc411f..e155fc931e 100644 --- a/knox-token-generation-ui/token-generation/app/token-generation.component.ts +++ b/knox-token-generation-ui/token-generation/app/token-generation.component.ts @@ -188,9 +188,9 @@ export class TokenGenerationComponent implements OnInit { private decideTssMessage() { if (this.tssStatus.tokenManagementEnabled) { if (this.tssStatus.allowedTssForTokengen) { - if (this.tssStatus.actualTssBackend === 'AliasBasedTokenStateService') { - this.setTssMessage('warning', `Token management backend is configured to store tokens in keystores. - This is only valid non-HA environments!`); + if (this.tssStatus.actualTssBackend === 'DerbyDBTokenStateService') { + this.setTssMessage('warning', `Token management backend is configured to store tokens in a Derby DB on local file system. + This is only valid in non-HA environments!`); } else { this.setTssMessage('info', 'Token management backend is properly configured for HA and production deployments.'); } diff --git a/pom.xml b/pom.xml index a8b3317849..8b0c51611f 100644 --- a/pom.xml +++ b/pom.xml @@ -539,6 +539,7 @@ ${project.version} + /dev/null @@ -558,6 +559,9 @@ **/*.java + + /dev/null +