diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 2717d936c8cc0..ca9bc50600306 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -53,8 +53,6 @@ static TransportVersion def(int id) {
}
// TODO: ES-10337 we can remove all transport versions earlier than 8.18
- public static final TransportVersion V_7_1_0 = def(7_01_00_99);
- public static final TransportVersion V_7_2_0 = def(7_02_00_99);
public static final TransportVersion V_7_3_0 = def(7_03_00_99);
public static final TransportVersion V_7_3_2 = def(7_03_02_99);
public static final TransportVersion V_7_4_0 = def(7_04_00_99);
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java
index 19f8c6d31f1dd..8731804f03046 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java
@@ -55,10 +55,8 @@ public SecurityFeatureSetUsage(StreamInput in) throws IOException {
realmsUsage = in.readGenericMap();
rolesStoreUsage = in.readGenericMap();
sslUsage = in.readGenericMap();
- if (in.getTransportVersion().onOrAfter(TransportVersions.V_7_2_0)) {
- tokenServiceUsage = in.readGenericMap();
- apiKeyServiceUsage = in.readGenericMap();
- }
+ tokenServiceUsage = in.readGenericMap();
+ apiKeyServiceUsage = in.readGenericMap();
auditUsage = in.readGenericMap();
ipFilterUsage = in.readGenericMap();
anonymousUsage = in.readGenericMap();
@@ -121,10 +119,8 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeGenericMap(realmsUsage);
out.writeGenericMap(rolesStoreUsage);
out.writeGenericMap(sslUsage);
- if (out.getTransportVersion().onOrAfter(TransportVersions.V_7_2_0)) {
- out.writeGenericMap(tokenServiceUsage);
- out.writeGenericMap(apiKeyServiceUsage);
- }
+ out.writeGenericMap(tokenServiceUsage);
+ out.writeGenericMap(apiKeyServiceUsage);
out.writeGenericMap(auditUsage);
out.writeGenericMap(ipFilterUsage);
out.writeGenericMap(anonymousUsage);
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/TokensInvalidationResult.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/TokensInvalidationResult.java
index e73efd9d6dd9e..decb4b5e06352 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/TokensInvalidationResult.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/TokensInvalidationResult.java
@@ -8,7 +8,6 @@
package org.elasticsearch.xpack.core.security.authc.support;
import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
@@ -59,9 +58,6 @@ public TokensInvalidationResult(StreamInput in) throws IOException {
this.invalidatedTokens = in.readStringCollectionAsList();
this.previouslyInvalidatedTokens = in.readStringCollectionAsList();
this.errors = in.readCollectionAsList(StreamInput::readException);
- if (in.getTransportVersion().before(TransportVersions.V_7_2_0)) {
- in.readVInt();
- }
this.restStatus = RestStatus.readFrom(in);
}
@@ -109,9 +105,6 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeStringCollection(invalidatedTokens);
out.writeStringCollection(previouslyInvalidatedTokens);
out.writeCollection(errors, StreamOutput::writeException);
- if (out.getTransportVersion().before(TransportVersions.V_7_2_0)) {
- out.writeVInt(5);
- }
RestStatus.writeTo(out, restStatus);
}
}
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java
index fef1a98ca67e9..0a6ebd86dce31 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java
@@ -750,37 +750,6 @@ public void testClientCredentialsGrant() throws Exception {
assertUnauthorizedToken(createTokenResponse.accessToken());
}
- public void testAuthenticateWithWrongToken() throws Exception {
- final TokenService tokenService = internalCluster().getInstance(TokenService.class);
- OAuth2Token response = createToken(TEST_USER_NAME, SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING);
- assertNotNull(response.getRefreshToken());
- // Assert that we can authenticate with the access token
- assertAuthenticateWithToken(response.accessToken(), TEST_USER_NAME);
- // Now attempt to authenticate with an invalid access token string
- assertUnauthorizedToken(randomAlphaOfLengthBetween(0, 128));
- // Now attempt to authenticate with an invalid access token with valid structure (pre 7.2)
- assertUnauthorizedToken(
- tokenService.prependVersionAndEncodeAccessToken(
- TransportVersions.V_7_1_0,
- tokenService.getRandomTokenBytes(TransportVersions.V_7_1_0, randomBoolean()).v1()
- )
- );
- // Now attempt to authenticate with an invalid access token with valid structure (after 7.2 pre 8.10)
- assertUnauthorizedToken(
- tokenService.prependVersionAndEncodeAccessToken(
- TransportVersions.V_7_4_0,
- tokenService.getRandomTokenBytes(TransportVersions.V_7_4_0, randomBoolean()).v1()
- )
- );
- // Now attempt to authenticate with an invalid access token with valid structure (current version)
- assertUnauthorizedToken(
- tokenService.prependVersionAndEncodeAccessToken(
- TransportVersion.current(),
- tokenService.getRandomTokenBytes(TransportVersion.current(), randomBoolean()).v1()
- )
- );
- }
-
@Before
public void waitForSecurityIndexWritable() throws Exception {
createSecurityIndexWithWaitForActiveShards();
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java
index 6e7ab91c0ba46..38ab34a21587d 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java
@@ -48,9 +48,7 @@
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
-import org.elasticsearch.common.io.stream.OutputStreamStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
-import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
@@ -59,7 +57,6 @@
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.core.Nullable;
-import org.elasticsearch.core.Streams;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple;
@@ -94,10 +91,8 @@
import org.elasticsearch.xpack.security.support.SecurityIndexManager.IndexState;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -134,7 +129,6 @@
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
@@ -207,10 +201,6 @@ public class TokenService {
static final int MINIMUM_BYTES = VERSION_BYTES + TOKEN_LENGTH + 1;
static final int LEGACY_MINIMUM_BASE64_BYTES = Double.valueOf(Math.ceil((4 * LEGACY_MINIMUM_BYTES) / 3)).intValue();
public static final int MINIMUM_BASE64_BYTES = Double.valueOf(Math.ceil((4 * MINIMUM_BYTES) / 3)).intValue();
- static final TransportVersion VERSION_HASHED_TOKENS = TransportVersions.V_7_2_0;
- static final TransportVersion VERSION_TOKENS_INDEX_INTRODUCED = TransportVersions.V_7_2_0;
- static final TransportVersion VERSION_ACCESS_TOKENS_AS_UUIDS = TransportVersions.V_7_2_0;
- static final TransportVersion VERSION_MULTIPLE_CONCURRENT_REFRESHES = TransportVersions.V_7_2_0;
static final TransportVersion VERSION_CLIENT_AUTH_FOR_REFRESH = TransportVersions.V_8_2_0;
static final TransportVersion VERSION_GET_TOKEN_DOC_FOR_REFRESH = TransportVersions.V_8_10_X;
@@ -275,8 +265,7 @@ public TokenService(
/**
* Creates an access token and optionally a refresh token as well, based on the provided authentication and metadata with
- * auto-generated values. The created tokens are stored in the security index for versions up to
- * {@link #VERSION_TOKENS_INDEX_INTRODUCED} and to a specific security tokens index for later versions.
+ * auto-generated values. The created tokens are stored in a specific security tokens index.
*/
public void createOAuth2Tokens(
Authentication authentication,
@@ -293,8 +282,7 @@ public void createOAuth2Tokens(
/**
* Creates an access token and optionally a refresh token as well from predefined values, based on the provided authentication and
- * metadata. The created tokens are stored in the security index for versions up to {@link #VERSION_TOKENS_INDEX_INTRODUCED} and to a
- * specific security tokens index for later versions.
+ * metadata. The created tokens are stored in specific security tokens index.
*/
// public for testing
public void createOAuth2Tokens(
@@ -316,21 +304,16 @@ public void createOAuth2Tokens(
*
* @param accessTokenBytes The predefined seed value for the access token. This will then be
*
- * - Encrypted before stored for versions before {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Hashed before stored for versions after {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Stored in the security index for versions up to {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Stored in a specific security tokens index for versions after
- * {@link #VERSION_TOKENS_INDEX_INTRODUCED}
+ * - Hashed before stored
+ * - Stored in a specific security tokens index
* - Prepended with a version ID and Base64 encoded before returned to the caller of the APIs
*
* @param refreshTokenBytes The predefined seed value for the access token. This will then be
*
- * - Hashed before stored for versions after {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Stored in the security index for versions up to {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Stored in a specific security tokens index for versions after
- * {@link #VERSION_TOKENS_INDEX_INTRODUCED}
- * - Prepended with a version ID and encoded with Base64 before returned to the caller of the APIs
- * for versions after {@link #VERSION_TOKENS_INDEX_INTRODUCED}
+ * - Hashed before stored
+ * - Stored in a specific security tokens index
+ * - Prepended with a version ID and encoded with
+ * Base64 before returned to the caller of the APIs
*
* @param tokenVersion The version of the nodes with which these tokens will be compatible.
* @param authentication The authentication object representing the user for which the tokens are created
@@ -386,7 +369,7 @@ private void createOAuth2Tokens(
} else {
refreshTokenToStore = refreshTokenToReturn = null;
}
- } else if (tokenVersion.onOrAfter(VERSION_HASHED_TOKENS)) {
+ } else {
assert accessTokenBytes.length == RAW_TOKEN_BYTES_LENGTH;
userTokenId = hashTokenString(Strings.BASE_64_NO_PADDING_URL_ENCODER.encodeToString(accessTokenBytes));
accessTokenToStore = null;
@@ -397,18 +380,6 @@ private void createOAuth2Tokens(
} else {
refreshTokenToStore = refreshTokenToReturn = null;
}
- } else {
- assert accessTokenBytes.length == RAW_TOKEN_BYTES_LENGTH;
- userTokenId = Strings.BASE_64_NO_PADDING_URL_ENCODER.encodeToString(accessTokenBytes);
- accessTokenToStore = null;
- if (refreshTokenBytes != null) {
- assert refreshTokenBytes.length == RAW_TOKEN_BYTES_LENGTH;
- refreshTokenToStore = refreshTokenToReturn = Strings.BASE_64_NO_PADDING_URL_ENCODER.encodeToString(
- refreshTokenBytes
- );
- } else {
- refreshTokenToStore = refreshTokenToReturn = null;
- }
}
UserToken userToken = new UserToken(userTokenId, tokenVersion, tokenAuth, getExpirationTime(), metadata);
tokenDocument = createTokenDocument(userToken, accessTokenToStore, refreshTokenToStore, originatingClientAuth);
@@ -421,24 +392,23 @@ private void createOAuth2Tokens(
final RefreshPolicy tokenCreationRefreshPolicy = tokenVersion.onOrAfter(VERSION_GET_TOKEN_DOC_FOR_REFRESH)
? RefreshPolicy.NONE
: RefreshPolicy.WAIT_UNTIL;
- final SecurityIndexManager tokensIndex = getTokensIndexForVersion(tokenVersion);
logger.debug(
() -> format(
"Using refresh policy [%s] when creating token doc [%s] in the security index [%s]",
tokenCreationRefreshPolicy,
documentId,
- tokensIndex.aliasName()
+ securityTokensIndex.aliasName()
)
);
- final IndexRequest indexTokenRequest = client.prepareIndex(tokensIndex.aliasName())
+ final IndexRequest indexTokenRequest = client.prepareIndex(securityTokensIndex.aliasName())
.setId(documentId)
.setOpType(OpType.CREATE)
.setSource(tokenDocument, XContentType.JSON)
.setRefreshPolicy(tokenCreationRefreshPolicy)
.request();
- tokensIndex.forCurrentProject()
+ securityTokensIndex.forCurrentProject()
.prepareIndexIfNeededThenExecute(
- ex -> listener.onFailure(traceLog("prepare tokens index [" + tokensIndex.aliasName() + "]", documentId, ex)),
+ ex -> listener.onFailure(traceLog("prepare tokens index [" + securityTokensIndex.aliasName() + "]", documentId, ex)),
() -> executeAsyncWithOrigin(
client,
SECURITY_ORIGIN,
@@ -560,17 +530,16 @@ private void getTokenDocById(
@Nullable String storedRefreshToken,
ActionListener listener
) {
- final SecurityIndexManager tokensIndex = getTokensIndexForVersion(tokenVersion);
- final IndexState projectSecurityIndex = tokensIndex.forCurrentProject();
+ final IndexState projectSecurityIndex = securityTokensIndex.forCurrentProject();
if (projectSecurityIndex.isAvailable(PRIMARY_SHARDS) == false) {
- logger.warn("failed to get access token [{}] because index [{}] is not available", tokenId, tokensIndex.aliasName());
+ logger.warn("failed to get access token [{}] because index [{}] is not available", tokenId, securityTokensIndex.aliasName());
listener.onFailure(projectSecurityIndex.getUnavailableReason(PRIMARY_SHARDS));
return;
}
- final GetRequest getRequest = client.prepareGet(tokensIndex.aliasName(), getTokenDocumentId(tokenId)).request();
+ final GetRequest getRequest = client.prepareGet(securityTokensIndex.aliasName(), getTokenDocumentId(tokenId)).request();
final Consumer onFailure = ex -> listener.onFailure(traceLog("get token from id", tokenId, ex));
projectSecurityIndex.checkIndexVersionThenExecute(
- ex -> listener.onFailure(traceLog("prepare tokens index [" + tokensIndex.aliasName() + "]", tokenId, ex)),
+ ex -> listener.onFailure(traceLog("prepare tokens index [" + securityTokensIndex.aliasName() + "]", tokenId, ex)),
() -> executeAsyncWithOrigin(
client.threadPool().getThreadContext(),
SECURITY_ORIGIN,
@@ -616,7 +585,11 @@ private void getTokenDocById(
// if the index or the shard is not there / available we assume that
// the token is not valid
if (isShardNotAvailableException(e)) {
- logger.warn("failed to get token doc [{}] because index [{}] is not available", tokenId, tokensIndex.aliasName());
+ logger.warn(
+ "failed to get token doc [{}] because index [{}] is not available",
+ tokenId,
+ securityTokensIndex.aliasName()
+ );
} else {
logger.error(() -> "failed to get token doc [" + tokenId + "]", e);
}
@@ -656,7 +629,7 @@ void decodeToken(String token, boolean validateUserToken, ActionListener VERSION_ACCESS_TOKENS_UUIDS cluster
if (in.available() < MINIMUM_BYTES) {
logger.debug("invalid token, smaller than [{}] bytes", MINIMUM_BYTES);
@@ -666,41 +639,6 @@ void decodeToken(String token, boolean validateUserToken, ActionListener {
- if (decodeKey != null) {
- try {
- final Cipher cipher = getDecryptionCipher(iv, decodeKey, version, decodedSalt);
- final String tokenId = decryptTokenId(encryptedTokenId, cipher, version);
- getAndValidateUserToken(tokenId, version, null, validateUserToken, listener);
- } catch (IOException | GeneralSecurityException e) {
- // could happen with a token that is not ours
- logger.warn("invalid token", e);
- listener.onResponse(null);
- }
- } else {
- // could happen with a token that is not ours
- listener.onResponse(null);
- }
- }, listener::onFailure));
- } else {
- logger.debug(() -> format("invalid key %s key: %s", passphraseHash, keyCache.cache.keySet()));
- listener.onResponse(null);
- }
}
} catch (Exception e) {
// could happen with a token that is not ours
@@ -858,11 +796,7 @@ private void indexInvalidation(
final Set idsOfOlderTokens = new HashSet<>();
boolean anyOlderTokensBeforeRefreshViaGet = false;
for (UserToken userToken : userTokens) {
- if (userToken.getTransportVersion().onOrAfter(VERSION_TOKENS_INDEX_INTRODUCED)) {
- idsOfRecentTokens.add(userToken.getId());
- } else {
- idsOfOlderTokens.add(userToken.getId());
- }
+ idsOfRecentTokens.add(userToken.getId());
anyOlderTokensBeforeRefreshViaGet |= userToken.getTransportVersion().before(VERSION_GET_TOKEN_DOC_FOR_REFRESH);
}
final RefreshPolicy tokensInvalidationRefreshPolicy = anyOlderTokensBeforeRefreshViaGet
@@ -1133,7 +1067,7 @@ private void findTokenFromRefreshToken(String refreshToken, Iterator
);
getTokenDocById(userTokenId, version, null, storedRefreshToken, listener);
}
- } else if (version.onOrAfter(VERSION_HASHED_TOKENS)) {
+ } else {
final String unencodedRefreshToken = in.readString();
if (unencodedRefreshToken.length() != TOKEN_LENGTH) {
logger.debug("Decoded refresh token [{}] with version [{}] is invalid.", unencodedRefreshToken, version);
@@ -1142,9 +1076,6 @@ private void findTokenFromRefreshToken(String refreshToken, Iterator
final String hashedRefreshToken = hashTokenString(unencodedRefreshToken);
findTokenFromRefreshToken(hashedRefreshToken, securityTokensIndex, backoff, listener);
}
- } else {
- logger.debug("Unrecognized refresh token version [{}].", version);
- listener.onResponse(null);
}
} catch (IOException e) {
logger.debug(() -> "Could not decode refresh token [" + refreshToken + "].", e);
@@ -1259,7 +1190,7 @@ private void innerRefresh(
return;
}
final RefreshTokenStatus refreshTokenStatus = checkRefreshResult.v1();
- final SecurityIndexManager refreshedTokenIndex = getTokensIndexForVersion(refreshTokenStatus.getTransportVersion());
+ final SecurityIndexManager refreshedTokenIndex = securityTokensIndex;
if (refreshTokenStatus.isRefreshed()) {
logger.debug(
"Token document [{}] was recently refreshed, when a new token document was generated. Reusing that result.",
@@ -1273,25 +1204,23 @@ private void innerRefresh(
final Tuple newTokenBytes = getRandomTokenBytes(newTokenVersion, true);
final Map updateMap = new HashMap<>();
updateMap.put("refreshed", true);
- if (newTokenVersion.onOrAfter(VERSION_MULTIPLE_CONCURRENT_REFRESHES)) {
- updateMap.put("refresh_time", clock.instant().toEpochMilli());
- try {
- final byte[] iv = getRandomBytes(IV_BYTES);
- final byte[] salt = getRandomBytes(SALT_BYTES);
- String encryptedAccessAndRefreshToken = encryptSupersedingTokens(
- newTokenBytes.v1(),
- newTokenBytes.v2(),
- refreshToken,
- iv,
- salt
- );
- updateMap.put("superseding.encrypted_tokens", encryptedAccessAndRefreshToken);
- updateMap.put("superseding.encryption_iv", Base64.getEncoder().encodeToString(iv));
- updateMap.put("superseding.encryption_salt", Base64.getEncoder().encodeToString(salt));
- } catch (GeneralSecurityException e) {
- logger.warn("could not encrypt access token and refresh token string", e);
- onFailure.accept(invalidGrantException("could not refresh the requested token"));
- }
+ updateMap.put("refresh_time", clock.instant().toEpochMilli());
+ try {
+ final byte[] iv = getRandomBytes(IV_BYTES);
+ final byte[] salt = getRandomBytes(SALT_BYTES);
+ String encryptedAccessAndRefreshToken = encryptSupersedingTokens(
+ newTokenBytes.v1(),
+ newTokenBytes.v2(),
+ refreshToken,
+ iv,
+ salt
+ );
+ updateMap.put("superseding.encrypted_tokens", encryptedAccessAndRefreshToken);
+ updateMap.put("superseding.encryption_iv", Base64.getEncoder().encodeToString(iv));
+ updateMap.put("superseding.encryption_salt", Base64.getEncoder().encodeToString(salt));
+ } catch (GeneralSecurityException e) {
+ logger.warn("could not encrypt access token and refresh token string", e);
+ onFailure.accept(invalidGrantException("could not refresh the requested token"));
}
assert tokenDoc.seqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO : "expected an assigned sequence number";
assert tokenDoc.primaryTerm() != SequenceNumbers.UNASSIGNED_PRIMARY_TERM : "expected an assigned primary term";
@@ -1703,17 +1632,13 @@ private static Optional checkMultipleRefreshes(
RefreshTokenStatus refreshTokenStatus
) {
if (refreshTokenStatus.isRefreshed()) {
- if (refreshTokenStatus.getTransportVersion().onOrAfter(VERSION_MULTIPLE_CONCURRENT_REFRESHES)) {
- if (refreshRequested.isAfter(refreshTokenStatus.getRefreshInstant().plus(30L, ChronoUnit.SECONDS))) {
- return Optional.of(invalidGrantException("token has already been refreshed more than 30 seconds in the past"));
- }
- if (refreshRequested.isBefore(refreshTokenStatus.getRefreshInstant().minus(30L, ChronoUnit.SECONDS))) {
- return Optional.of(
- invalidGrantException("token has been refreshed more than 30 seconds in the future, clock skew too great")
- );
- }
- } else {
- return Optional.of(invalidGrantException("token has already been refreshed"));
+ if (refreshRequested.isAfter(refreshTokenStatus.getRefreshInstant().plus(30L, ChronoUnit.SECONDS))) {
+ return Optional.of(invalidGrantException("token has already been refreshed more than 30 seconds in the past"));
+ }
+ if (refreshRequested.isBefore(refreshTokenStatus.getRefreshInstant().minus(30L, ChronoUnit.SECONDS))) {
+ return Optional.of(
+ invalidGrantException("token has been refreshed more than 30 seconds in the future, clock skew too great")
+ );
}
}
return Optional.empty();
@@ -2000,11 +1925,7 @@ private void ensureEnabled() {
* consider both the new and the old indices.
*/
private SecurityIndexManager getTokensIndexForVersion(TransportVersion version) {
- if (version.onOrAfter(VERSION_TOKENS_INDEX_INTRODUCED)) {
- return securityTokensIndex;
- } else {
- return securityMainIndex;
- }
+ return securityTokensIndex;
}
public TimeValue getExpirationDelay() {
@@ -2035,41 +1956,13 @@ public String prependVersionAndEncodeAccessToken(TransportVersion version, byte[
out.writeByteArray(accessTokenBytes);
return Base64.getEncoder().encodeToString(out.bytes().toBytesRef().bytes);
}
- } else if (version.onOrAfter(VERSION_ACCESS_TOKENS_AS_UUIDS)) {
+ } else {
try (BytesStreamOutput out = new BytesStreamOutput(MINIMUM_BASE64_BYTES)) {
out.setTransportVersion(version);
TransportVersion.writeVersion(version, out);
out.writeString(Strings.BASE_64_NO_PADDING_URL_ENCODER.encodeToString(accessTokenBytes));
return Base64.getEncoder().encodeToString(out.bytes().toBytesRef().bytes);
}
- } else {
- // we know that the minimum length is larger than the default of the ByteArrayOutputStream so set the size to this explicitly
- try (
- ByteArrayOutputStream os = new ByteArrayOutputStream(LEGACY_MINIMUM_BASE64_BYTES);
- OutputStream base64 = Base64.getEncoder().wrap(os);
- StreamOutput out = new OutputStreamStreamOutput(base64)
- ) {
- out.setTransportVersion(version);
- KeyAndCache keyAndCache = keyCache.activeKeyCache;
- TransportVersion.writeVersion(version, out);
- out.writeByteArray(keyAndCache.getSalt().bytes);
- out.writeByteArray(keyAndCache.getKeyHash().bytes);
- final byte[] initializationVector = getRandomBytes(IV_BYTES);
- out.writeByteArray(initializationVector);
- try (
- CipherOutputStream encryptedOutput = new CipherOutputStream(
- out,
- getEncryptionCipher(initializationVector, keyAndCache, version)
- );
- StreamOutput encryptedStreamOutput = new OutputStreamStreamOutput(encryptedOutput)
- ) {
- encryptedStreamOutput.setTransportVersion(version);
- encryptedStreamOutput.writeString(Strings.BASE_64_NO_PADDING_URL_ENCODER.encodeToString(accessTokenBytes));
- // StreamOutput needs to be closed explicitly because it wraps CipherOutputStream
- encryptedStreamOutput.close();
- return new String(os.toByteArray(), StandardCharsets.UTF_8);
- }
- }
}
}
diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java
index 3a43464765045..d1e610a200e05 100644
--- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java
+++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java
@@ -128,7 +128,6 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
@@ -150,7 +149,6 @@ public class TokenServiceTests extends ESTestCase {
private SecurityIndexManager securityMainIndex;
private SecurityIndexManager securityTokensIndex;
private ClusterService clusterService;
- private DiscoveryNode pre72OldNode;
private DiscoveryNode pre8500040OldNode;
private Settings tokenServiceEnabledSettings = Settings.builder()
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true)
@@ -230,22 +228,12 @@ public void setupClient() {
licenseState = mock(MockLicenseState.class);
when(licenseState.isAllowed(Security.TOKEN_SERVICE_FEATURE)).thenReturn(true);
- if (randomBoolean()) {
- // version 7.2 was an "inflection" point in the Token Service development (access_tokens as UUIDS, multiple concurrent
- // refreshes,
- // tokens docs on a separate index)
- pre72OldNode = addAnother7071DataNode(this.clusterService);
- }
if (randomBoolean()) {
// before refresh tokens used GET, i.e. TokenService#VERSION_GET_TOKEN_DOC_FOR_REFRESH
pre8500040OldNode = addAnotherPre8500DataNode(this.clusterService);
}
}
- private static DiscoveryNode addAnother7071DataNode(ClusterService clusterService) {
- return addAnotherDataNodeWithVersion(clusterService, Version.V_7_1_0, TransportVersions.V_7_1_0);
- }
-
private static DiscoveryNode addAnotherPre8500DataNode(ClusterService clusterService) {
Version version;
TransportVersion transportVersion;
@@ -294,53 +282,6 @@ public static void shutdownThreadpool() {
threadPool = null;
}
- public void testAttachAndGetToken() throws Exception {
- TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- // This test only makes sense in mixed clusters with pre v7.2.0 nodes where the Token Service Key is used (to encrypt tokens)
- if (null == pre72OldNode) {
- pre72OldNode = addAnother7071DataNode(this.clusterService);
- }
- Authentication authentication = AuthenticationTestHelper.builder()
- .user(new User("joe", "admin"))
- .realmRef(new RealmRef("native_realm", "native", "node1"))
- .build(false);
- PlainActionFuture tokenFuture = new PlainActionFuture<>();
- Tuple newTokenBytes = tokenService.getRandomTokenBytes(randomBoolean());
- tokenService.createOAuth2Tokens(
- newTokenBytes.v1(),
- newTokenBytes.v2(),
- authentication,
- authentication,
- Collections.emptyMap(),
- tokenFuture
- );
- final String accessToken = tokenFuture.get().getAccessToken();
- assertNotNull(accessToken);
- mockGetTokenFromAccessTokenBytes(tokenService, newTokenBytes.v1(), authentication, false, null);
-
- ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
- requestContext.putHeader("Authorization", randomFrom("Bearer ", "BEARER ", "bearer ") + accessToken);
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- tokenService.tryAuthenticateToken(bearerToken, future);
- UserToken serialized = future.get();
- assertAuthentication(authentication, serialized.getAuthentication());
- }
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- // verify a second separate token service with its own salt can also verify
- TokenService anotherService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- anotherService.refreshMetadata(tokenService.getTokenMetadata());
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- anotherService.tryAuthenticateToken(bearerToken, future);
- UserToken fromOtherService = future.get();
- assertAuthentication(authentication, fromOtherService.getAuthentication());
- }
- }
-
public void testInvalidAuthorizationHeader() throws Exception {
TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
@@ -357,89 +298,6 @@ public void testInvalidAuthorizationHeader() throws Exception {
}
}
- public void testPassphraseWorks() throws Exception {
- TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- // This test only makes sense in mixed clusters with pre v7.1.0 nodes where the Key is actually used
- if (null == pre72OldNode) {
- pre72OldNode = addAnother7071DataNode(this.clusterService);
- }
- Authentication authentication = AuthenticationTestHelper.builder()
- .user(new User("joe", "admin"))
- .realmRef(new RealmRef("native_realm", "native", "node1"))
- .build(false);
- PlainActionFuture tokenFuture = new PlainActionFuture<>();
- Tuple newTokenBytes = tokenService.getRandomTokenBytes(randomBoolean());
- tokenService.createOAuth2Tokens(
- newTokenBytes.v1(),
- newTokenBytes.v2(),
- authentication,
- authentication,
- Collections.emptyMap(),
- tokenFuture
- );
- final String accessToken = tokenFuture.get().getAccessToken();
- assertNotNull(accessToken);
- mockGetTokenFromAccessTokenBytes(tokenService, newTokenBytes.v1(), authentication, false, null);
-
- ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
- storeTokenHeader(requestContext, accessToken);
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- tokenService.tryAuthenticateToken(bearerToken, future);
- UserToken serialized = future.get();
- assertAuthentication(authentication, serialized.getAuthentication());
- }
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- // verify a second separate token service with its own passphrase cannot verify
- TokenService anotherService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- anotherService.tryAuthenticateToken(bearerToken, future);
- assertNull(future.get());
- }
- }
-
- public void testGetTokenWhenKeyCacheHasExpired() throws Exception {
- TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- // This test only makes sense in mixed clusters with pre v7.1.0 nodes where the Key is actually used
- if (null == pre72OldNode) {
- pre72OldNode = addAnother7071DataNode(this.clusterService);
- }
- Authentication authentication = AuthenticationTestHelper.builder()
- .user(new User("joe", "admin"))
- .realmRef(new RealmRef("native_realm", "native", "node1"))
- .build(false);
-
- PlainActionFuture tokenFuture = new PlainActionFuture<>();
- Tuple newTokenBytes = tokenService.getRandomTokenBytes(randomBoolean());
- tokenService.createOAuth2Tokens(
- newTokenBytes.v1(),
- newTokenBytes.v2(),
- authentication,
- authentication,
- Collections.emptyMap(),
- tokenFuture
- );
- String accessToken = tokenFuture.get().getAccessToken();
- assertThat(accessToken, notNullValue());
-
- tokenService.clearActiveKeyCache();
-
- tokenService.createOAuth2Tokens(
- newTokenBytes.v1(),
- newTokenBytes.v2(),
- authentication,
- authentication,
- Collections.emptyMap(),
- tokenFuture
- );
- accessToken = tokenFuture.get().getAccessToken();
- assertThat(accessToken, notNullValue());
- }
-
public void testAuthnWithInvalidatedToken() throws Exception {
IndexState projectIndex = securityMainIndex.forCurrentProject();
when(projectIndex.indexExists()).thenReturn(true);
@@ -817,57 +675,6 @@ public void testMalformedRefreshTokens() throws Exception {
}
}
- public void testNonExistingPre72Token() throws Exception {
- TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- // mock another random token so that we don't find a token in TokenService#getUserTokenFromId
- Authentication authentication = AuthenticationTestHelper.builder()
- .user(new User("joe", "admin"))
- .realmRef(new RealmRef("native_realm", "native", "node1"))
- .build(false);
- mockGetTokenFromAccessTokenBytes(tokenService, tokenService.getRandomTokenBytes(randomBoolean()).v1(), authentication, false, null);
- ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
- storeTokenHeader(
- requestContext,
- tokenService.prependVersionAndEncodeAccessToken(
- TransportVersions.V_7_1_0,
- tokenService.getRandomTokenBytes(TransportVersions.V_7_1_0, randomBoolean()).v1()
- )
- );
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- tokenService.tryAuthenticateToken(bearerToken, future);
- assertNull(future.get());
- }
- }
-
- public void testNonExistingUUIDToken() throws Exception {
- TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
- // mock another random token so that we don't find a token in TokenService#getUserTokenFromId
- Authentication authentication = AuthenticationTestHelper.builder()
- .user(new User("joe", "admin"))
- .realmRef(new RealmRef("native_realm", "native", "node1"))
- .build(false);
- mockGetTokenFromAccessTokenBytes(tokenService, tokenService.getRandomTokenBytes(randomBoolean()).v1(), authentication, false, null);
- ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
- TransportVersion uuidTokenVersion = randomFrom(TransportVersions.V_7_2_0, TransportVersions.V_7_3_2);
- storeTokenHeader(
- requestContext,
- tokenService.prependVersionAndEncodeAccessToken(
- uuidTokenVersion,
- tokenService.getRandomTokenBytes(uuidTokenVersion, randomBoolean()).v1()
- )
- );
-
- try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
- PlainActionFuture future = new PlainActionFuture<>();
- final SecureString bearerToken = Authenticator.extractBearerTokenFromHeader(requestContext);
- tokenService.tryAuthenticateToken(bearerToken, future);
- assertNull(future.get());
- }
- }
-
public void testNonExistingLatestTokenVersion() throws Exception {
TokenService tokenService = createTokenService(tokenServiceEnabledSettings, systemUTC());
// mock another random token so that we don't find a token in TokenService#getUserTokenFromId
@@ -922,20 +729,11 @@ public void testIndexNotAvailable() throws Exception {
return Void.TYPE;
}).when(client).get(any(GetRequest.class), anyActionListener());
- final SecurityIndexManager tokensIndex;
- if (pre72OldNode != null) {
- tokensIndex = securityMainIndex;
- final IndexState projectTokenIndex = securityTokensIndex.forCurrentProject();
- when(projectTokenIndex.isAvailable(SecurityIndexManager.Availability.PRIMARY_SHARDS)).thenReturn(false);
- when(projectTokenIndex.indexExists()).thenReturn(false);
- } else {
- tokensIndex = securityTokensIndex;
- final IndexState projectMainIndex = securityMainIndex.forCurrentProject();
- when(projectMainIndex.isAvailable(SecurityIndexManager.Availability.PRIMARY_SHARDS)).thenReturn(false);
- when(projectMainIndex.indexExists()).thenReturn(false);
- }
+ final IndexState projectMainIndex = securityMainIndex.forCurrentProject();
+ when(projectMainIndex.isAvailable(SecurityIndexManager.Availability.PRIMARY_SHARDS)).thenReturn(false);
+ when(projectMainIndex.indexExists()).thenReturn(false);
- IndexState projectIndex = tokensIndex.forCurrentProject();
+ IndexState projectIndex = securityTokensIndex.forCurrentProject();
try (ThreadContext.StoredContext ignore = requestContext.newStoredContextPreservingResponseHeaders()) {
PlainActionFuture future = new PlainActionFuture<>();
final SecureString bearerToken3 = Authenticator.extractBearerTokenFromHeader(requestContext);
@@ -987,7 +785,6 @@ public void testGetAuthenticationWorksWithExpiredUserToken() throws Exception {
}
public void testSupersedingTokenEncryption() throws Exception {
- assumeTrue("Superseding tokens are only created in post 7.2 clusters", pre72OldNode == null);
TokenService tokenService = createTokenService(tokenServiceEnabledSettings, Clock.systemUTC());
Authentication authentication = AuthenticationTests.randomAuthentication(null, null);
PlainActionFuture tokenFuture = new PlainActionFuture<>();
@@ -1022,13 +819,11 @@ public void testSupersedingTokenEncryption() throws Exception {
authentication,
tokenFuture
);
- if (version.onOrAfter(TokenService.VERSION_ACCESS_TOKENS_AS_UUIDS)) {
- // previous versions serialized the access token encrypted and the cipher text was different each time (due to different IVs)
- assertThat(
- tokenService.prependVersionAndEncodeAccessToken(version, newTokenBytes.v1()),
- equalTo(tokenFuture.get().getAccessToken())
- );
- }
+ // previous versions serialized the access token encrypted and the cipher text was different each time (due to different IVs)
+ assertThat(
+ tokenService.prependVersionAndEncodeAccessToken(version, newTokenBytes.v1()),
+ equalTo(tokenFuture.get().getAccessToken())
+ );
assertThat(
TokenService.prependVersionAndEncodeRefreshToken(version, newTokenBytes.v2()),
equalTo(tokenFuture.get().getRefreshToken())
@@ -1157,10 +952,8 @@ public static String tokenDocIdFromAccessTokenBytes(byte[] accessTokenBytes, Tra
MessageDigest userTokenIdDigest = sha256();
userTokenIdDigest.update(accessTokenBytes, RAW_TOKEN_BYTES_LENGTH, RAW_TOKEN_DOC_ID_BYTES_LENGTH);
return Base64.getUrlEncoder().withoutPadding().encodeToString(userTokenIdDigest.digest());
- } else if (tokenVersion.onOrAfter(TokenService.VERSION_ACCESS_TOKENS_AS_UUIDS)) {
- return TokenService.hashTokenString(Base64.getUrlEncoder().withoutPadding().encodeToString(accessTokenBytes));
} else {
- return Base64.getUrlEncoder().withoutPadding().encodeToString(accessTokenBytes);
+ return TokenService.hashTokenString(Base64.getUrlEncoder().withoutPadding().encodeToString(accessTokenBytes));
}
}
@@ -1177,12 +970,9 @@ private void mockTokenForRefreshToken(
if (userToken.getTransportVersion().onOrAfter(VERSION_GET_TOKEN_DOC_FOR_REFRESH)) {
storedAccessToken = Base64.getUrlEncoder().withoutPadding().encodeToString(sha256().digest(accessTokenBytes));
storedRefreshToken = Base64.getUrlEncoder().withoutPadding().encodeToString(sha256().digest(refreshTokenBytes));
- } else if (userToken.getTransportVersion().onOrAfter(TokenService.VERSION_HASHED_TOKENS)) {
- storedAccessToken = null;
- storedRefreshToken = TokenService.hashTokenString(Base64.getUrlEncoder().withoutPadding().encodeToString(refreshTokenBytes));
} else {
storedAccessToken = null;
- storedRefreshToken = Base64.getUrlEncoder().withoutPadding().encodeToString(refreshTokenBytes);
+ storedRefreshToken = TokenService.hashTokenString(Base64.getUrlEncoder().withoutPadding().encodeToString(refreshTokenBytes));
}
final RealmRef realmRef = new RealmRef(
refreshTokenStatus == null ? randomAlphaOfLength(6) : refreshTokenStatus.getAssociatedRealm(),