mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
Token management (#1201)
* http-rest-starter w/ Stargate data gateway * Token management w/ Token() binding function * Expansion of Token usage to scenarios * Version requirements updated for http-rest scenarios
This commit is contained in:
@@ -35,60 +35,75 @@ import java.util.function.Function;
|
||||
|
||||
@ThreadSafeMapper
|
||||
@Categories({Category.general})
|
||||
public class StargateToken implements Function<Object, String> {
|
||||
public class Token implements Function<String, String> {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(StargateToken.class);
|
||||
private final Credentials credentials;
|
||||
private final String url;
|
||||
private final HttpClient httpClient;
|
||||
private static final Logger logger = LogManager.getLogger(Token.class);
|
||||
private Credentials credentials;
|
||||
private URI uri;
|
||||
private String providedToken;
|
||||
|
||||
|
||||
public StargateToken(String url) throws SecurityException {
|
||||
this(url, Credentials.defaultCredentials());
|
||||
}
|
||||
public Token(String token, String uri, String uid, String password) {
|
||||
|
||||
public StargateToken(String url, Credentials credentials) throws SecurityException {
|
||||
this(url, credentials, HttpClient.newBuilder().build());
|
||||
}
|
||||
if (token != null && !token.trim().isEmpty()) {
|
||||
this.providedToken = token.trim();
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri == null || uri.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Expected uri to be specified for obtaining Token.");
|
||||
}
|
||||
this.uri = URI.create(uri.trim());
|
||||
|
||||
if (uid == null || uid.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Expected uid to be specified for obtaining Token.");
|
||||
}
|
||||
|
||||
if (password == null || password.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Expected password to be specified for obtaining Token.");
|
||||
}
|
||||
this.credentials = Credentials.create(uid.trim(), password.trim());
|
||||
|
||||
public StargateToken(String url, Credentials credentials, HttpClient client) throws SecurityException {
|
||||
this.url = url;
|
||||
this.credentials = credentials;
|
||||
this.httpClient = client;
|
||||
if (TokenKeeper.isExpired()) {
|
||||
authTokenStargate(url, credentials);
|
||||
authTokenStargate(this.uri, this.credentials);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(Object value) throws SecurityException {
|
||||
if (TokenKeeper.isExpired()) {
|
||||
authTokenStargate(url, credentials);
|
||||
public String apply(String p1) {
|
||||
|
||||
if (this.providedToken != null) {
|
||||
return this.providedToken;
|
||||
}
|
||||
return TokenKeeper.token;
|
||||
|
||||
if (TokenKeeper.isExpired() || TokenKeeper.token == null || TokenKeeper.token.isEmpty()) {
|
||||
authTokenStargate(this.uri, this.credentials);
|
||||
}
|
||||
return TokenKeeper.getToken();
|
||||
}
|
||||
|
||||
public static void setExpired() {
|
||||
TokenKeeper.isExpiredRequested = true;
|
||||
}
|
||||
|
||||
private void authTokenStargate(String url, Credentials credentials) throws SecurityException {
|
||||
private static void authTokenStargate(URI uri, Credentials credentials) throws SecurityException {
|
||||
|
||||
if (credentials == null || url == null) {
|
||||
if (credentials == null || uri == null) {
|
||||
throw new BasicError("Must provide url and credentials to obtain authTokenStargate");
|
||||
}
|
||||
logger.debug("Received url for Stargate auth token request: {} ", url);
|
||||
|
||||
logger.debug(() -> "Received uri for auth token request: " + uri);
|
||||
|
||||
try {
|
||||
final Gson gson = new Gson();
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder();
|
||||
builder = builder.uri(URI.create(url));
|
||||
builder = builder.uri(uri);
|
||||
builder = builder.POST(HttpRequest.BodyPublishers.ofString(gson.toJson(credentials)));
|
||||
builder.setHeader("Content-Type", "application/json");
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpClient httpClient = HttpClient.newBuilder().build();
|
||||
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
logger.debug(() -> "Stargate response status code: " + resp.statusCode());
|
||||
|
||||
if (resp.statusCode() != 201) {
|
||||
@@ -98,8 +113,8 @@ public class StargateToken implements Function<Object, String> {
|
||||
throw new BasicError(errorMessage);
|
||||
}
|
||||
|
||||
Credentials retrievedToken = gson.fromJson(resp.body(), Credentials.class);
|
||||
TokenKeeper.setToken(retrievedToken.getAuthToken());
|
||||
final Credentials cred = gson.fromJson(resp.body(), Credentials.class);
|
||||
TokenKeeper.setToken(cred.getAuthToken());
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException("Auth Token error, stargate-token retrieval failure", e);
|
||||
@@ -121,28 +136,31 @@ public class StargateToken implements Function<Object, String> {
|
||||
lastTokenInstant = Instant.now();
|
||||
}
|
||||
|
||||
public static void setToken(String input) {
|
||||
token = input;
|
||||
}
|
||||
|
||||
public static String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public static Instant lastTokenInstant() {
|
||||
return lastTokenInstant;
|
||||
}
|
||||
|
||||
public static boolean isExpired() {
|
||||
|
||||
if (isExpiredRequested || Duration.between(Instant.now(),
|
||||
if (token == null || isExpiredRequested || Duration.between(Instant.now(),
|
||||
lastTokenInstant).toMinutes() > TOKEN_EXPIRE_MIN) {
|
||||
logger.trace("Token expiry detected.");
|
||||
logger.debug("Token expiry detected.");
|
||||
lastTokenInstant = Instant.now();
|
||||
isExpiredRequested = false;
|
||||
token = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.debug(() -> "Token not expired, reusing as: " + token);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static synchronized void setToken(String value) {
|
||||
token = value;
|
||||
}
|
||||
|
||||
public static synchronized String getToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,23 +18,17 @@ package io.nosqlbench.virtdata.library.basics.shared.util;
|
||||
|
||||
public class Credentials {
|
||||
|
||||
private static final String DEFAULT_IDENTITY = "cassandra";
|
||||
private String username;
|
||||
private String password;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private String authToken;
|
||||
|
||||
public static Credentials defaultCredentials() {
|
||||
return new Credentials(DEFAULT_IDENTITY, DEFAULT_IDENTITY);
|
||||
}
|
||||
|
||||
public Credentials(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
// Added for support of auth tokens not obtained via defaultCredentials
|
||||
public Credentials(String authToken) {
|
||||
this.authToken = authToken;
|
||||
public static Credentials create(String username, String password) {
|
||||
return new Credentials(username, password);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
@@ -48,4 +42,5 @@ public class Credentials {
|
||||
public String getAuthToken() {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.virtdata.library.basics.shared.unary_string;
|
||||
|
||||
import io.nosqlbench.virtdata.library.basics.shared.util.Credentials;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.util.reflection.FieldReader;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class StargateTokenTest extends TokenTest {
|
||||
|
||||
private static final String TEST_AUTH_TOKEN = "8675309";
|
||||
private static final String VALID_TEST_URL = "http://foobar.com:8675";
|
||||
private static final String VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON =
|
||||
"{ 'authToken': " + "\"" + TEST_AUTH_TOKEN + "\"" + "}";
|
||||
private static final Credentials VALID_TEST_CREDS = new Credentials("username", "password");
|
||||
|
||||
private static final Object TOKEN_APPLY_PLACEHOLDER = new Object();
|
||||
@Mock
|
||||
private static HttpResponse<String> httpResponse;
|
||||
@Mock
|
||||
private static HttpClient httpClient;
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
httpResponse = mock(HttpResponse.class);
|
||||
httpClient = mock(HttpClient.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
StargateToken.TokenKeeper.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTokenSuccess() throws Exception {
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
when(httpClient.send(Mockito.any(HttpRequest.class),
|
||||
Mockito.any(HttpResponse.BodyHandlers.ofString().getClass())))
|
||||
.thenReturn(httpResponse);
|
||||
|
||||
final StargateToken stargateToken = new StargateToken(VALID_TEST_URL,
|
||||
VALID_TEST_CREDS, httpClient);
|
||||
final String result = stargateToken.apply(TOKEN_APPLY_PLACEHOLDER);
|
||||
|
||||
assertThat(result).isEqualTo(TEST_AUTH_TOKEN);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void receivedResponse500() throws Exception {
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(500);
|
||||
when(httpClient.send(Mockito.any(HttpRequest.class), Mockito.any(HttpResponse.BodyHandlers.ofString()
|
||||
.getClass())))
|
||||
.thenReturn(httpResponse);
|
||||
|
||||
assertThatExceptionOfType(SecurityException.class).isThrownBy(() -> new StargateToken(VALID_TEST_URL,
|
||||
VALID_TEST_CREDS, httpClient));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTokenSuccessWithRefreshTokenRequested() throws Exception {
|
||||
|
||||
// --- Initial check
|
||||
StargateToken.TokenKeeper.reset();
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
when(httpClient.send(Mockito.any(HttpRequest.class),
|
||||
Mockito.any(HttpResponse.BodyHandlers.ofString()
|
||||
.getClass())))
|
||||
.thenReturn(httpResponse);
|
||||
|
||||
final StargateToken stargateToken = new StargateToken(VALID_TEST_URL, VALID_TEST_CREDS, httpClient);
|
||||
final String resultFirstCheck = stargateToken.apply(TOKEN_APPLY_PLACEHOLDER);
|
||||
final Instant tokenInstantFirstCheck = StargateToken.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(resultFirstCheck).isEqualTo(TEST_AUTH_TOKEN);
|
||||
assertThat(tokenInstantFirstCheck).isNotNull();
|
||||
|
||||
// --- Subtest 2 - When NOT having an expired token, expect that the lastTokenInstant does NOT change.
|
||||
when(httpResponse.body()).thenReturn("{ 'authToken': " + "\"" + "refreshed-token" + "\"" + "}");
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
when(httpClient.send(Mockito.any(HttpRequest.class), Mockito.any(HttpResponse.BodyHandlers.ofString()
|
||||
.getClass())))
|
||||
.thenReturn(httpResponse);
|
||||
|
||||
final String resultSecondCheck = stargateToken.apply(TOKEN_APPLY_PLACEHOLDER);
|
||||
final Instant tokenInstantSecondCheck = StargateToken.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(resultSecondCheck).isEqualTo(resultFirstCheck);
|
||||
assertThat(tokenInstantSecondCheck).isEqualTo(tokenInstantFirstCheck);
|
||||
|
||||
// --- Subtest 3 - When having an expired token, expect that the lastTokenInstant changes and
|
||||
// tokens are different.
|
||||
// Note: Explicit token expiry, default is 30 minutes
|
||||
StargateToken.setExpired();
|
||||
|
||||
final String resultThirdCheck = stargateToken.apply(TOKEN_APPLY_PLACEHOLDER);
|
||||
final FieldReader fileReaderLastCheck = new FieldReader(stargateToken,
|
||||
FieldUtils.getDeclaredField(StargateToken.class,
|
||||
"lastTokenInstant", true));
|
||||
final Instant tokenInstantThirdCheck = StargateToken.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(tokenInstantThirdCheck.isAfter(tokenInstantFirstCheck)).isTrue();
|
||||
assertThat(resultSecondCheck).isNotEqualTo(resultThirdCheck);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,8 +13,163 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.virtdata.library.basics.shared.unary_string;
|
||||
|
||||
public abstract class TokenTest {
|
||||
// Intent is to expand for generic (non-Stargate) http-rest test conditions and utils.
|
||||
}
|
||||
import io.nosqlbench.virtdata.library.basics.shared.util.Credentials;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.util.reflection.FieldReader;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class TokenTest {
|
||||
|
||||
private static final String TEST_AUTH_TOKEN = "8675309";
|
||||
private static final String VALID_TEST_URL = "http://foobar.com:8675";
|
||||
private static final String VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON =
|
||||
"{ 'authToken': " + "\"" + TEST_AUTH_TOKEN + "\"" + "}";
|
||||
private static final Credentials VALID_TEST_CREDS = new Credentials("username", "password");
|
||||
|
||||
@Mock
|
||||
private static HttpResponse<String> httpResponse;
|
||||
@Mock
|
||||
private static HttpClient httpClient;
|
||||
|
||||
private static MockedStatic<HttpClient> HttpCli;
|
||||
|
||||
@Mock
|
||||
private static HttpClient.Builder httpBuilder;
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
httpResponse = mock(HttpResponse.class);
|
||||
httpClient = mock(HttpClient.class);
|
||||
httpBuilder = mock(HttpClient.Builder.class);
|
||||
HttpCli = Mockito.mockStatic(HttpClient.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Token.TokenKeeper.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTokenSuccess() throws Exception {
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
|
||||
mockResponse();
|
||||
|
||||
final Token token = new Token(null, VALID_TEST_URL, VALID_TEST_CREDS.getUsername(),
|
||||
VALID_TEST_CREDS.getPassword());
|
||||
// Since constructor handles state management, the inputs aren't utilized in the apply function.
|
||||
final String result = token.apply("p1");
|
||||
|
||||
assertThat(result).isEqualTo(TEST_AUTH_TOKEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void receivedResponse500() throws Exception {
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(500);
|
||||
mockResponse();
|
||||
|
||||
assertThatExceptionOfType(SecurityException.class).isThrownBy(() -> new Token(null, VALID_TEST_URL,
|
||||
VALID_TEST_CREDS.getUsername(), VALID_TEST_CREDS.getPassword()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyTokenSuccessWithRefreshTokenRequested() throws Exception {
|
||||
|
||||
Token.TokenKeeper.reset();
|
||||
|
||||
when(httpResponse.body()).thenReturn(VALID_STARGATE_AUTH_TOKEN_RESPONSE_JSON);
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
mockResponse();
|
||||
|
||||
final Token token = new Token(null, VALID_TEST_URL, VALID_TEST_CREDS.getUsername(),
|
||||
VALID_TEST_CREDS.getPassword());
|
||||
final String resultFirstCheck = token.apply("p1");
|
||||
final Instant tokenInstantFirstCheck = Token.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(resultFirstCheck).isEqualTo(TEST_AUTH_TOKEN);
|
||||
assertThat(tokenInstantFirstCheck).isNotNull();
|
||||
|
||||
// --- Subtest 2 - NOT having an expired token, expect that the lastTokenInstant does NOT change.
|
||||
when(httpResponse.body()).thenReturn("{ 'authToken': " + "\"" + "refreshed-token" + "\"" + "}");
|
||||
when(httpResponse.statusCode()).thenReturn(201);
|
||||
mockResponse();
|
||||
|
||||
final String resultSecondCheck = token.apply("p1");
|
||||
final Instant tokenInstantSecondCheck = Token.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(resultSecondCheck).isEqualTo(resultFirstCheck);
|
||||
assertThat(tokenInstantSecondCheck).isEqualTo(tokenInstantFirstCheck);
|
||||
|
||||
// --- Subtest 3 - Having expired token, expect that the lastTokenInstant changes and
|
||||
// tokens are different.
|
||||
// Note: Explicit token expiry, default is 30m
|
||||
Token.setExpired();
|
||||
|
||||
final String resultThirdCheck = token.apply("p1");
|
||||
final FieldReader fileReaderLastCheck = new FieldReader(token,
|
||||
FieldUtils.getDeclaredField(Token.class,
|
||||
"lastTokenInstant", true));
|
||||
final Instant tokenInstantThirdCheck = Token.TokenKeeper.lastTokenInstant();
|
||||
|
||||
assertThat(tokenInstantThirdCheck.isAfter(tokenInstantFirstCheck)).isTrue();
|
||||
assertThat(resultSecondCheck).isNotEqualTo(resultThirdCheck);
|
||||
}
|
||||
|
||||
@Test
|
||||
void provideToken() {
|
||||
|
||||
final Token token = new Token(TEST_AUTH_TOKEN, VALID_TEST_URL, VALID_TEST_CREDS.getUsername(),
|
||||
VALID_TEST_CREDS.getPassword());
|
||||
|
||||
final String result = token.apply("p1");
|
||||
|
||||
assertThat(result).isEqualTo(TEST_AUTH_TOKEN);
|
||||
|
||||
|
||||
final Token token2 = new Token(TEST_AUTH_TOKEN, null, null, null);
|
||||
final String result2 = token2.apply("p1");
|
||||
|
||||
assertThat(result2).isEqualTo(TEST_AUTH_TOKEN);
|
||||
}
|
||||
|
||||
|
||||
private void mockResponse() throws Exception {
|
||||
|
||||
HttpCli.when(HttpClient::newBuilder).thenReturn(httpBuilder);
|
||||
when(httpBuilder.build()).thenReturn(httpClient);
|
||||
|
||||
when(httpClient.send(Mockito.any(HttpRequest.class),
|
||||
Mockito.any(HttpResponse.BodyHandlers.ofString().getClass())))
|
||||
.thenReturn(httpResponse);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user