This is an automated email from the ASF dual-hosted git repository.
smolnar82 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 8f65731a5 KNOX-3370: Eliminate deprecated TokenStateService
implementations (#1289)
8f65731a5 is described below
commit 8f65731a5be596fbdd80217f25b69d04fd395902
Author: Sandor Molnar <[email protected]>
AuthorDate: Wed Jul 1 21:13:37 2026 +0200
KNOX-3370: Eliminate deprecated TokenStateService implementations (#1289)
---
.../services/factory/TokenStateServiceFactory.java | 25 +-
.../token/impl/AliasBasedTokenStateService.java | 721 ----------------
.../token/impl/JournalBasedTokenStateService.java | 221 -----
.../token/impl/ZookeeperTokenStateService.java | 185 ----
.../knox/gateway/util/TokenMigrationTool.java | 9 +-
.../factory/TokenStateServiceFactoryTest.java | 26 -
.../impl/AliasBasedTokenStateServiceTest.java | 943 ---------------------
.../impl/JournalBasedTokenStateServiceTest.java | 331 --------
.../token/impl/ZookeeperTokenStateServiceTest.java | 242 ------
9 files changed, 12 insertions(+), 2691 deletions(-)
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 4934c1f5f..332d257d3 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
@@ -17,12 +17,6 @@
*/
package org.apache.knox.gateway.services.factory;
-import static java.util.Arrays.asList;
-import static java.util.Collections.unmodifiableList;
-
-import java.util.Collection;
-import java.util.Map;
-
import org.apache.knox.gateway.GatewayMessages;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
@@ -30,12 +24,15 @@ import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.Service;
import org.apache.knox.gateway.services.ServiceLifecycleException;
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;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableList;
public class TokenStateServiceFactory extends AbstractServiceFactory {
@@ -50,13 +47,6 @@ public class TokenStateServiceFactory extends
AbstractServiceFactory {
service = useDerbyDatabaseTokenStateService(gatewayServices,
gatewayConfig, options);
} else if (matchesImplementation(implementation,
DefaultTokenStateService.class)) {
service = new DefaultTokenStateService();
- } else if (matchesImplementation(implementation,
AliasBasedTokenStateService.class)) {
- service = new AliasBasedTokenStateService();
- ((AliasBasedTokenStateService)
service).setAliasService(getAliasService(gatewayServices));
- } else if (matchesImplementation(implementation,
JournalBasedTokenStateService.class)) {
- service = new JournalBasedTokenStateService();
- } else if (matchesImplementation(implementation,
ZookeeperTokenStateService.class)) {
- service = new ZookeeperTokenStateService(gatewayServices);
} else if (matchesImplementation(implementation,
JDBCTokenStateService.class)) {
try {
service = new JDBCTokenStateService();
@@ -95,7 +85,6 @@ public class TokenStateServiceFactory extends
AbstractServiceFactory {
@Override
protected Collection<String> getKnownImplementations() {
- return unmodifiableList(asList(DefaultTokenStateService.class.getName(),
AliasBasedTokenStateService.class.getName(),
JournalBasedTokenStateService.class.getName(),
- ZookeeperTokenStateService.class.getName(),
JDBCTokenStateService.class.getName(),
DerbyDBTokenStateService.class.getName()));
+ return unmodifiableList(asList(DefaultTokenStateService.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
deleted file mode 100644
index 18902eed8..000000000
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateService.java
+++ /dev/null
@@ -1,721 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.commons.lang3.concurrent.BasicThreadFactory;
-import org.apache.knox.gateway.config.GatewayConfig;
-import org.apache.knox.gateway.services.ServiceLifecycleException;
-import org.apache.knox.gateway.services.security.AliasService;
-import org.apache.knox.gateway.services.security.AliasServiceException;
-import org.apache.knox.gateway.services.security.impl.DefaultKeystoreService;
-import org.apache.knox.gateway.services.security.token.TokenMetadata;
-import org.apache.knox.gateway.services.security.token.UnknownTokenException;
-import org.apache.knox.gateway.services.token.TokenStateServiceStatistics;
-import
org.apache.knox.gateway.services.token.impl.state.TokenStateJournalFactory;
-import org.apache.knox.gateway.services.token.state.JournalEntry;
-import org.apache.knox.gateway.services.token.state.TokenStateJournal;
-import org.apache.knox.gateway.util.ExecutorServiceUtils;
-import org.apache.knox.gateway.util.Tokens;
-
-/**
- * A TokenStateService implementation based on the AliasService.
- *
- * @deprecated Since 2.1.0
- */
-public class AliasBasedTokenStateService extends
AbstractPersistentTokenStateService implements
TokenStatePeristerMonitorListener {
-
- static final String TOKEN_ALIAS_SUFFIX_DELIM = "--";
- public static final String TOKEN_ISSUE_TIME_POSTFIX =
TOKEN_ALIAS_SUFFIX_DELIM + "iss";
- public static final String TOKEN_MAX_LIFETIME_POSTFIX =
TOKEN_ALIAS_SUFFIX_DELIM + "max";
- public static final String TOKEN_META_POSTFIX =
TOKEN_ALIAS_SUFFIX_DELIM + "meta";
-
- protected AliasService aliasService;
-
- protected long statePersistenceInterval = TimeUnit.SECONDS.toSeconds(15);
-
- private ScheduledExecutorService statePersistenceScheduler;
-
- private final Set<TokenState> unpersistedState = new HashSet<>();
-
- private final AtomicBoolean readyForEviction = new AtomicBoolean(false);
-
- private TokenStateJournal journal;
-
- private Path gatewayCredentialsFilePath;
-
- public void setAliasService(AliasService aliasService) {
- this.aliasService = aliasService;
- }
-
- @Override
- public void init(final GatewayConfig config, final Map<String, String>
options) throws ServiceLifecycleException {
- log.deprecatedServiceUsage(this.getClass().getCanonicalName());
- super.init(config, options);
- if (aliasService == null) {
- throw new ServiceLifecycleException("The required AliasService reference
has not been set.");
- }
-
- try {
- // Initialize the token state journal
- journal = TokenStateJournalFactory.create(config);
-
- // Load any persisted journal entries, and add them to the unpersisted
state collection
- List<JournalEntry> entries = journal.get();
- for (JournalEntry entry : entries) {
- String id = entry.getTokenId();
- try {
- long issueTime = Long.parseLong(entry.getIssueTime());
- long expiration = Long.parseLong(entry.getExpiration());
- long maxLifetime = Long.parseLong(entry.getMaxLifetime());
-
- // Add the token state to memory
- super.addToken(id, issueTime, expiration, maxLifetime);
-
- synchronized (unpersistedState) {
- // The max lifetime entry is added by way of the call to
super.addToken(),
- // so only need to add the expiration entry here.
- unpersistedState.add(new TokenExpiration(id, expiration));
- }
- } catch (Exception e) {
- log.failedToLoadJournalEntry(Tokens.getTokenIDDisplayText(id), e);
- }
- }
- } catch (IOException e) {
- throw new ServiceLifecycleException("Failed to load persisted state from
the token state journal", e);
- }
-
- statePersistenceInterval =
config.getKnoxTokenStateAliasPersistenceInterval();
-
- if (tokenStateServiceStatistics != null) {
- this.gatewayCredentialsFilePath =
Paths.get(config.getGatewayKeystoreDir()).resolve(AliasService.NO_CLUSTER_NAME
+ DefaultKeystoreService.CREDENTIALS_SUFFIX +
config.getCredentialStoreType().toLowerCase(Locale.ROOT));
-
tokenStateServiceStatistics.setGatewayCredentialsFileSize(this.gatewayCredentialsFilePath.toFile().length());
- }
- }
-
- @Override
- public void start() throws ServiceLifecycleException {
- super.start();
- if (statePersistenceInterval > 0) {
- //first schedule event; only happen if the feature is not disabled via
persistence interval settings
- scheduleTokenStatePersistence();
- }
-
- // Loading ALL entries from __gateway-credentials.jceks could be VERY
time-consuming (it took a bit more than 19 minutes to load 12k aliases
- // during my tests).
- // Therefore, it's safer to do it in a background thread than just make
the service start hang until it's finished
- final ExecutorService gatewayCredentialsLoader =
Executors.newSingleThreadExecutor(new
BasicThreadFactory.Builder().namingPattern("PersistenceStoreLoader").build());
-
gatewayCredentialsLoader.execute(this::loadTokenAliasesFromPersistenceStore);
- }
-
- protected void loadTokenAliasesFromPersistenceStore() {
- try {
- log.loadingTokenAliasesFromPersistenceStore();
- final long start = System.currentTimeMillis();
- final Map<String, char[]> passwordAliasMap =
aliasService.getPasswordsForGateway();
- String alias, tokenId;
- long expiration, maxLifeTime;
- int count = 0;
- for (Map.Entry<String, char[]> passwordAliasMapEntry :
passwordAliasMap.entrySet()) {
- alias = passwordAliasMapEntry.getKey();
- if (alias.endsWith(TOKEN_MAX_LIFETIME_POSTFIX)) {
- // This token state service implementation persists 4 aliases in
__gateway-credentials.jceks (see persistTokenState below):
- // - 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
- // Given this, we should check aliases ending with '--max' and
calculate the token ID from this alias.
- // If all aliases were blindly processed we would end-up handling
aliases that were not persisted via this token state service
- // implementation -> facing error(s) when trying to parse the
expiration/maxLifeTime values and irrelevant data would be loaded in the
- // in-memory collections in the parent class
- tokenId = alias.substring(0,
alias.indexOf(TOKEN_MAX_LIFETIME_POSTFIX));
- expiration = convertCharArrayToLong(passwordAliasMap.get(tokenId));
- maxLifeTime =
convertCharArrayToLong(passwordAliasMapEntry.getValue());
- super.updateExpiration(tokenId, expiration);
- super.setMaxLifetime(tokenId, maxLifeTime);
- count+=2;
- } else if (alias.endsWith(TOKEN_META_POSTFIX)) {
- tokenId = alias.substring(0, alias.indexOf(TOKEN_META_POSTFIX));
- super.addMetadata(tokenId, TokenMetadata.fromJSON(new
String(passwordAliasMapEntry.getValue())));
- } else if (alias.endsWith(TOKEN_ISSUE_TIME_POSTFIX)) {
- tokenId = alias.substring(0,
alias.indexOf(TOKEN_ISSUE_TIME_POSTFIX));
- setIssueTimeInMemory(tokenId,
convertCharArrayToLong(passwordAliasMapEntry.getValue()));
- }
-
- // log some progress (it's very useful in case a huge amount of token
related aliases in __gateway-credentials.jceks)
- if (count % 100 == 0) {
- log.loadedTokenAliasesFromPersistenceStore(count,
System.currentTimeMillis() - start);
- }
- }
- log.loadedTokenAliasesFromPersistenceStore(count * 2,
System.currentTimeMillis() - start); //count is multiplied by two: tokenId +
tokenId--max
- } catch (AliasServiceException e) {
- log.errorWhileLoadingTokenAliasesFromPersistenceStore(e.getMessage(), e);
- } finally {
- readyForEviction.set(true);
- }
- }
-
- @Override
- protected boolean readyForEviction() {
- return readyForEviction.get();
- }
-
- @Override
- public void stop() throws ServiceLifecycleException {
- super.stop();
- if (statePersistenceScheduler != null) {
- statePersistenceScheduler.shutdown();
- }
-
- // Make an attempt to persist any unpersisted token state before shutting
down
- persistTokenState();
- }
-
- private void scheduleTokenStatePersistence() {
- if (statePersistenceScheduler != null) {
-
ExecutorServiceUtils.shutdownAndAwaitTermination(statePersistenceScheduler, 10,
TimeUnit.SECONDS);
- }
- statePersistenceScheduler = Executors.newSingleThreadScheduledExecutor(new
BasicThreadFactory.Builder().namingPattern("TokenStatePerister-%d").build());
- final ScheduledFuture<?> persistTokenStateTask =
statePersistenceScheduler.scheduleAtFixedRate(this::persistTokenState,
statePersistenceInterval, statePersistenceInterval, TimeUnit.SECONDS);
- log.runningTokenStateAliasePersisterTask(statePersistenceInterval,
TimeUnit.SECONDS.toString());
- final TokenStatePersisterMonitor taskMonitor = new
TokenStatePersisterMonitor(persistTokenStateTask, this);
- taskMonitor.startMonitor();
- }
-
- @Override
- public void onTokenStatePeristerTaskError(Throwable error) {
- scheduleTokenStatePersistence();
- }
-
- protected void persistTokenState() {
- Set<String> tokenIds = new HashSet<>(); // Collect the tokenIds for logging
-
- List<TokenState> processing;
- synchronized (unpersistedState) {
- // Move unpersisted state to temp collection
- processing = new ArrayList<>(unpersistedState);
- unpersistedState.clear();
- }
-
- // Create a set of aliases based on the unpersisted TokenState objects
- Map<String, String> aliases = new HashMap<>();
- for (TokenState state : processing) {
- tokenIds.add(state.getTokenId());
- aliases.put(state.getAlias(), state.getAliasValue());
- }
-
- for (String tokenId: tokenIds) {
- log.creatingTokenStateAliases(Tokens.getTokenIDDisplayText(tokenId));
- }
-
- // Write aliases in a batch
- if (!aliases.isEmpty()) {
- log.creatingTokenStateAliases();
-
- try {
- aliasService.addAliasesForCluster(AliasService.NO_CLUSTER_NAME,
aliases);
- if (tokenStateServiceStatistics != null) {
-
tokenStateServiceStatistics.interactKeystore(TokenStateServiceStatistics.KeystoreInteraction.SAVE_ALIAS);
-
tokenStateServiceStatistics.setGatewayCredentialsFileSize(this.gatewayCredentialsFilePath.toFile().length());
- }
- for (String tokenId : tokenIds) {
- log.createdTokenStateAliases(Tokens.getTokenIDDisplayText(tokenId));
- // After the aliases have been successfully persisted, remove their
associated state from the journal
- try {
- journal.remove(tokenId);
- } catch (IOException e) {
-
log.failedToRemoveJournalEntry(Tokens.getTokenIDDisplayText(tokenId), e);
- }
- }
- } catch (AliasServiceException e) {
- log.failedToCreateTokenStateAliases(e);
- synchronized (unpersistedState) {
- unpersistedState.addAll(processing); // Restore the unpersisted
state objects so they can be attempted later
- }
- }
- }
- }
-
- @Override
- public void addToken(final String tokenId,
- long issueTime,
- long expiration,
- long maxLifetimeDuration) {
- super.addToken(tokenId, issueTime, expiration, maxLifetimeDuration);
-
- synchronized (unpersistedState) {
- unpersistedState.add(new TokenExpiration(tokenId, expiration));
- }
-
- try {
- journal.add(tokenId, issueTime, expiration, maxLifetimeDuration, null);
- } catch (IOException e) {
- log.failedToAddJournalEntry(Tokens.getTokenIDDisplayText(tokenId), e);
- }
- }
-
- @Override
- protected void setIssueTime(String tokenId, long issueTime) {
- synchronized (unpersistedState) {
- unpersistedState.add(new TokenIssueTime(tokenId, issueTime));
- }
- setIssueTimeInMemory(tokenId, issueTime);
- }
-
- protected void setIssueTimeInMemory(String tokenId, long issueTime) {
- super.setIssueTime(tokenId, issueTime);
- }
-
- @Override
- protected void setMaxLifetime(final String tokenId, long issueTime, long
maxLifetimeDuration) {
- super.setMaxLifetime(tokenId, issueTime, maxLifetimeDuration);
- synchronized (unpersistedState) {
- unpersistedState.add(new TokenMaxLifetime(tokenId, issueTime,
maxLifetimeDuration));
- }
- }
-
- @Override
- protected long getMaxLifetime(final String tokenId) {
- long result = super.getMaxLifetime(tokenId);
-
- // If there is no result from the in-memory collection, proceed to check
the alias service
- if (result < 1L) {
- try {
- char[] maxLifetimeStr = getPasswordUsingAliasService(tokenId +
TOKEN_MAX_LIFETIME_POSTFIX);
- if (maxLifetimeStr != null) {
- result = convertCharArrayToLong(maxLifetimeStr);
- }
- } catch (AliasServiceException e) {
- log.errorAccessingTokenState(Tokens.getTokenIDDisplayText(tokenId), e);
- }
- }
- return result;
- }
-
- protected char[] getPasswordUsingAliasService(String alias) throws
AliasServiceException {
- char[] password =
aliasService.getPasswordFromAliasForCluster(AliasService.NO_CLUSTER_NAME,
alias);
- if (tokenStateServiceStatistics != null) {
-
tokenStateServiceStatistics.interactKeystore(TokenStateServiceStatistics.KeystoreInteraction.GET_PASSWORD);
- }
- return password;
- }
-
- protected long convertCharArrayToLong(char[] charArray) {
- return Long.parseLong(new String(charArray));
- }
-
- @Override
- public long getTokenIssueTime(String tokenId) throws UnknownTokenException {
- // Check the in-memory collection first, to avoid costly keystore access
when possible
- try {
- // check the in-memory cache first
- return super.getTokenIssueTime(tokenId);
- } catch (UnknownTokenException e) {
- // It's not in memory
- }
-
- // If there is no associated state in the in-memory cache, proceed to
check the alias service
- long issueTime = 0;
- try {
- char[] issueTimeStr = getPasswordUsingAliasService(tokenId +
TOKEN_ISSUE_TIME_POSTFIX);
- if (issueTimeStr == null) {
- throw new UnknownTokenException(tokenId);
- }
- issueTime = convertCharArrayToLong(issueTimeStr);
- // Update the in-memory cache to avoid subsequent keystore look-ups for
the same state
- setIssueTimeInMemory(tokenId, issueTime);
- } catch (UnknownTokenException e) {
- throw e;
- } catch (Exception e) {
- log.errorAccessingTokenState(Tokens.getTokenIDDisplayText(tokenId), e);
- }
-
- return issueTime;
- }
-
- @Override
- public long getTokenExpiration(String tokenId, boolean validate) throws
UnknownTokenException {
- // Check the in-memory collection first, to avoid costly keystore access
when possible
- try {
- // If the token identifier is valid, and the associated state is
available from the in-memory cache, then
- // return the expiration from there.
- return super.getTokenExpiration(tokenId, validate);
- } catch (UnknownTokenException e) {
- // It's not in memory
- }
-
- if (validate) {
- validateToken(tokenId);
- }
-
- // If there is no associated state in the in-memory cache, proceed to
check the alias service
- long expiration = 0;
- try {
- char[] expStr = getPasswordUsingAliasService(tokenId);
- if (expStr == null) {
- throw new UnknownTokenException(tokenId);
- }
- expiration = Long.parseLong(new String(expStr));
- // Update the in-memory cache to avoid subsequent keystore look-ups for
the same state
- super.updateExpiration(tokenId, expiration);
- } catch (UnknownTokenException e) {
- throw e;
- } catch (Exception e) {
- log.errorAccessingTokenState(Tokens.getTokenIDDisplayText(tokenId), e);
- }
-
- return expiration;
- }
-
- @Override
- protected boolean isUnknown(final String tokenId) {
- boolean isUnknown = super.isUnknown(tokenId);
-
- // If it's not in the cache, then check the underlying alias
- if (isUnknown) {
- try {
- isUnknown = (getPasswordUsingAliasService(tokenId) == null);
- } catch (AliasServiceException e) {
- log.errorAccessingTokenState(Tokens.getTokenIDDisplayText(tokenId), e);
- }
- }
- return isUnknown;
- }
-
- @Override
- protected void removeTokens(Set<String> tokenIds) {
-
- // If any of the token IDs is represented among the unpersisted state,
remove the associated state
- synchronized (unpersistedState) {
- List<TokenState> unpersistedToRemove = new ArrayList<>();
- for (TokenState state : unpersistedState) {
- if (tokenIds.contains(state.getTokenId())) {
- unpersistedToRemove.add(state);
- }
- }
- unpersistedState.removeAll(unpersistedToRemove);
- }
-
- // Add the max lifetime, metadata and issue time aliases to the list of
aliases to remove
- Set<String> aliasesToRemove = new HashSet<>(tokenIds);
- for (String tokenId : tokenIds) {
- aliasesToRemove.add(tokenId + TOKEN_MAX_LIFETIME_POSTFIX);
- aliasesToRemove.add(tokenId + TOKEN_META_POSTFIX);
- aliasesToRemove.add(tokenId + TOKEN_ISSUE_TIME_POSTFIX);
- }
-
- if (!aliasesToRemove.isEmpty()) {
- log.removingTokenStateAliases();
- try {
- aliasService.removeAliasesForCluster(AliasService.NO_CLUSTER_NAME,
aliasesToRemove);
- if (tokenStateServiceStatistics != null) {
-
tokenStateServiceStatistics.interactKeystore(TokenStateServiceStatistics.KeystoreInteraction.REMOVE_ALIAS);
-
tokenStateServiceStatistics.setGatewayCredentialsFileSize(this.gatewayCredentialsFilePath.toFile().length());
- }
- log.removedTokenStateAliases(String.join(", ",
Tokens.getDisplayableTokenIDsText(tokenIds)));
- } catch (AliasServiceException e) {
- log.failedToRemoveTokenStateAliases(e);
- }
- }
-
- removeTokensFromMemory(tokenIds);
- }
-
- protected void removeTokensFromMemory(Set<String> tokenIds) {
- super.removeTokens(tokenIds);
- }
-
- @Override
- protected void updateExpiration(final String tokenId, long expiration) {
- //Update in-memory
- updateExpirationInMemory(tokenId, expiration);
-
- //Update the in-memory representation of unpersisted states that will be
processed by the state persistence thread
- synchronized (unpersistedState) {
- unpersistedState.add(new TokenExpiration(tokenId, expiration));
- }
- }
-
- protected void updateExpirationInMemory(final String tokenId, long
expiration) {
- super.updateExpiration(tokenId, expiration);
- }
-
- @Override
- public void addMetadata(String tokenId, TokenMetadata metadata) {
- addMetadataInMemory(tokenId, metadata);
- try {
- final JournalEntry entry = journal.get(tokenId);
- if (entry != null) {
- journal.add(entry.getTokenId(), Long.parseLong(entry.getIssueTime()),
Long.parseLong(entry.getExpiration()), Long.parseLong(entry.getMaxLifetime()),
metadata);
- }
- } catch (IOException e) {
- log.failedToAddJournalEntry(Tokens.getTokenIDDisplayText(tokenId), e);
- }
-
- synchronized (unpersistedState) {
- unpersistedState.add(new TokenMetadataState(tokenId, metadata));
- }
- }
-
- protected void addMetadataInMemory(String tokenId, TokenMetadata metadata) {
- super.addMetadata(tokenId, metadata);
- }
-
- @Override
- public TokenMetadata getTokenMetadata(String tokenId) throws
UnknownTokenException {
- TokenMetadata tokenMetadata = null;
- try {
- tokenMetadata = super.getTokenMetadata(tokenId);
- } catch (UnknownTokenException e) {
- // This is expected if the metadata is not yet part of the in-memory
record. In this case, the metadata will
- // be retrieved from the alias store.
- }
-
- if (tokenMetadata == null) {
- try {
- final char[] tokenMetadataAliasValue =
getPasswordUsingAliasService(tokenId + TOKEN_META_POSTFIX);
- if (tokenMetadataAliasValue != null) {
- tokenMetadata = TokenMetadata.fromJSON(new
String(tokenMetadataAliasValue));
- } else {
- throw new UnknownTokenException(tokenId);
- }
- } catch (AliasServiceException e) {
- log.errorAccessingTokenState(Tokens.getTokenIDDisplayText(tokenId), e);
- }
- }
- return tokenMetadata;
- }
-
- enum TokenStateType {
- EXP(1), MAX(2), META(3), ISS(4);
-
- private final int id;
-
- TokenStateType(int id) {
- this.id = id;
- }
- }
-
- interface TokenState {
- String getTokenId();
- String getAlias();
- String getAliasValue();
- TokenStateType getType();
- }
-
- private static final class TokenMaxLifetime implements TokenState {
- private String tokenId;
- private long issueTime;
- private long maxLifetime;
-
- TokenMaxLifetime(String tokenId, long issueTime, long maxLifetime) {
- this.tokenId = tokenId;
- this.issueTime = issueTime;
- this.maxLifetime = maxLifetime;
- }
-
- @Override
- public String getTokenId() {
- return tokenId;
- }
-
- @Override
- public String getAlias() {
- return tokenId + TOKEN_MAX_LIFETIME_POSTFIX;
- }
-
- @Override
- public String getAliasValue() {
- return String.valueOf(issueTime + maxLifetime);
- }
-
- @Override
- public TokenStateType getType() {
- return TokenStateType.MAX;
- }
-
- @Override
- public int hashCode() {
- return new
HashCodeBuilder().append(tokenId).append(getType().id).toHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj == this) {
- return true;
- }
- if (obj.getClass() != getClass()) {
- return false;
- }
- final TokenMaxLifetime rhs = (TokenMaxLifetime) obj;
- return new EqualsBuilder().append(this.tokenId,
rhs.tokenId).append(this.getType().id, rhs.getType().id).isEquals();
- }
- }
-
- private static final class TokenExpiration implements TokenState {
- private String tokenId;
- private long expiration;
-
- TokenExpiration(String tokenId, long expiration) {
- this.tokenId = tokenId;
- this.expiration = expiration;
- }
-
- @Override
- public String getTokenId() {
- return tokenId;
- }
-
- @Override
- public String getAlias() {
- return tokenId;
- }
-
- @Override
- public String getAliasValue() {
- return String.valueOf(expiration);
- }
-
- @Override
- public TokenStateType getType() {
- return TokenStateType.EXP;
- }
-
- @Override
- public int hashCode() {
- return new
HashCodeBuilder().append(tokenId).append(getType().id).toHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj == this) {
- return true;
- }
- if (obj.getClass() != getClass()) {
- return false;
- }
- final TokenExpiration rhs = (TokenExpiration) obj;
- return new EqualsBuilder().append(this.tokenId,
rhs.tokenId).append(this.getType().id, rhs.getType().id).isEquals();
- }
- }
-
- private static final class TokenIssueTime implements TokenState {
- private String tokenId;
- private long issueTime;
-
- TokenIssueTime(String tokenId, long issueTime) {
- this.tokenId = tokenId;
- this.issueTime = issueTime;
- }
-
- @Override
- public String getTokenId() {
- return tokenId;
- }
-
- @Override
- public String getAlias() {
- return tokenId + TOKEN_ISSUE_TIME_POSTFIX;
- }
-
- @Override
- public String getAliasValue() {
- return String.valueOf(issueTime);
- }
-
- @Override
- public TokenStateType getType() {
- return TokenStateType.ISS;
- }
-
- @Override
- public int hashCode() {
- return new
HashCodeBuilder().append(tokenId).append(getType().id).toHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj == this) {
- return true;
- }
- if (obj.getClass() != getClass()) {
- return false;
- }
- final TokenIssueTime rhs = (TokenIssueTime) obj;
- return new EqualsBuilder().append(this.tokenId,
rhs.tokenId).append(this.getType().id, rhs.getType().id).isEquals();
- }
- }
-
- private static final class TokenMetadataState implements TokenState {
-
- private final String tokenId;
- private final TokenMetadata metadata;
-
- TokenMetadataState(String tokenId, TokenMetadata metadata) {
- this.tokenId = tokenId;
- this.metadata = metadata;
- }
-
- @Override
- public String getTokenId() {
- return tokenId;
- }
-
- @Override
- public String getAlias() {
- return tokenId + TOKEN_META_POSTFIX;
- }
-
- @Override
- public String getAliasValue() {
- return metadata.toJSON();
- }
-
- @Override
- public TokenStateType getType() {
- return TokenStateType.META;
- }
- }
-
-}
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
deleted file mode 100644
index e21852b14..000000000
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateService.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- *
- * 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 org.apache.knox.gateway.config.GatewayConfig;
-import org.apache.knox.gateway.services.ServiceLifecycleException;
-import org.apache.knox.gateway.services.security.token.TokenMetadata;
-import org.apache.knox.gateway.services.security.token.UnknownTokenException;
-import
org.apache.knox.gateway.services.token.impl.state.TokenStateJournalFactory;
-import org.apache.knox.gateway.services.token.state.JournalEntry;
-import org.apache.knox.gateway.services.token.state.TokenStateJournal;
-import org.apache.knox.gateway.util.Tokens;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @deprecated Since 2.1.0
- */
-public class JournalBasedTokenStateService extends
AbstractPersistentTokenStateService {
-
- private TokenStateJournal journal;
-
- @Override
- public void init(final GatewayConfig config, final Map<String, String>
options) throws ServiceLifecycleException {
- log.deprecatedServiceUsage(this.getClass().getCanonicalName());
- super.init(config, options);
-
- try {
- // Initialize the token state journal
- journal = TokenStateJournalFactory.create(config);
-
- // Load any persisted journal entries, and add them to the
in-memory collection
- List<JournalEntry> entries = journal.get();
- for (JournalEntry entry : entries) {
- String id = entry.getTokenId();
- try {
- long issueTime = Long.parseLong(entry.getIssueTime());
- long expiration = Long.parseLong(entry.getExpiration());
- long maxLifetime = Long.parseLong(entry.getMaxLifetime());
-
- // Add the token state to memory
- super.addToken(id, issueTime, expiration, maxLifetime);
-
- } catch (Exception e) {
-
log.failedToLoadJournalEntry(Tokens.getTokenIDDisplayText(id), e);
- }
- }
- } catch (IOException e) {
- throw new ServiceLifecycleException("Failed to load persisted
state from the token state journal", e);
- }
- }
-
- @Override
- public void addToken(final String tokenId, long issueTime, long
expiration, long maxLifetimeDuration) {
- super.addToken(tokenId, issueTime, expiration, maxLifetimeDuration);
-
- try {
- journal.add(tokenId, issueTime, expiration, maxLifetimeDuration,
null);
- } catch (IOException e) {
- log.failedToAddJournalEntry(Tokens.getTokenIDDisplayText(tokenId),
e);
- }
- }
-
- @Override
- public long getTokenIssueTime(String tokenId) throws UnknownTokenException
{
- try {
- // Check the in-memory collection first, to avoid file access when
possible
- return super.getTokenIssueTime(tokenId);
- } catch (UnknownTokenException e) {
- // It's not in memory
- }
-
- validateToken(tokenId);
-
- // If there is no associated state in the in-memory cache, proceed to
check the journal
- long issueTime = 0;
- try {
- JournalEntry entry = journal.get(tokenId);
- if (entry == null) {
- throw new UnknownTokenException(tokenId);
- }
-
- issueTime = Long.parseLong(entry.getIssueTime());
- } catch (IOException e) {
- log.failedToLoadJournalEntry(e);
- }
-
- return issueTime;
- }
-
- @Override
- public long getTokenExpiration(final String tokenId, boolean validate)
throws UnknownTokenException {
- // Check the in-memory collection first, to avoid file access when
possible
- try {
- // If the token identifier is valid, and the associated state is
available from the in-memory cache, then
- // return the expiration from there.
- return super.getTokenExpiration(tokenId, validate);
- } catch (UnknownTokenException e) {
- // It's not in memory
- }
-
- if (validate) {
- validateToken(tokenId);
- }
-
- // If there is no associated state in the in-memory cache, proceed to
check the journal
- long expiration = 0;
- try {
- JournalEntry entry = journal.get(tokenId);
- if (entry == null) {
- throw new UnknownTokenException(tokenId);
- }
-
- expiration = Long.parseLong(entry.getExpiration());
- super.addToken(tokenId,
- Long.parseLong(entry.getIssueTime()),
- expiration,
- Long.parseLong(entry.getMaxLifetime()));
- } catch (IOException e) {
- log.failedToLoadJournalEntry(e);
- }
-
- return expiration;
- }
-
- @Override
- protected long getMaxLifetime(final String tokenId) {
- long result = super.getMaxLifetime(tokenId);
-
- // If there is no result from the in-memory collection, proceed to
check the journal
- if (result < 1L) {
- try {
- JournalEntry entry = journal.get(tokenId);
- if (entry == null) {
- throw new UnknownTokenException(tokenId);
- }
- result = Long.parseLong(entry.getMaxLifetime());
- super.setMaxLifetime(tokenId,
Long.parseLong(entry.getIssueTime()), result);
- } catch (Exception e) {
- log.failedToLoadJournalEntry(e);
- }
- }
- return result;
- }
-
- @Override
- protected void removeTokens(final Set<String> tokenIds) {
- super.removeTokens(tokenIds);
- try {
- journal.remove(tokenIds);
- } catch (IOException e) {
- log.failedToRemoveJournalEntries(e);
- }
- }
-
- @Override
- protected void updateExpiration(final String tokenId, long expiration) {
- super.updateExpiration(tokenId, expiration);
- try {
- JournalEntry entry = journal.get(tokenId);
- if (entry == null) {
-
log.journalEntryNotFound(Tokens.getTokenIDDisplayText(tokenId));
- } else {
- // Adding will overwrite the existing journal entry, thus
updating it with the new expiration
- journal.add(entry.getTokenId(),
- Long.parseLong(entry.getIssueTime()),
- expiration,
- Long.parseLong(entry.getMaxLifetime()),
- entry.getTokenMetadata());
- }
- } catch (IOException e) {
- log.errorAccessingTokenState(e);
- }
- }
-
- @Override
- protected boolean isUnknown(final String tokenId) {
- JournalEntry entry = null;
- try {
- entry = journal.get(tokenId);
- } catch (IOException e) {
- log.errorAccessingTokenState(e);
- }
-
- return (entry == null);
- }
-
- @Override
- public void addMetadata(String tokenId, TokenMetadata metadata) {
- super.addMetadata(tokenId, metadata);
- try {
- JournalEntry entry = journal.get(tokenId);
- if (entry == null) {
- log.journalEntryNotFound(Tokens.getTokenIDDisplayText(tokenId));
- } else {
- journal.add(entry.getTokenId(), Long.parseLong(entry.getIssueTime()),
Long.parseLong(entry.getExpiration()), Long.parseLong(entry.getMaxLifetime()),
metadata);
- }
- } catch (IOException e) {
- log.errorAccessingTokenState(e);
- }
- }
-}
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
deleted file mode 100644
index 7fdc3000d..000000000
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateService.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.services.ServiceType.ALIAS_SERVICE;
-
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.knox.gateway.config.GatewayConfig;
-import org.apache.knox.gateway.services.GatewayServices;
-import org.apache.knox.gateway.services.ServiceLifecycleException;
-import org.apache.knox.gateway.services.factory.AliasServiceFactory;
-import org.apache.knox.gateway.services.security.AliasServiceException;
-import
org.apache.knox.gateway.services.security.impl.ZookeeperRemoteAliasService;
-import org.apache.knox.gateway.services.security.token.TokenMetadata;
-import org.apache.knox.gateway.services.token.RemoteTokenStateChangeListener;
-import org.apache.knox.gateway.util.Tokens;
-
-/**
- * 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 {
-
- // Constants for token aliases - needed for test compatibility
- public static final String TOKEN_MAX_LIFETIME_POSTFIX =
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX;
- public static final String TOKEN_META_POSTFIX =
AliasBasedTokenStateService.TOKEN_META_POSTFIX;
-
- private final GatewayServices gatewayServices;
- private final AliasServiceFactory aliasServiceFactory;
-
- public ZookeeperTokenStateService(GatewayServices gatewayServices) {
- this(gatewayServices, new AliasServiceFactory());
- }
-
- public ZookeeperTokenStateService(GatewayServices gatewayServices,
AliasServiceFactory aliasServiceFactory) {
- this.gatewayServices = gatewayServices;
- this.aliasServiceFactory = aliasServiceFactory;
- }
-
- @Override
- public void init(GatewayConfig config, Map<String, String> options) throws
ServiceLifecycleException {
- log.deprecatedServiceUsage(this.getClass().getCanonicalName());
-
- final Object createdService = aliasServiceFactory.create(gatewayServices,
ALIAS_SERVICE, config, options,
- ZookeeperRemoteAliasService.class.getName());
-
- if (createdService == null) {
- throw new ServiceLifecycleException("aliasServiceFactory.create()
returned null - cannot create ZookeeperRemoteAliasService");
- }
-
- if (!(createdService instanceof ZookeeperRemoteAliasService)) {
- throw new ServiceLifecycleException("aliasServiceFactory.create()
returned unexpected type: " + createdService.getClass().getName() +
- ", expected: ZookeeperRemoteAliasService");
- }
-
- final ZookeeperRemoteAliasService zookeeperAliasService =
(ZookeeperRemoteAliasService) createdService;
-
-
-
options.put(ZookeeperRemoteAliasService.OPTION_NAME_SHOULD_CREATE_TOKENS_SUB_NODE,
"true");
-
options.put(ZookeeperRemoteAliasService.OPTION_NAME_SHOULD_USE_LOCAL_ALIAS,
"false");
-
-
- zookeeperAliasService.registerRemoteTokenStateChangeListener(this);
- zookeeperAliasService.init(config, options);
- super.setAliasService(zookeeperAliasService);
- super.init(config, options);
-
-
options.remove(ZookeeperRemoteAliasService.OPTION_NAME_SHOULD_CREATE_TOKENS_SUB_NODE);
-
options.remove(ZookeeperRemoteAliasService.OPTION_NAME_SHOULD_USE_LOCAL_ALIAS);
- }
-
- @Override
- protected void loadTokenAliasesFromPersistenceStore() {
- // NOP : registering 'knox/security/topology' child entry listener in
ZKRemoteAliasService ends-up reading existing ZK nodes
- // and with the help of RemoteTokenStateChangeListener notifications
in-memory collections will be populated
- // without loading them here directly
- }
-
- @Override
- protected boolean readyForEviction() {
- return true;
- }
-
- @Override
- protected char[] getPasswordUsingAliasService(String alias) throws
AliasServiceException {
- char[] password = super.getPasswordUsingAliasService(alias);
-
- if (password == null) {
- password = retry(alias);
- }
- return password;
- }
-
- /*
- * In HA scenarios, it might happen, that node1 generated a token but the
state
- * persister thread saves that token in ZK a bit later. If there is a
subsequent
- * call to this token on another node - e.g. node2 - before it's persisted
in ZK
- * the token would be considered unknown. (see CDPD-22225)
- *
- * To avoid this issue, the ZK token state service should retry to fetch the
- * token from ZK in every second until the token is found or the number of
- * retries exceeded the configured persistence interval
- */
- private char[] retry(String alias) throws AliasServiceException {
- char[] password = null;
- final Instant timeLimit =
Instant.now().plusSeconds(statePersistenceInterval).plusSeconds(1); // an
addition of 1 second as grace period
-
- while (password == null && timeLimit.isAfter(Instant.now())) {
- try {
- TimeUnit.SECONDS.sleep(1);
- log.retryZkFetchAlias(getDisplayableAliasText(alias));
- password = super.getPasswordUsingAliasService(alias);
- } catch (InterruptedException e) {
- log.failedRetryZkFetchAlias(getDisplayableAliasText(alias),
e.getMessage(), e);
- }
- }
- return password;
- }
-
- @Override
- public void onChanged(String alias, String updatedState) {
- processAlias(alias, updatedState);
- log.onRemoteTokenStateChanged(getDisplayableAliasText(alias));
- }
-
- @Override
- public void onRemoved(String alias) {
- final String tokenId = getTokenIdFromAlias(alias);
- removeTokensFromMemory(Collections.singleton(tokenId));
- log.onRemoteTokenStateRemoval(getDisplayableAliasText(alias));
- }
-
- private void processAlias(String alias, String value) {
- if (!ZookeeperRemoteAliasService.TOKENS_SUB_NODE_NAME.equals(alias)) {
- try {
- final String tokenId = getTokenIdFromAlias(alias);
- if (alias.endsWith(TOKEN_MAX_LIFETIME_POSTFIX)) {
- final long maxLifeTime = Long.parseLong(value);
- setMaxLifetime(tokenId, maxLifeTime);
- } else if (alias.endsWith(TOKEN_META_POSTFIX)) {
- addMetadataInMemory(tokenId, TokenMetadata.fromJSON(value));
- } else if (alias.endsWith(TOKEN_ISSUE_TIME_POSTFIX)) {
- setIssueTimeInMemory(tokenId, Long.parseLong(value));
- } else {
- final long expiration = Long.parseLong(value);
- updateExpirationInMemory(tokenId, expiration);
- }
- } catch (Throwable e) {
- log.errorWhileProcessingTokenAlias(getDisplayableAliasText(alias),
e.getMessage(), e);
- }
- }
- }
-
- private String getTokenIdFromAlias(final String alias) {
- return alias.contains(TOKEN_ALIAS_SUFFIX_DELIM) ? alias.substring(0,
alias.indexOf(TOKEN_ALIAS_SUFFIX_DELIM)) : alias;
- }
-
- private String getDisplayableAliasText(final String alias) {
- String tokenId = getTokenIdFromAlias(alias);
- String suffix = alias.length() > tokenId.length() ?
alias.substring(tokenId.length()) : "";
- return Tokens.getTokenIDDisplayText(tokenId) + suffix;
- }
-}
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
index 78dda075e..33d4bdc83 100644
---
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
@@ -17,10 +17,6 @@
*/
package org.apache.knox.gateway.util;
-import static
org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService.TOKEN_ISSUE_TIME_POSTFIX;
-import static
org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX;
-import static
org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService.TOKEN_META_POSTFIX;
-
import java.io.PrintStream;
import java.util.Arrays;
import java.util.HashMap;
@@ -42,6 +38,11 @@ public class TokenMigrationTool {
private static final TokenStateServiceMessages LOG =
MessagesFactory.get(TokenStateServiceMessages.class);
+ 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 final AliasService aliasService;
private final TokenStateService tokenStateService;
private final PrintStream out;
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 c33ecd22a..a412aa429 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
@@ -21,11 +21,8 @@ import static org.junit.Assert.assertTrue;
import org.apache.knox.gateway.services.ServiceType;
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.Test;
public class TokenStateServiceFactoryTest extends ServiceFactoryTest {
@@ -60,29 +57,6 @@ public class TokenStateServiceFactoryTest extends
ServiceFactoryTest {
}
}
- @Test
- public void shouldReturnAliasBasedTokenStateService() throws Exception {
- 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;
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
deleted file mode 100644
index f6971f483..000000000
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
+++ /dev/null
@@ -1,943 +0,0 @@
-/*
- * 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 org.apache.knox.gateway.config.GatewayConfig;
-import org.apache.knox.gateway.services.ServiceLifecycleException;
-import org.apache.knox.gateway.services.security.AbstractAliasService;
-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.security.token.impl.JWTToken;
-import org.apache.knox.gateway.services.token.state.JournalEntry;
-import org.apache.knox.gateway.services.token.state.TokenStateJournal;
-import
org.apache.knox.gateway.services.token.impl.state.TokenStateJournalFactory;
-import org.easymock.EasyMock;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.anyString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class AliasBasedTokenStateServiceTest extends
DefaultTokenStateServiceTest {
-
- private Long tokenStatePersistenceInterval = TimeUnit.SECONDS.toMillis(15);
-
- @Override
- protected long getTokenStatePersistenceInterval() {
- return (tokenStatePersistenceInterval != null) ?
tokenStatePersistenceInterval : super.getTokenStatePersistenceInterval();
- }
-
- /*
- * KNOX-2375
- */
- @Test
- public void testBulkTokenStateEviction() throws Exception {
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < 10 ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- List<String> testTokenStateAliases = new ArrayList<>();
- for (JWTToken token : testTokens) {
- String tokenId = token.getClaim(JWTToken.KNOX_ID_CLAIM);
- testTokenStateAliases.add(tokenId);
- testTokenStateAliases.add(tokenId +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX);
- }
-
- // Create a mock AliasService so we can verify that the expected bulk
removal method is invoked when the token state
- // reaper runs.
- AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
- EasyMock.expect(aliasService.getPasswordFromAliasForCluster(anyString(),
anyString()))
-
.andReturn(String.valueOf(System.currentTimeMillis()).toCharArray())
- .anyTimes();
-
EasyMock.expect(aliasService.getAliasesForCluster(AliasService.NO_CLUSTER_NAME)).andReturn(testTokenStateAliases).anyTimes();
- // Expecting the bulk alias removal method to be invoked only once, rather
than the individual alias removal method
- // invoked twice for every expired token.
- aliasService.removeAliasesForCluster(anyString(), anyObject());
- EasyMock.expectLastCall().andVoid().once();
-
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
-
- EasyMock.replay(aliasService);
-
- AliasBasedTokenStateService tss = new AliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new
TokenMetadata("alice"));
- assertTrue("Expected the token to have expired.",
tss.isExpired(token));
- }
-
- // Sleep to allow the eviction evaluation to be performed
- Thread.sleep(evictionInterval + (evictionInterval / 2));
- } finally {
- tss.stop();
- }
-
- // Verify that the expected method was invoked
- EasyMock.verify(aliasService);
- }
-
- @Test
- public void testAddAndRemoveTokenIncludesCache() throws Exception {
- final int TOKEN_COUNT = 10;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- Set<String> testTokenStateAliases = new HashSet<>();
- for (JWTToken token : testTokens) {
- String tokenId = token.getClaim(JWTToken.KNOX_ID_CLAIM);
- testTokenStateAliases.add(tokenId);
- testTokenStateAliases.add(tokenId +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX);
- testTokenStateAliases.add(tokenId +
AliasBasedTokenStateService.TOKEN_META_POSTFIX);
- testTokenStateAliases.add(tokenId +
AliasBasedTokenStateService.TOKEN_ISSUE_TIME_POSTFIX);
- }
-
- // Create a mock AliasService so we can verify that the expected bulk
removal method is invoked (and that the
- // individual removal method is NOT invoked) when the token state reaper
runs.
- AliasService aliasService = EasyMock.createMock(AliasService.class);
-
EasyMock.expect(aliasService.getAliasesForCluster(AliasService.NO_CLUSTER_NAME)).andReturn(new
ArrayList<>(testTokenStateAliases)).anyTimes();
- // Expecting the bulk alias removal method to be invoked only once, rather
than the individual alias removal method
- // invoked twice for every expired token.
-
aliasService.removeAliasesForCluster((EasyMock.eq(AliasService.NO_CLUSTER_NAME)),
EasyMock.eq(testTokenStateAliases));
- EasyMock.expectLastCall().andVoid().once();
-
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
-
- EasyMock.replay(aliasService);
-
- AliasBasedTokenStateService tss = new AliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss, 2);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss, 2);
- Map<String, Map<String, TokenMetadata>> metadata =
getMetadataMapField(tss, 2);
- Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 2);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new
TokenMetadata("alice"));
- }
-
- assertEquals("Expected the tokens to have been added in the base class
cache.", TOKEN_COUNT, tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been added in the
base class cache.",
- TOKEN_COUNT,
- maxTokenLifetimes.size());
- assertEquals("Expected the token metadata to have been added in the base
class cache.", TOKEN_COUNT, metadata.size());
- assertEquals("Expected the token issue times to have been added in the
base class cache.", TOKEN_COUNT, tokenIssueTimes.size());
-
- // Sleep to allow the eviction evaluation to be performed
- Thread.sleep(evictionInterval + (evictionInterval / 4));
-
- } finally {
- tss.stop();
- }
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
-
- assertEquals("Expected the tokens to have been removed from the base class
cache as a result of eviction.",
- 0,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been removed from the
base class cache as a result of eviction.",
- 0,
- maxTokenLifetimes.size());
- assertEquals("Expected the token metadata to have been removed from the
base class cache as a result of eviction.",
- 0,
- metadata.size());
- assertEquals("Expected the token issue times to have been removed from the
base class cache as a result of eviction.",
- 0,
- tokenIssueTimes.size());
- }
-
- /*
- * Verify that the token state reaper includes token state which has not
been cached, so it's not left in the keystore
- * forever.
- */
- @Ignore("I'm not sure if this is a valid use case since we have everything
in the cache when eviction takes place")
- @Test()
- public void testTokenEvictionIncludesUncachedAliases() throws Exception {
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < 10 ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- List<String> testTokenStateAliases = new ArrayList<>();
- for (JWTToken token : testTokens) {
- testTokenStateAliases.add(token.getClaim(JWTToken.KNOX_ID_CLAIM));
- testTokenStateAliases.add(token.getClaim(JWTToken.KNOX_ID_CLAIM) +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX);
- }
-
- // Add aliases for an uncached test token
- final JWTToken uncachedToken = createMockToken(System.currentTimeMillis()
- TimeUnit.SECONDS.toMillis(60));
- final String uncachedTokenId =
uncachedToken.getClaim(JWTToken.KNOX_ID_CLAIM);
- testTokenStateAliases.add(uncachedTokenId);
- testTokenStateAliases.add(uncachedTokenId +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX);
- final long uncachedTokenExpiration = System.currentTimeMillis();
- System.out.println("Uncached token ID: " + uncachedTokenId);
-
- final Set<String> expectedTokensToEvict = new HashSet<>();
- expectedTokensToEvict.addAll(testTokenStateAliases);
- expectedTokensToEvict.add(uncachedTokenId);
- expectedTokensToEvict.add(uncachedTokenId +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX);
-
- // Create a mock AliasService so we can verify that the expected bulk
removal method is invoked (and that the
- // individual removal method is NOT invoked) when the token state reaper
runs.
- AliasService aliasService = EasyMock.createMock(AliasService.class);
-
EasyMock.expect(aliasService.getAliasesForCluster(AliasService.NO_CLUSTER_NAME)).andReturn(testTokenStateAliases).anyTimes();
- // Expecting the bulk alias removal method to be invoked only once, rather
than the individual alias removal method
- // invoked twice for every expired token.
- aliasService.removeAliasesForCluster(anyString(),
EasyMock.eq(expectedTokensToEvict));
- EasyMock.expectLastCall().andVoid().once();
- aliasService.getPasswordFromAliasForCluster(AliasService.NO_CLUSTER_NAME,
uncachedTokenId);
-
EasyMock.expectLastCall().andReturn(String.valueOf(uncachedTokenExpiration).toCharArray()).once();
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
-
- EasyMock.replay(aliasService);
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens to have been added in the base class
cache.", 10, tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been added in the
base class cache.",
- 10,
- maxTokenLifetimes.size());
-
- // Sleep to allow the eviction evaluation to be performed, but only one
iteration
- Thread.sleep(evictionInterval + (evictionInterval / 4));
- } finally {
- tss.stop();
- }
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
-
- assertEquals("Expected the tokens to have been removed from the base class
cache as a result of eviction.",
- 0,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been removed from the
base class cache as a result of eviction.",
- 0,
- maxTokenLifetimes.size());
- }
-
- @Test
- public void testGetMaxLifetimeUsesCache() throws Exception {
- AliasService aliasService = EasyMock.createMock(AliasService.class);
- aliasService.addAliasesForCluster(anyString(), anyObject());
- EasyMock.expectLastCall().once(); // Expecting this during shutdown
-
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
-
- EasyMock.replay(aliasService);
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < 10 ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
-
- }
-
- assertEquals("Expected the tokens lifetimes to have been added in the
base class cache.",
- 10,
- maxTokenLifetimes.size());
-
- // Set the cache values to be different from the underlying alias value
- final long updatedMaxLifetime = evictionInterval * 5;
- for (Map.Entry<String, Long> entry : maxTokenLifetimes.entrySet()) {
- entry.setValue(updatedMaxLifetime);
- }
-
- // Verify that we get the cache value back
- for (String tokenId : maxTokenLifetimes.keySet()) {
- assertEquals("Expected the cached max lifetime, rather than the alias
value",
- updatedMaxLifetime,
- tss.getMaxLifetime(tokenId));
- }
- } finally {
- tss.stop();
- }
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
- }
-
- @Test
- public void testUpdateExpirationUsesCache() throws Exception {
- final AliasService aliasService = EasyMock.createMock(AliasService.class);
- // Neither addAliasForCluster nor removeAliasForCluster should be called
because updating expiration should happen in memory and let the
- // background persistence job done its job
- aliasService.addAliasesForCluster(anyString(), anyObject());
- EasyMock.expectLastCall().andVoid().once(); // Expecting this during
shutdown
-
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
- EasyMock.replay(aliasService);
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < 10 ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens expirations to have been added in the
base class cache.",
- 10,
- tokenExpirations.size());
-
- // Set the cache values to be different from the underlying alias value
- final long updatedExpiration = System.currentTimeMillis();
- for (String tokenId : tokenExpirations.keySet()) {
- tss.updateExpiration(tokenId, updatedExpiration);
- }
-
- // Invoking with true/false validation flags as it should not affect if
values are coming from the cache
- int count = 0;
- for (String tokenId : tokenExpirations.keySet()) {
- assertEquals("Expected the cached expiration to have been updated.",
updatedExpiration, tss.getTokenExpiration(tokenId, count++ % 2 == 0));
- }
-
- } finally {
- tss.stop();
- }
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
- }
-
- @Test
- public void testTokenStateJournaling() throws Exception {
- AliasService aliasService = EasyMock.createMock(AliasService.class);
- aliasService.getAliasesForCluster(anyString());
- EasyMock.expectLastCall().andReturn(Collections.emptyList()).anyTimes();
- aliasService.addAliasesForCluster(anyString(), anyObject());
- EasyMock.expectLastCall().once();
-
- //expecting this call when loading credentials from the keystore on startup
-
EasyMock.expect(aliasService.getPasswordsForGateway()).andReturn(Collections.emptyMap()).anyTimes();
-
- EasyMock.replay(aliasService);
-
- tokenStatePersistenceInterval = 1L; // Override the persistence interval
for this test
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
- initTokenStateService(tss);
-
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- Path journalDir = Paths.get(getGatewaySecurityDir(), "token-state");
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final List<String> tokenIds = new ArrayList<>();
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < 10 ; i++) {
- JWTToken token = createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60));
- testTokens.add(token);
- tokenIds.add(token.getClaim(JWTToken.KNOX_ID_CLAIM));
- }
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens lifetimes to have been added in the
base class cache.",
- 10,
- maxTokenLifetimes.size());
-
- // Check for the expected number of files corresponding to journal
entries
- List<Path> listing = Files.list(journalDir).collect(Collectors.toList());
- assertFalse(listing.isEmpty());
- assertEquals(10, listing.size());
-
- // Validate the journal entry file names
- for (Path p : listing) {
- Path filename = p.getFileName();
- String filenameString = filename.toString();
- assertTrue(filenameString.endsWith(".ts"));
- String tokenId = filenameString.substring(0, filenameString.length() -
3);
- assertTrue(tokenIds.contains(tokenId));
- }
-
- // Sleep to allow the persistence to be performed
- Thread.sleep(TimeUnit.SECONDS.toMillis(tokenStatePersistenceInterval) *
2);
-
- } finally {
- tss.stop();
- tokenStatePersistenceInterval = null;
- }
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
-
- // Verify that the journal entries were removed when the aliases were
created
- List<Path> listing = Files.list(journalDir).collect(Collectors.toList());
- assertTrue(listing.isEmpty());
- }
-
- @Test
- public void testLoadTokenStateJournalDuringInit() throws Exception {
- final int TOKEN_COUNT = 10;
-
- AliasService aliasService = EasyMock.createMock(AliasService.class);
- aliasService.getAliasesForCluster(anyString());
- EasyMock.expectLastCall().andReturn(Collections.emptyList()).anyTimes();
- EasyMock.replay(aliasService);
-
- // Create some test tokens
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- JWTToken token = createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60));
- testTokens.add(token);
- }
-
- // Persist the token state journal entries before initializing the
TokenStateService
- TokenStateJournal journal =
TokenStateJournalFactory.create(createMockGatewayConfig(false));
- for (JWTToken token : testTokens) {
- journal.add(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24),
- null);
- }
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
-
- // Initialize the service, and presumably load the previously-persisted
journal entries
- initTokenStateService(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
- Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 3);
-
- Set<AliasBasedTokenStateService.TokenState> unpersistedState =
getUnpersistedStateField(tss);
-
- assertEquals("Expected the tokens expirations to have been added in the
base class cache.",
- TOKEN_COUNT,
- tokenExpirations.size());
-
- assertEquals("Expected the tokens lifetimes to have been added in the base
class cache.",
- TOKEN_COUNT,
- maxTokenLifetimes.size());
-
- assertEquals("Expected the tokens issue times to have been added in the
base class cache.",
- TOKEN_COUNT,
- tokenIssueTimes.size());
-
- assertEquals("Expected the unpersisted state to have been added.",
- (TOKEN_COUNT * 3), // Two TokenState entries per token
(expiration, max lifetime, issue time)
- unpersistedState.size());
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
- }
-
- @Test
- public void testLoadTokenStateJournalDuringInitWithInvalidEntries() throws
Exception {
- final int TOKEN_COUNT = 5;
-
- AliasService aliasService = EasyMock.createMock(AliasService.class);
- aliasService.getAliasesForCluster(anyString());
- EasyMock.expectLastCall().andReturn(Collections.emptyList()).anyTimes();
- EasyMock.replay(aliasService);
-
- // Create some test tokens
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- JWTToken token = createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60));
- testTokens.add(token);
- }
-
- // Persist the token state journal entries before initializing the
TokenStateService
- TokenStateJournal journal =
TokenStateJournalFactory.create(createMockGatewayConfig(false));
- for (JWTToken token : testTokens) {
- journal.add(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24),
- null);
- }
-
- // Add an entry with an invalid token identifier
- journal.add(" ",
- System.currentTimeMillis(),
- System.currentTimeMillis(),
- System.currentTimeMillis(),
- null);
-
- // Add an entry with an invalid issue time
- journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
- "invalidLongValue",
- String.valueOf(System.currentTimeMillis()),
- String.valueOf(System.currentTimeMillis()),
- new TokenMetadata("testUser")));
-
- // Add an entry with an invalid expiration time
- journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
- String.valueOf(System.currentTimeMillis()),
- "invalidLongValue",
- String.valueOf(System.currentTimeMillis()),
- new TokenMetadata("testUser")));
-
- // Add an entry with an invalid max lifetime
- journal.add(new TestJournalEntry(UUID.randomUUID().toString(),
-
String.valueOf(System.currentTimeMillis()),
-
String.valueOf(System.currentTimeMillis()),
- "invalidLongValue",
- new TokenMetadata("testUser")));
-
- AliasBasedTokenStateService tss = new
NoEvictionAliasBasedTokenStateService();
- tss.setAliasService(aliasService);
-
- // Initialize the service, and presumably load the previously-persisted
journal entries
- initTokenStateService(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
- Map<String, Long> tokenIssueTimes = getTokenIssueTimesField(tss, 3);
-
- Set<AliasBasedTokenStateService.TokenState> unpersistedState =
getUnpersistedStateField(tss);
-
- assertEquals("Expected the tokens expirations to have been added in the
base class cache.",
- TOKEN_COUNT,
- tokenExpirations.size());
-
- assertEquals("Expected the tokens lifetimes to have been added in the base
class cache.",
- TOKEN_COUNT,
- maxTokenLifetimes.size());
-
- assertEquals("Expected the tokens issue times to have been added in the
base class cache.",
- TOKEN_COUNT,
- tokenIssueTimes.size());
-
- assertEquals("Expected the unpersisted state to have been added.",
- (TOKEN_COUNT * 3), // Two TokenState entries per token
(expiration, max lifetime, issue time)
- unpersistedState.size());
-
- // Verify that the expected methods were invoked
- EasyMock.verify(aliasService);
- }
-
- @Test
- public void ensureAliases() throws Exception {
- final int tokenCount = 1000;
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < tokenCount ; i++) {
- JWTToken token = createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60));
- testTokens.add(token);
- }
-
- final AliasBasedTokenStateService tss = (AliasBasedTokenStateService)
createTokenStateService();
- final long issueTime = System.currentTimeMillis();
- for (JWTToken token : testTokens) {
- tss.addToken(token, issueTime);
- tss.renewToken(token);
- }
-
- final List<AliasBasedTokenStateService.TokenState> unpersistedTokenStates
= new ArrayList<>(getUnpersistedStateField(tss, 0));
- final int expectedAliasCount = 3 * tokenCount; //expiration + max + issue
time for each token
- assertEquals(expectedAliasCount, unpersistedTokenStates.size());
- for (JWTToken token : testTokens) {
- String tokenId = token.getClaim(JWTToken.KNOX_ID_CLAIM);
- assertTrue(containsAlias(unpersistedTokenStates, tokenId));
- assertTrue(containsAlias(unpersistedTokenStates, tokenId +
AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX));
- }
- }
-
- private boolean containsAlias(List<AliasBasedTokenStateService.TokenState>
unpersistedTokenStates, String expectedAlias) {
- for(AliasBasedTokenStateService.TokenState tokenState :
unpersistedTokenStates) {
- if (tokenState.getAlias().equals(expectedAlias)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected TokenStateService createTokenStateService() throws Exception {
- AliasBasedTokenStateService tss = new AliasBasedTokenStateService();
- tss.setAliasService(new TestAliasService());
- initTokenStateService(tss);
- return tss;
- }
-
- /**
- * A dumbed-down AliasService implementation for testing purposes only.
- */
- private static final class TestAliasService extends AbstractAliasService {
-
- private final Map<String, Map<String, String>> clusterAliases= new
HashMap<>();
-
-
- @Override
- public List<String> getAliasesForCluster(String clusterName) throws
AliasServiceException {
- List<String> aliases = new ArrayList<>();
-
- if (clusterAliases.containsKey(clusterName)) {
- aliases.addAll(clusterAliases.get(clusterName).keySet());
- }
- return aliases;
- }
-
- @Override
- public void addAliasForCluster(String clusterName, String alias, String
value) throws AliasServiceException {
- Map<String, String> aliases = null;
- if (clusterAliases.containsKey(clusterName)) {
- aliases = clusterAliases.get(clusterName);
- } else {
- aliases = new HashMap<>();
- clusterAliases.put(clusterName, aliases);
- }
- aliases.put(alias, value);
- }
-
- @Override
- public void addAliasesForCluster(String clusterName, Map<String, String>
credentials) throws AliasServiceException {
- for (Map.Entry<String, String> credential : credentials.entrySet()) {
- addAliasForCluster(clusterName, credential.getKey(),
credential.getValue());
- }
- }
-
- @Override
- public void removeAliasForCluster(String clusterName, String alias) throws
AliasServiceException {
- if (clusterAliases.containsKey(clusterName)) {
- clusterAliases.get(clusterName).remove(alias);
- }
- }
-
- @Override
- public void removeAliasesForCluster(String clusterName, Set<String>
aliases) throws AliasServiceException {
- for (String alias : aliases) {
- removeAliasForCluster(clusterName, alias);
- }
- }
-
- @Override
- public char[] getPasswordFromAliasForCluster(String clusterName, String
alias) throws AliasServiceException {
- char[] value = null;
- if (clusterAliases.containsKey(clusterName)) {
- String valString = clusterAliases.get(clusterName).get(alias);
- if (valString != null) {
- value = valString.toCharArray();
- }
- }
- return value;
- }
-
- @Override
- public char[] getPasswordFromAliasForCluster(String clusterName, String
alias, boolean generate) throws AliasServiceException {
- return new char[0];
- }
-
- @Override
- public void generateAliasForCluster(String clusterName, String alias)
throws AliasServiceException {
- }
-
- @Override
- public char[] getPasswordFromAliasForGateway(String alias) throws
AliasServiceException {
- return getPasswordFromAliasForCluster(AliasService.NO_CLUSTER_NAME,
alias);
- }
-
- @Override
- public char[] getGatewayIdentityPassphrase() throws AliasServiceException {
- return new char[0];
- }
-
- @Override
- public char[] getGatewayIdentityKeystorePassword() throws
AliasServiceException {
- return new char[0];
- }
-
- @Override
- public char[] getSigningKeyPassphrase() throws AliasServiceException {
- return new char[0];
- }
-
- @Override
- public char[] getSigningKeystorePassword() throws AliasServiceException {
- return new char[0];
- }
-
- @Override
- public void generateAliasForGateway(String alias) throws
AliasServiceException {
- }
-
- @Override
- public Certificate getCertificateForGateway(String alias) throws
AliasServiceException {
- return null;
- }
-
- @Override
- public void init(GatewayConfig config, Map<String, String> options) throws
ServiceLifecycleException {
- }
-
- @Override
- public void start() throws ServiceLifecycleException {
- }
-
- @Override
- public void stop() throws ServiceLifecycleException {
- }
- }
-
- @Override
- protected void addToken(TokenStateService tss, String tokenId, long
issueTime, long expiration, long maxLifetime) {
- super.addToken(tss, tokenId, issueTime, expiration, maxLifetime);
-
- // Persist any unpersisted token state aliases
- triggerAliasPersistence(tss);
- }
-
- @Override
- protected void addToken(TokenStateService tss, JWTToken token, long
issueTime) {
- super.addToken(tss, token, issueTime);
-
- // Persist any unpersisted token state aliases
- triggerAliasPersistence(tss);
- }
-
- private void triggerAliasPersistence(TokenStateService tss) {
- if (tss instanceof AliasBasedTokenStateService) {
- try {
- Method m = tss.getClass().getDeclaredMethod("persistTokenState");
- m.setAccessible(true);
- m.invoke(tss);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private static Map<String, Long> getTokenExpirationsField(TokenStateService
tss) throws Exception {
- return getTokenExpirationsField(tss, 3);
- }
-
- private static Map<String, Long> getTokenExpirationsField(TokenStateService
tss, int level) throws Exception {
- return (Map<String, Long>) getField(tss, level, "tokenExpirations");
- }
-
- private static Object getField(TokenStateService tss, int level, String
fieldName) throws Exception {
- final Field field = getParentClass(tss, level).getDeclaredField(fieldName);
- field.setAccessible(true);
- return field.get(tss);
- }
-
- private static Class<TokenStateService> getParentClass(TokenStateService
tss, int level) {
- Class<TokenStateService> clazz = (Class<TokenStateService>) tss.getClass();
- for (int i = 1; i <= level; i++) {
- clazz = (Class<TokenStateService>) clazz.getSuperclass();
- }
- return clazz;
- }
-
- private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService
tss) throws Exception {
- return getMaxTokenLifetimesField(tss, 3);
- }
-
- private static Map<String, Long> getMaxTokenLifetimesField(TokenStateService
tss, int level) throws Exception {
- return (Map<String, Long>) getField(tss, level, "maxTokenLifetimes");
- }
-
- private static Map<String, Long> getTokenIssueTimesField(TokenStateService
tss, int level) throws Exception {
- return (Map<String, Long>) getField(tss, level, "tokenIssueTimes");
- }
-
- private static Map<String, Map<String, TokenMetadata>>
getMetadataMapField(TokenStateService tss, int level) throws Exception {
- return (Map<String, Map<String, TokenMetadata>>) getField(tss, level,
"metadataMap");
- }
-
- private static Set<AliasBasedTokenStateService.TokenState>
getUnpersistedStateField(TokenStateService tss) throws Exception {
- return getUnpersistedStateField(tss, 1);
- }
-
- private static Set<AliasBasedTokenStateService.TokenState>
getUnpersistedStateField(TokenStateService tss, int level) throws Exception {
- return (Set<AliasBasedTokenStateService.TokenState>) getField(tss, level,
"unpersistedState");
- }
-
- private static class TestJournalEntry implements JournalEntry {
-
- private String tokenId;
- private String issueTime;
- private String expiration;
- private String maxLifetime;
- private TokenMetadata tokenMetadata;
-
- TestJournalEntry(String tokenId, String issueTime, String expiration,
String maxLifetime, TokenMetadata tokenMetadata) {
- this.tokenId = tokenId;
- this.issueTime = issueTime;
- this.expiration = expiration;
- this.maxLifetime = maxLifetime;
- this.tokenMetadata = tokenMetadata;
- }
-
- @Override
- public String getTokenId() {
- return tokenId;
- }
-
- @Override
- public String getIssueTime() {
- return issueTime;
- }
-
- @Override
- public String getExpiration() {
- return expiration;
- }
-
- @Override
- public String getMaxLifetime() {
- return maxLifetime;
- }
-
- @Override
- public TokenMetadata getTokenMetadata() {
- return tokenMetadata;
- }
-
- @Override
- public String toString() {
- return tokenId + "," + issueTime + "," + expiration + "," + maxLifetime;
- }
- }
-
- private static class NoEvictionAliasBasedTokenStateService extends
AliasBasedTokenStateService {
-
- @Override
- protected boolean readyForEviction() {
- return false;
- }
-
- }
-
-}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
deleted file mode 100644
index 82ecbe8ef..000000000
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JournalBasedTokenStateServiceTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- *
- * 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 org.apache.knox.gateway.services.security.token.TokenStateService;
-import org.apache.knox.gateway.services.security.token.impl.JWTToken;
-import
org.apache.knox.gateway.services.token.impl.state.TokenStateJournalFactory;
-import org.apache.knox.gateway.services.token.state.TokenStateJournal;
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class JournalBasedTokenStateServiceTest extends
DefaultTokenStateServiceTest {
-
- @Override
- protected TokenStateService createTokenStateService() throws Exception {
- TokenStateService tss = new JournalBasedTokenStateService();
- initTokenStateService(tss);
- return tss;
- }
-
-
- @Test
- public void testBulkTokenStateEviction() throws Exception {
- final int TOKEN_COUNT = 5;
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- TokenStateService tss = createTokenStateService();
-
- TokenStateJournal journal = getJournalField(tss);
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- assertTrue("Expected the token to have expired.",
tss.isExpired(token));
- }
-
- assertEquals(TOKEN_COUNT, journal.get().size());
-
- // Sleep to allow the eviction evaluation to be performed
- Thread.sleep(evictionInterval + (evictionInterval / 2));
- } finally {
- tss.stop();
- }
-
- assertEquals(0, journal.get().size());
- }
-
- @Test
- public void testAddAndRemoveTokenIncludesCache() throws Exception {
- final int TOKEN_COUNT = 5;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- TokenStateService tss = createTokenStateService();
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens to have been added in the base
class cache.",
- TOKEN_COUNT,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been added in
the base class cache.",
- TOKEN_COUNT,
- maxTokenLifetimes.size());
-
- // Sleep to allow the eviction evaluation to be performed
- Thread.sleep(evictionInterval + (evictionInterval / 4));
-
- } finally {
- tss.stop();
- }
-
- assertEquals("Expected the tokens to have been removed from the base
class cache as a result of eviction.",
- 0,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been removed from
the base class cache as a result of eviction.",
- 0,
- maxTokenLifetimes.size());
- }
-
- /*
- * Verify that the token state reaper includes previously-persisted token
state, so it's not left in the file
- * system forever.
- */
- @Test
- public void testTokenEvictionIncludesPreviouslyPersistedJournalEntries()
throws Exception {
- final int TOKEN_COUNT = 5;
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- TokenStateJournal testJournal =
-
TokenStateJournalFactory.create(createMockGatewayConfig(false,
-
getGatewaySecurityDir(),
-
getTokenStatePersistenceInterval()));
-
- // Add a journal entry prior to initializing the TokenStateService
- final JWTToken uncachedToken =
createMockToken(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(60));
- final String uncachedTokenId =
uncachedToken.getClaim(JWTToken.KNOX_ID_CLAIM);
- testJournal.add(uncachedTokenId,
- System.currentTimeMillis(),
- uncachedToken.getExpiresDate().getTime(),
- maxTokenLifetime,
- null);
- assertEquals("Expected the uncached journal entry", 1,
testJournal.get().size());
-
- // Create and initialize the TokenStateService
- TokenStateService tss = createTokenStateService();
- TokenStateJournal journal = getJournalField(tss);
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- assertEquals("Expected the previously-persisted journal entry to have
been loaded into the cache.",
- 1,
- tokenExpirations.size());
- assertEquals("Expected the previously-persisted journal entry to have
been loaded into the cache.",
- 1,
- maxTokenLifetimes.size());
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens to have been added in the base
class cache.",
- TOKEN_COUNT + 1,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been added in
the base class cache.",
- TOKEN_COUNT + 1,
- maxTokenLifetimes.size());
- assertEquals("Expected the uncached journal entry in addition to
the cached tokens",
- TOKEN_COUNT + 1,
- journal.get().size());
-
-
- // Sleep to allow the eviction evaluation to be performed, but
only one iteration
- Thread.sleep(evictionInterval + (evictionInterval / 4));
- } finally {
- tss.stop();
- }
-
- assertEquals("Expected the tokens to have been removed from the base
class cache as a result of eviction.",
- 0,
- tokenExpirations.size());
- assertEquals("Expected the tokens lifetimes to have been removed from
the base class cache as a result of eviction.",
- 0,
- maxTokenLifetimes.size());
- assertEquals("Expected the journal entries to have been removed as a
result of the eviction",
- 0,
- journal.get().size());
- }
-
- @Test
- public void testGetMaxLifetimeUsesCache() throws Exception {
- final int TOKEN_COUNT = 10;
- TokenStateService tss = createTokenStateService();
-
- Map<String, Long> maxTokenLifetimes = getMaxTokenLifetimesField(tss);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
-
- }
-
- assertEquals("Expected the tokens lifetimes to have been added in
the base class cache.",
- TOKEN_COUNT,
- maxTokenLifetimes.size());
-
- // Set the cache values to be different from the underlying
journal entry value
- final long updatedMaxLifetime = evictionInterval * 5;
- for (Map.Entry<String, Long> entry : maxTokenLifetimes.entrySet())
{
- entry.setValue(updatedMaxLifetime);
- }
-
- // Verify that we get the cache value back
- for (String tokenId : maxTokenLifetimes.keySet()) {
- assertEquals("Expected the cached max lifetime, rather than
the journal entry value",
- updatedMaxLifetime,
- ((JournalBasedTokenStateService)
tss).getMaxLifetime(tokenId));
- }
- } finally {
- tss.stop();
- }
- }
-
- @Test
- public void testUpdateExpirationUsesCache() throws Exception {
- final int TOKEN_COUNT = 10;
- TokenStateService tss = createTokenStateService();
-
- Map<String, Long> tokenExpirations = getTokenExpirationsField(tss);
-
- final long evictionInterval = TimeUnit.SECONDS.toMillis(3);
- final long maxTokenLifetime = evictionInterval * 3;
-
- final Set<JWTToken> testTokens = new HashSet<>();
- for (int i = 0; i < TOKEN_COUNT ; i++) {
- testTokens.add(createMockToken(System.currentTimeMillis() -
TimeUnit.SECONDS.toMillis(60)));
- }
-
- try {
- tss.start();
-
- // Add the expired tokens
- for (JWTToken token : testTokens) {
- tss.addToken(token.getClaim(JWTToken.KNOX_ID_CLAIM),
- System.currentTimeMillis(),
- token.getExpiresDate().getTime(),
- maxTokenLifetime);
- }
-
- assertEquals("Expected the tokens expirations to have been added
in the base class cache.",
- TOKEN_COUNT,
- tokenExpirations.size());
-
- // Set the cache values to be different from the underlying
journal entry value
- final long updatedExpiration = System.currentTimeMillis();
- for (String tokenId : tokenExpirations.keySet()) {
- ((JournalBasedTokenStateService)
tss).updateExpiration(tokenId, updatedExpiration);
- }
-
- // Invoking with true/false validation flags as it should not
affect if values are coming from the cache
- int count = 0;
- for (String tokenId : tokenExpirations.keySet()) {
- assertEquals("Expected the cached expiration to have been
updated.",
- updatedExpiration,
- tss.getTokenExpiration(tokenId, count++ % 2 ==
0));
- }
-
- } finally {
- tss.stop();
- }
- }
-
- private static TokenStateJournal getJournalField(TokenStateService tss)
throws Exception {
- Field journalField =
JournalBasedTokenStateService.class.getDeclaredField("journal");
- journalField.setAccessible(true);
- return (TokenStateJournal) journalField.get(tss);
- }
-
- private static Map<String, Long>
getTokenExpirationsField(TokenStateService tss) throws Exception {
- Field tokenExpirationsField =
tss.getClass().getSuperclass().getSuperclass().getDeclaredField("tokenExpirations");
- tokenExpirationsField.setAccessible(true);
- return (Map<String, Long>) tokenExpirationsField.get(tss);
- }
-
- private static Map<String, Long>
getMaxTokenLifetimesField(TokenStateService tss) throws Exception {
- Field maxTokenLifetimesField =
tss.getClass().getSuperclass().getSuperclass().getDeclaredField("maxTokenLifetimes");
- maxTokenLifetimesField.setAccessible(true);
- return (Map<String, Long>) maxTokenLifetimesField.get(tss);
- }
-}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
deleted file mode 100644
index 2b8e32d1f..000000000
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * 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.GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS;
-import static
org.apache.knox.gateway.config.GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.lang.reflect.Method;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Properties;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-
-import com.google.common.io.Files;
-import org.apache.commons.io.FileUtils;
-import org.apache.knox.gateway.config.GatewayConfig;
-import org.apache.knox.gateway.service.config.remote.zk.ZooKeeperClientService;
-import
org.apache.knox.gateway.service.config.remote.zk.ZooKeeperClientServiceProvider;
-import org.apache.knox.gateway.services.GatewayServices;
-import org.apache.knox.gateway.services.ServiceType;
-import
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClientService;
-import org.apache.knox.gateway.services.security.AliasService;
-import org.apache.knox.gateway.services.security.KeystoreService;
-import org.apache.knox.gateway.services.security.MasterService;
-import org.apache.knox.gateway.services.security.token.TokenMetadata;
-import org.apache.knox.test.TestUtils;
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.ZooKeeper;
-import org.apache.zookeeper.server.embedded.ExitHandler;
-import org.apache.zookeeper.server.embedded.ZooKeeperServerEmbedded;
-import org.easymock.EasyMock;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class ZookeeperTokenStateServiceTest {
-
- @ClassRule
- public static final TemporaryFolder testFolder = new TemporaryFolder();
- private static final String CONFIG_MONITOR_NAME =
"remoteConfigMonitorClient";
- private static final long SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL = 2L;
- private static final long LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL = 5L;
- private static ZooKeeperServerEmbedded zkServer;
- private static ZooKeeper zkClient;
- private static File tempDir;
-
- @BeforeClass
- public static void configureAndStartZKCluster() throws Exception {
- tempDir = Files.createTempDir();
- Properties configuration = new Properties();
- int port = TestUtils.findFreePort();
- configuration.setProperty("clientPort", String.valueOf(port));
- // Removed SASL authentication to fix JDK 17 compatibility issues
- // configuration.put("authProvider.1",
"org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
- // configuration.put("requireClientAuthScheme", "sasl");
- configuration.put("admin.enableServer", "false");
- zkServer = ZooKeeperServerEmbedded
- .builder()
- .exitHandler(ExitHandler.LOG_ONLY)
- .baseDir(tempDir.toPath())
- .configuration(configuration)
- .build();
- zkServer.start();
-
- CountDownLatch latch = new CountDownLatch(1);
- zkClient = new ZooKeeper("localhost:" + port, 40000, event -> {
- System.out.println("event " + event);
- latch.countDown();
- });
- latch.await();
- }
-
- @AfterClass
- public static void tearDownSuite() throws Exception {
- if (tempDir != null) {
- FileUtils.deleteDirectory(tempDir);
- }
- if (zkClient != null) {
- zkClient.close();
- }
- if (zkServer != null) {
- zkServer.close();
- }
- }
-
- @Test
- public void testStoringTokenAliasesInZookeeper() throws Exception {
- final ZookeeperTokenStateService zktokenStateService =
setupZkTokenStateService(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
-
-
assertFalse(zkNodeExists("/knox/security/topology/__gateway/tokens/a0/a0-token1"));
-
assertFalse(zkNodeExists("/knox/security/topology/__gateway/tokens/a0/a0-token1"
+ AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX));
-
- zktokenStateService.addToken("a0-token1", 1L, 2L);
-
- // give some time for the token state service to persist the token aliases
in ZK (doubled the persistence interval)
- Thread.sleep(2 * SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1000);
-
-
assertTrue(zkNodeExists("/knox/security/topology/__gateway/tokens/a0/a0-token1"));
-
assertTrue(zkNodeExists("/knox/security/topology/__gateway/tokens/a0/a0-token1"
+ AliasBasedTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX));
- }
-
- @Test
- public void testRetry() throws Exception {
- final ZookeeperTokenStateService zktokenStateServiceNode1 =
setupZkTokenStateService(LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
- final ZookeeperTokenStateService zktokenStateServiceNode2 =
setupZkTokenStateService(LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
- final String tokenId = UUID.randomUUID().toString();
- zktokenStateServiceNode1.addToken(tokenId, 10L, 2000L);
- final long expiration =
zktokenStateServiceNode2.getTokenExpiration(tokenId);
- Thread.sleep(LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1000);
- assertEquals(2000L, expiration);
-
- final String userName = "testUser";
- final String comment = "This is my test comment";
- zktokenStateServiceNode1.addMetadata(tokenId, new TokenMetadata(userName,
comment, true));
- Thread.sleep(LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1000);
- assertEquals(userName,
zktokenStateServiceNode2.getTokenMetadata(tokenId).getUserName());
- assertEquals(comment,
zktokenStateServiceNode2.getTokenMetadata(tokenId).getComment());
- assertTrue(zktokenStateServiceNode2.getTokenMetadata(tokenId).isEnabled());
- }
-
- @Test
- public void testRenewal() throws Exception {
- final ZookeeperTokenStateService zktokenStateServiceNode1 =
setupZkTokenStateService(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
- final ZookeeperTokenStateService zktokenStateServiceNode2 =
setupZkTokenStateService(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
- final String tokenId = "a1-token";
- final long issueTime = System.currentTimeMillis();
- final long tokenTTL = 1000L;
- final long renewInterval = 2000L;
-
- zktokenStateServiceNode1.addToken(tokenId, issueTime, issueTime +
tokenTTL);
- Thread.sleep(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1500);
- assertEquals(zktokenStateServiceNode1.getTokenExpiration(tokenId),
zktokenStateServiceNode2.getTokenExpiration(tokenId));
-
- //now renew token on node 1 and check if renewal is reflected on node2
- zktokenStateServiceNode1.renewToken(tokenId, renewInterval);
- Thread.sleep(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1500);
- assertEquals(zktokenStateServiceNode1.getTokenExpiration(tokenId),
zktokenStateServiceNode2.getTokenExpiration(tokenId));
- }
-
- @Test
- public void testTokenIDDisplayText() throws Exception {
- ZookeeperTokenStateService tss =
setupZkTokenStateService(SHORT_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL);
- Method m = tss.getClass().getDeclaredMethod("getDisplayableAliasText",
String.class);
- m.setAccessible(true);
- final String uuid = UUID.randomUUID().toString();
- final String maxAlias = uuid +
ZookeeperTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX;
- final String metaAlias = uuid +
ZookeeperTokenStateService.TOKEN_META_POSTFIX;
-
- // Check an expiration alias
- String displayableUUID = (String) m.invoke(tss, uuid);
- assertTrue(displayableUUID.length() < uuid.length());
- assertEquals(8, displayableUUID.indexOf("..."));
-
- // Check a max lifetime alias
- String displayableMaxAlias = (String) m.invoke(tss, maxAlias);
- assertFalse(displayableMaxAlias.contains(uuid));
- assertTrue(displayableMaxAlias.length() < maxAlias.length());
- assertEquals(8, displayableMaxAlias.indexOf("..."));
-
assertTrue(displayableMaxAlias.endsWith(ZookeeperTokenStateService.TOKEN_MAX_LIFETIME_POSTFIX));
-
- // Check a metadata alias
- String displayableMetaAlias = (String) m.invoke(tss, metaAlias);
- assertFalse(displayableMetaAlias.contains(uuid));
- assertTrue(displayableMetaAlias.length() < metaAlias.length());
- assertEquals(8, displayableMetaAlias.indexOf("..."));
-
assertTrue(displayableMetaAlias.endsWith(ZookeeperTokenStateService.TOKEN_META_POSTFIX));
-
- }
-
- private ZookeeperTokenStateService setupZkTokenStateService(long
persistenceInterval) throws Exception {
- // mocking GatewayConfig
- final GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
-
expect(gc.getRemoteRegistryConfigurationNames()).andReturn(Collections.singletonList(CONFIG_MONITOR_NAME)).anyTimes();
- // Add null check to prevent NullPointerException if Zookeeper server
failed to start
- if (zkServer == null) {
- throw new IllegalStateException("Zookeeper server failed to start in
@BeforeClass");
- }
- final String registryConfig = REMOTE_CONFIG_REGISTRY_TYPE + "=" +
ZooKeeperClientService.TYPE + ";" + REMOTE_CONFIG_REGISTRY_ADDRESS + "=" +
zkServer.getConnectionString();
-
expect(gc.getRemoteRegistryConfiguration(CONFIG_MONITOR_NAME)).andReturn(registryConfig).anyTimes();
-
expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(CONFIG_MONITOR_NAME).anyTimes();
- expect(gc.getAlgorithm()).andReturn("AES").anyTimes();
- expect(gc.isRemoteAliasServiceEnabled()).andReturn(true).anyTimes();
-
expect(gc.getKnoxTokenStateAliasPersistenceInterval()).andReturn(persistenceInterval).anyTimes();
- final Path baseFolder =
Paths.get(testFolder.newFolder().getAbsolutePath());
- expect(gc.getGatewayDataDir()).andReturn(Paths.get(baseFolder.toString(),
"data").toString()).anyTimes();
-
expect(gc.getGatewayKeystoreDir()).andReturn(Paths.get(baseFolder.toString(),
"data", "keystores").toString()).anyTimes();
-
expect(gc.getGatewaySecurityDir()).andReturn(Paths.get(baseFolder.toString(),
"security").toString()).anyTimes();
- replay(gc);
-
- // mocking GatewayServices
- final GatewayServices gatewayServices =
EasyMock.createNiceMock(GatewayServices.class);
- final char[] masterSecret =
"ThisIsMySup3rS3cr3tM4sterPassW0rd!".toCharArray();
- final MasterService masterService =
EasyMock.createNiceMock(MasterService.class);
- expect(masterService.getMasterSecret()).andReturn(masterSecret).anyTimes();
-
expect(gatewayServices.getService(ServiceType.MASTER_SERVICE)).andReturn(masterService).anyTimes();
- final KeystoreService keystoreservice =
EasyMock.createNiceMock(KeystoreService.class);
-
expect(keystoreservice.getCredentialStoreForCluster(AliasService.NO_CLUSTER_NAME)).andReturn(null).anyTimes();
-
expect(gatewayServices.getService(ServiceType.KEYSTORE_SERVICE)).andReturn(keystoreservice).anyTimes();
- final AliasService aliasService =
EasyMock.createNiceMock(AliasService.class);
-
expect(gatewayServices.getService(ServiceType.ALIAS_SERVICE)).andReturn(aliasService).anyTimes();
- final RemoteConfigurationRegistryClientService clientService = (new
ZooKeeperClientServiceProvider()).newInstance();
- clientService.setAliasService(aliasService);
- clientService.init(gc, Collections.emptyMap());
-
expect(gatewayServices.getService(ServiceType.REMOTE_REGISTRY_CLIENT_SERVICE)).andReturn(clientService).anyTimes();
- replay(gatewayServices, masterService, keystoreservice);
-
- final ZookeeperTokenStateService zktokenStateService = new
ZookeeperTokenStateService(gatewayServices);
- zktokenStateService.init(gc, new HashMap<>());
- zktokenStateService.start();
- return zktokenStateService;
- }
-
- private boolean zkNodeExists(String nodeName) throws InterruptedException,
KeeperException {
- return zkClient.exists(nodeName, false) != null;
- }
-}