Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.elasticsearch.action.support.TransportActions.isShardNotAvailableException;
import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK;
import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME;
Expand Down Expand Up @@ -658,9 +660,16 @@ public void invalidateRefreshToken(String refreshToken, ActionListener<TokensInv
} else if (searchHits.getHits().length > 1) {
listener.onFailure(new IllegalStateException("multiple tokens share the same refresh token"));
} else {
final Tuple<UserToken, String> parsedTokens =
parseTokensFromDocument(searchHits.getAt(0).getSourceAsMap(), null);
indexInvalidation(Collections.singletonList(parsedTokens.v1()), backoff, "refresh_token", null, listener);
final Tuple<UserToken, RefreshTokenStatus> parsedTokens =
parseTokenAndRefreshStatus(searchHits.getAt(0).getSourceAsMap());
final UserToken userToken = parsedTokens.v1();
final RefreshTokenStatus refresh = parsedTokens.v2();
if (refresh.isInvalidated()) {
listener.onResponse(
new TokensInvalidationResult(emptyList(), singletonList(userToken.getId()), null, RestStatus.OK));
} else {
indexInvalidation(singletonList(userToken), backoff, "refresh_token", null, listener);
}
}
}, e -> {
if (e instanceof IndexNotFoundException || e instanceof IndexClosedException) {
Expand Down Expand Up @@ -1259,9 +1268,7 @@ private static Optional<ElasticsearchSecurityException> checkTokenDocumentExpire
*/
private static Tuple<RefreshTokenStatus, Optional<ElasticsearchSecurityException>> checkTokenDocumentForRefresh(
Instant refreshRequested, Authentication clientAuth, Map<String, Object> source) throws IllegalStateException, DateTimeException {
final RefreshTokenStatus refreshTokenStatus = RefreshTokenStatus.fromSourceMap(getRefreshTokenSourceMap(source));
final UserToken userToken = UserToken.fromSourceMap(getUserTokenSourceMap(source));
refreshTokenStatus.setVersion(userToken.getVersion());
final RefreshTokenStatus refreshTokenStatus = parseTokenAndRefreshStatus(source).v2();
final ElasticsearchSecurityException validationException = checkTokenDocumentExpired(refreshRequested, source).orElseGet(() -> {
if (refreshTokenStatus.isInvalidated()) {
return invalidGrantException("token has been invalidated");
Expand All @@ -1273,6 +1280,13 @@ private static Tuple<RefreshTokenStatus, Optional<ElasticsearchSecurityException
return new Tuple<>(refreshTokenStatus, Optional.ofNullable(validationException));
}

private static Tuple<UserToken, RefreshTokenStatus> parseTokenAndRefreshStatus(Map<String, Object> source) {
final RefreshTokenStatus refreshTokenStatus = RefreshTokenStatus.fromSourceMap(getRefreshTokenSourceMap(source));
final UserToken userToken = UserToken.fromSourceMap(getUserTokenSourceMap(source));
refreshTokenStatus.setVersion(userToken.getVersion());
return new Tuple<>(userToken, refreshTokenStatus);
}

/**
* Refresh tokens are bound to be used only by the client that originally created them. This check validates this condition, given the
* {@code Authentication} of the client that attempted the refresh operation.
Expand Down Expand Up @@ -1355,7 +1369,7 @@ public void findActiveTokensForRealm(String realmName, @Nullable Predicate<Map<S
}
sourceIndicesWithTokensAndRun(ActionListener.wrap(indicesWithTokens -> {
if (indicesWithTokens.isEmpty()) {
listener.onResponse(Collections.emptyList());
listener.onResponse(emptyList());
} else {
final Instant now = clock.instant();
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
Expand Down Expand Up @@ -1403,7 +1417,7 @@ public void findActiveTokensForUser(String username, ActionListener<Collection<T
}
sourceIndicesWithTokensAndRun(ActionListener.wrap(indicesWithTokens -> {
if (indicesWithTokens.isEmpty()) {
listener.onResponse(Collections.emptyList());
listener.onResponse(emptyList());
} else {
final Instant now = clock.instant();
final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
Expand Down Expand Up @@ -1485,12 +1499,18 @@ private void sourceIndicesWithTokensAndRun(ActionListener<List<String>> listener

private BytesReference createTokenDocument(UserToken userToken, @Nullable String refreshToken,
@Nullable Authentication originatingClientAuth) {
final Instant creationTime = getCreationTime(userToken.getExpirationTime());
return createTokenDocument(userToken, refreshToken, originatingClientAuth, creationTime);
}

static BytesReference createTokenDocument(UserToken userToken, String refreshToken, Authentication originatingClientAuth,
Instant creationTime) {
assert refreshToken == null || originatingClientAuth != null : "non-null refresh token " + refreshToken
+ " requires non-null client authn " + originatingClientAuth;
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
builder.startObject();
builder.field("doc_type", TOKEN_DOC_TYPE);
builder.field("creation_time", getCreationTime(userToken.getExpirationTime()).toEpochMilli());
builder.field("creation_time", creationTime.toEpochMilli());
if (refreshToken != null) {
builder.startObject("refresh_token")
.field("token", refreshToken)
Expand Down Expand Up @@ -1999,7 +2019,7 @@ synchronized TokenMetadata generateSpareKey() {
continue; // collision -- generate a new key
}
return newTokenMetadata(keyCache.currentTokenKeyHash, Iterables.concat(keyCache.cache.values(),
Collections.singletonList(keyAndCache)));
singletonList(keyAndCache)));
}
}
return newTokenMetadata(keyCache.currentTokenKeyHash, keyCache.cache.values());
Expand Down
Loading