|
65 | 65 | import static org.hamcrest.Matchers.empty; |
66 | 66 | import static org.hamcrest.Matchers.equalTo; |
67 | 67 | import static org.hamcrest.Matchers.hasItem; |
| 68 | +import static org.hamcrest.Matchers.hasSize; |
68 | 69 |
|
69 | 70 | public class TokenAuthIntegTests extends SecurityIntegTestCase { |
70 | 71 |
|
@@ -217,22 +218,71 @@ public void testExpiredTokensDeletedAfterExpiration() throws Exception { |
217 | 218 |
|
218 | 219 | // Now the documents are deleted, try to invalidate the access token and refresh token again |
219 | 220 | InvalidateTokenResponse invalidateAccessTokenResponse = securityClient.prepareInvalidateToken(accessToken) |
220 | | - .setType(randomFrom(InvalidateTokenRequest.Type.values())) |
| 221 | + .setType(InvalidateTokenRequest.Type.ACCESS_TOKEN) |
221 | 222 | .execute() |
222 | 223 | .actionGet(); |
223 | 224 | assertThat(invalidateAccessTokenResponse.getResult().getInvalidatedTokens(), empty()); |
224 | 225 | assertThat(invalidateAccessTokenResponse.getResult().getPreviouslyInvalidatedTokens(), empty()); |
225 | 226 | assertThat(invalidateAccessTokenResponse.getResult().getErrors(), empty()); |
| 227 | + |
| 228 | + // Weird testing behaviour ahead... |
| 229 | + // invalidating by access token (above) is a Get, but invalidating by refresh token (below) is a Search |
| 230 | + // In a multi node cluster, in a small % of cases, the search might find a document that has been deleted but not yet refreshed |
| 231 | + // in that node's shard. |
| 232 | + // Our assertion, therefore, is that an attempt to invalidate the refresh token must not actually invalidate |
| 233 | + // anything (concurrency controls must prevent that), nor may return any errors, |
| 234 | + // but it might _temporarily_ find an "already deleted" token. |
226 | 235 | InvalidateTokenResponse invalidateRefreshTokenResponse = securityClient.prepareInvalidateToken(refreshToken) |
227 | 236 | .setType(InvalidateTokenRequest.Type.REFRESH_TOKEN) |
228 | 237 | .execute() |
229 | 238 | .actionGet(); |
230 | 239 | assertThat(invalidateRefreshTokenResponse.getResult().getInvalidatedTokens(), empty()); |
231 | 240 | assertThat(invalidateRefreshTokenResponse.getResult().getPreviouslyInvalidatedTokens(), empty()); |
232 | | - assertThat(invalidateRefreshTokenResponse.getResult().getErrors(), empty()); |
| 241 | + |
| 242 | + // 99% of the time, this will already be empty, but if not ensure it goes to empty within the allowed timeframe |
| 243 | + if (false == invalidateRefreshTokenResponse.getResult().getErrors().isEmpty()) { |
| 244 | + assertBusy(() -> { |
| 245 | + InvalidateTokenResponse newResponse = securityClient.prepareInvalidateToken(refreshToken) |
| 246 | + .setType(InvalidateTokenRequest.Type.REFRESH_TOKEN) |
| 247 | + .execute() |
| 248 | + .actionGet(); |
| 249 | + assertThat(newResponse.getResult().getErrors(), empty()); |
| 250 | + }); |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + public void testAccessTokenAndRefreshTokenCanBeInvalidatedIndependently() { |
| 255 | + CreateTokenResponse response = securityClient().prepareCreateToken().setGrantType("password") |
| 256 | + .setUsername(SecuritySettingsSource.TEST_USER_NAME) |
| 257 | + .setPassword(new SecureString(SecuritySettingsSourceField.TEST_PASSWORD.toCharArray())) |
| 258 | + .get(); |
| 259 | + final InvalidateTokenResponse response1, response2; |
| 260 | + if (randomBoolean()) { |
| 261 | + response1 = securityClient().prepareInvalidateToken(response.getTokenString()) |
| 262 | + .setType(InvalidateTokenRequest.Type.ACCESS_TOKEN) |
| 263 | + .execute().actionGet(); |
| 264 | + response2 = securityClient().prepareInvalidateToken(response.getRefreshToken()) |
| 265 | + .setType(InvalidateTokenRequest.Type.REFRESH_TOKEN) |
| 266 | + .execute().actionGet(); |
| 267 | + } else { |
| 268 | + response1 = securityClient().prepareInvalidateToken(response.getRefreshToken()) |
| 269 | + .setType(InvalidateTokenRequest.Type.REFRESH_TOKEN) |
| 270 | + .execute().actionGet(); |
| 271 | + response2 = securityClient().prepareInvalidateToken(response.getTokenString()) |
| 272 | + .setType(InvalidateTokenRequest.Type.ACCESS_TOKEN) |
| 273 | + .execute().actionGet(); |
| 274 | + } |
| 275 | + |
| 276 | + assertThat(response1.getResult().getInvalidatedTokens(), hasSize(1)); |
| 277 | + assertThat(response1.getResult().getPreviouslyInvalidatedTokens(), empty()); |
| 278 | + assertThat(response1.getResult().getErrors(), empty()); |
| 279 | + |
| 280 | + assertThat(response2.getResult().getInvalidatedTokens(), hasSize(1)); |
| 281 | + assertThat(response2.getResult().getPreviouslyInvalidatedTokens(), empty()); |
| 282 | + assertThat(response2.getResult().getErrors(), empty()); |
233 | 283 | } |
234 | 284 |
|
235 | | - public void testInvalidateAllTokensForUser() throws Exception{ |
| 285 | + public void testInvalidateAllTokensForUser() throws Exception { |
236 | 286 | final int numOfRequests = randomIntBetween(5, 10); |
237 | 287 | for (int i = 0; i < numOfRequests; i++) { |
238 | 288 | securityClient().prepareCreateToken() |
|
0 commit comments