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..7e4f9bb8a2 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 @@ -473,13 +477,11 @@ 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..523d6c19cb 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,31 @@ 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)); + 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 +95,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..74997628b0 --- /dev/null +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DerbyDBTokenStateService.java @@ -0,0 +1,101 @@ +/* + * 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.commons.text.lookup.StringLookupFactory.KEY_LOCALHOST; +import static org.apache.derby.drda.NetworkServerControl.DEFAULT_PORTNUMBER; +import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_HOST; +import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_NAME; +import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_PORT; +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.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.derby.drda.NetworkServerControl; +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.config.impl.GatewayConfigImpl; +import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.knox.gateway.services.security.MasterService; +import org.apache.knox.gateway.util.FileUtils; + +public class DerbyDBTokenStateService extends JDBCTokenStateService { + + private static final String DEFAULT_TOKEN_DB_USER_NAME = "knox"; + private static final String DB_NAME = "tokens"; + + private NetworkServerControl derbyNetworkServerControl; + private MasterService masterService; + + public void setMasterService(MasterService masterService) { + this.masterService = masterService; + } + + @Override + public void init(GatewayConfig config, Map options) throws ServiceLifecycleException { + try { + startDerby(); + final Path derbyDatabaseFolder = Paths.get(config.getGatewaySecurityDir(), DB_NAME); + ((GatewayConfigImpl) config).set(GATEWAY_DATABASE_TYPE, DERBY_DB_TYPE); + ((GatewayConfigImpl) config).set(GATEWAY_DATABASE_HOST, KEY_LOCALHOST); + ((GatewayConfigImpl) config).setInt(GATEWAY_DATABASE_PORT, DEFAULT_PORTNUMBER); + ((GatewayConfigImpl) 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) + FileUtils.chmod("700", derbyDatabaseFolder.toFile()); + } catch (Exception e) { + throw new ServiceLifecycleException("Error while initiating DerbyDBTokenStateService: " + e, e); + } + } + + private void startDerby() throws Exception { + derbyNetworkServerControl = new NetworkServerControl(getDatabaseUserName(), getDatabasePassword()); + derbyNetworkServerControl.start(null); + 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 (derbyNetworkServerControl != null) { + derbyNetworkServerControl.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..d7deb2d037 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(getAliasService(), 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..5323edb09a 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 @@ -53,6 +53,7 @@ public class JDBCUtils { public static final String DATABASE_USER_ALIAS_NAME = "gateway_database_user"; public static final String DATABASE_PASSWORD_ALIAS_NAME = "gateway_database_password"; public static final String DATABASE_TRUSTSTORE_PASSWORD_ALIAS_NAME = "gateway_database_ssl_truststore_password"; + private static final String DERBY_DB_CREATE_OPTION = "create"; public static DataSource getDataSource(GatewayConfig gatewayConfig, AliasService aliasService) throws AliasServiceException, SQLException { if (POSTGRESQL_DB_TYPE.equalsIgnoreCase(gatewayConfig.getDatabaseType())) { @@ -117,6 +118,7 @@ private static DataSource createDerbyDatasource(GatewayConfig gatewayConfig, Ali derbyDatasource.setPortNumber(gatewayConfig.getDatabasePort()); derbyDatasource.setUser(getDatabaseUser(aliasService)); derbyDatasource.setPassword(getDatabasePassword(aliasService)); + derbyDatasource.setCreateDatabase(DERBY_DB_CREATE_OPTION); return derbyDatasource; } @@ -195,8 +197,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..84a374ccc5 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 = false; + private boolean migrateExpiredTokens = false; + private boolean verbose = false; 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.valueOf(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..168807fc13 --- /dev/null +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/TokenMigrationTool.java @@ -0,0 +1,231 @@ +/* + * 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.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 = false; + private boolean migrateExpiredTokens = false; + private boolean verbose = false; + + 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("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 " + tokenId + " into the configured TokenStateService backend."); + } + } else { + if (verbose) { + log("Skipping the migration of expired token with ID = " + 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 = false; + long issueTime = -1; + long maxLifeTime = -1; + long expiration = -2; // can be set to '-1' meaning it never expires + TokenMetadata metadata = null; + + 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-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..a24e5f5209 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 @@ -1067,4 +1067,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/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.'); }