From b053a2ae245e199ac4a33510bd5d515c4a85436a Mon Sep 17 00:00:00 2001 From: David An Date: Wed, 18 Oct 2023 13:27:09 -0400 Subject: [PATCH] BearerToken model (#373) --- .../activitylog/ActivityEventBuilder.java | 7 ++- .../datarepo/HttpDataRepoClientFactory.java | 7 ++- .../leonardo/HttpLeonardoClientFactory.java | 7 ++- .../sam/HttpSamClientFactory.java | 8 +-- .../sam/TokenContextUtil.java | 58 +++++++++++-------- .../service/ImportService.java | 7 ++- .../shared/model/BearerToken.java | 52 +++++++++++++++++ ...HttpWorkspaceDataServiceClientFactory.java | 7 ++- .../HttpWorkspaceManagerClientFactory.java | 7 ++- .../jobexec/QuartzJobTest.java | 10 ++-- .../sam/TokenContextUtilTest.java | 31 +++++----- .../shared/model/BearerTokenTest.java | 42 ++++++++++++++ 12 files changed, 180 insertions(+), 63 deletions(-) create mode 100644 service/src/main/java/org/databiosphere/workspacedataservice/shared/model/BearerToken.java create mode 100644 service/src/test/java/org/databiosphere/workspacedataservice/shared/model/BearerTokenTest.java diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/activitylog/ActivityEventBuilder.java b/service/src/main/java/org/databiosphere/workspacedataservice/activitylog/ActivityEventBuilder.java index 9bae52050..bdda54ff4 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/activitylog/ActivityEventBuilder.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/activitylog/ActivityEventBuilder.java @@ -3,6 +3,7 @@ import java.util.UUID; import org.databiosphere.workspacedataservice.sam.SamDao; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.databiosphere.workspacedataservice.shared.model.RecordType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,10 +37,10 @@ public ActivityEventBuilder(SamDao samDao) { public ActivityEventBuilder currentUser() { try { // grab the current user's bearer token (see BearerTokenFilter) - String token = TokenContextUtil.getToken(); - if (token != null) { + BearerToken token = TokenContextUtil.getToken(); + if (token.nonEmpty()) { // resolve the token to a user id via Sam - this.subject = samDao.getUserId(token); + this.subject = samDao.getUserId(token.getValue()); } else { this.subject = "anonymous"; } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/datarepo/HttpDataRepoClientFactory.java b/service/src/main/java/org/databiosphere/workspacedataservice/datarepo/HttpDataRepoClientFactory.java index 776fe3fee..cd788fd44 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/datarepo/HttpDataRepoClientFactory.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/datarepo/HttpDataRepoClientFactory.java @@ -5,6 +5,7 @@ import javax.ws.rs.client.Client; import org.apache.commons.lang3.StringUtils; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,11 +30,11 @@ private ApiClient getApiClient() { apiClient.setBasePath(dataRepoUrl); } // grab the current user's bearer token (see BearerTokenFilter) - String token = TokenContextUtil.getToken(); + BearerToken token = TokenContextUtil.getToken(); // add the user's bearer token to the client - if (token != null) { + if (token.nonEmpty()) { LOGGER.debug("setting access token for data repo request"); - apiClient.setAccessToken(token); + apiClient.setAccessToken(token.getValue()); } else { LOGGER.warn("No access token found for data repo request."); } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/leonardo/HttpLeonardoClientFactory.java b/service/src/main/java/org/databiosphere/workspacedataservice/leonardo/HttpLeonardoClientFactory.java index fbe64c85b..32027ac53 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/leonardo/HttpLeonardoClientFactory.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/leonardo/HttpLeonardoClientFactory.java @@ -7,6 +7,7 @@ import org.broadinstitute.dsde.workbench.client.leonardo.ApiClient; import org.broadinstitute.dsde.workbench.client.leonardo.api.AppsApi; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,11 +36,11 @@ private ApiClient getApiClient(String authToken) { } // grab the current user's bearer token (see BearerTokenFilter) or use parameter value - String token = TokenContextUtil.getToken(authToken); + BearerToken token = TokenContextUtil.getToken(authToken); // add the user's bearer token to the client - if (token != null) { + if (token.nonEmpty()) { LOGGER.debug("setting access token for leonardo request"); - apiClient.setAccessToken(token); + apiClient.setAccessToken(token.getValue()); } else { LOGGER.warn("No access token found for leonardo request."); } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/sam/HttpSamClientFactory.java b/service/src/main/java/org/databiosphere/workspacedataservice/sam/HttpSamClientFactory.java index c1ffd12da..a13770192 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/sam/HttpSamClientFactory.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/sam/HttpSamClientFactory.java @@ -1,7 +1,6 @@ package org.databiosphere.workspacedataservice.sam; import java.util.List; -import java.util.Objects; import okhttp3.OkHttpClient; import okhttp3.Protocol; import org.apache.commons.lang3.StringUtils; @@ -9,6 +8,7 @@ import org.broadinstitute.dsde.workbench.client.sam.api.ResourcesApi; import org.broadinstitute.dsde.workbench.client.sam.api.StatusApi; import org.broadinstitute.dsde.workbench.client.sam.api.UsersApi; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,12 +44,12 @@ private ApiClient getApiClient(String authToken) { } // grab the current user's bearer token (see BearerTokenFilter) or use parameter value - String token = TokenContextUtil.getToken(authToken); + BearerToken token = TokenContextUtil.getToken(authToken); // add the user's bearer token to the client - if (!Objects.isNull(token)) { + if (token.nonEmpty()) { LOGGER.debug("setting access token for Sam request"); - apiClient.setAccessToken(token); + apiClient.setAccessToken(token.getValue()); } else { LOGGER.warn("No access token found for Sam request."); } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/sam/TokenContextUtil.java b/service/src/main/java/org/databiosphere/workspacedataservice/sam/TokenContextUtil.java index f1c1846a6..c5eb7eab8 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/sam/TokenContextUtil.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/sam/TokenContextUtil.java @@ -5,7 +5,9 @@ import java.util.Map; import java.util.function.Supplier; +import javax.validation.constraints.NotNull; import org.databiosphere.workspacedataservice.jobexec.JobContextHolder; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestAttributes; @@ -33,8 +35,9 @@ private TokenContextUtil() {} * @param initialValue the first value to check for a non-null token * @return the final value */ - public static String getToken(String initialValue) { - return getToken(initialValue, () -> null); + @NotNull + public static BearerToken getToken(String initialValue) { + return getToken(BearerToken.of(initialValue), BearerToken::empty); } /** @@ -54,8 +57,9 @@ public static String getToken(String initialValue) { * @param orElse the value to return if the token was not found otherwise * @return the final value */ - public static String getToken(String initialValue, Supplier orElse) { - if (initialValue != null) { + @NotNull + public static BearerToken getToken(BearerToken initialValue, Supplier orElse) { + if (initialValue != null && initialValue.nonEmpty()) { return initialValue; } return getToken(orElse); @@ -68,9 +72,10 @@ public static String getToken(String initialValue, Supplier orElse) { * @param orElse the value to return if the token was not found otherwise * @return the final value */ - public static String getToken(Supplier orElse) { - String foundToken = getToken(); - if (foundToken != null) { + @NotNull + public static BearerToken getToken(Supplier orElse) { + BearerToken foundToken = getToken(); + if (foundToken.nonEmpty()) { // N.B. no logging here; this is the simplest case return foundToken; } else { @@ -83,20 +88,21 @@ public static String getToken(Supplier orElse) { * Look in RequestContextHolder and then JobContextHolder, in that order, for a non-null String * ATTRIBUTE_NAME_TOKEN * - * @return the token if found; null otherwise + * @return the token if found; BearerToken.empty() otherwise */ - public static String getToken() { - String foundToken; + @NotNull + public static BearerToken getToken() { + BearerToken foundToken; // look in request context; if non-null, return it foundToken = tokenFromRequestContext(); - if (foundToken != null) { + if (foundToken.nonEmpty()) { LOGGER.debug("Token retrieved from request context."); return foundToken; } // look in job context; return whatever we found, even if null foundToken = tokenFromJobContext(); - if (foundToken != null) { + if (foundToken.nonEmpty()) { LOGGER.debug("Token retrieved from job context."); } return foundToken; @@ -105,36 +111,42 @@ public static String getToken() { /** * Look in RequestContextHolder for a non-null String ATTRIBUTE_NAME_TOKEN * - * @return the token if found; null otherwise + * @return the token if found; BearerToken.empty() otherwise */ - private static String tokenFromRequestContext() { + @NotNull + private static BearerToken tokenFromRequestContext() { // do any request attributes exist? RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes != null) { return maybeToken(requestAttributes.getAttribute(ATTRIBUTE_NAME_TOKEN, SCOPE_REQUEST)); } - return null; + return BearerToken.empty(); } /** * Look in JobContextHolder for a non-null String ATTRIBUTE_NAME_TOKEN * - * @return the token if found; null otherwise + * @return the token if found; BearerToken.empty() otherwise */ - private static String tokenFromJobContext() { + @NotNull + private static BearerToken tokenFromJobContext() { // do any job attributes exist? Map jobAttributes = JobContextHolder.getAttributes(); - if (jobAttributes == null) { - return null; + if (jobAttributes != null) { + return maybeToken(jobAttributes.get(ATTRIBUTE_NAME_TOKEN)); } - return maybeToken(jobAttributes.get(ATTRIBUTE_NAME_TOKEN)); + return BearerToken.empty(); } /** Convenience: is the input object non-null and a String? */ - private static String maybeToken(Object obj) { + @NotNull + private static BearerToken maybeToken(Object obj) { + // as of this writing, if "obj instanceof String" passes, then "BearerToken.isValid" will always + // pass. The check is included here for future compatibility, in case we change the isValid + // implementation later. if (obj instanceof String strVal) { - return strVal; + return BearerToken.of(strVal); } - return null; + return BearerToken.empty(); } } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/service/ImportService.java b/service/src/main/java/org/databiosphere/workspacedataservice/service/ImportService.java index 3822aa00a..924d5af58 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/service/ImportService.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/service/ImportService.java @@ -18,6 +18,7 @@ import org.databiosphere.workspacedataservice.sam.SamDao; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; import org.databiosphere.workspacedataservice.service.model.exception.AuthorizationException; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.databiosphere.workspacedataservice.shared.model.Schedulable; import org.databiosphere.workspacedataservice.shared.model.job.Job; import org.databiosphere.workspacedataservice.shared.model.job.JobInput; @@ -69,12 +70,12 @@ public GenericJobServerModel createImport( // get the user's token from the current request // TODO: this should actually get a pet token for the user, to ensure the token won't time out - String token = TokenContextUtil.getToken(); + BearerToken token = TokenContextUtil.getToken(); // create the arguments for the schedulable job Map arguments = new HashMap<>(); - if (token != null) { - arguments.put(ARG_TOKEN, token); + if (token.nonEmpty()) { + arguments.put(ARG_TOKEN, token.getValue()); } arguments.put(ARG_URL, importRequest.getUrl().toString()); arguments.put(ARG_INSTANCE, instanceUuid.toString()); diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/shared/model/BearerToken.java b/service/src/main/java/org/databiosphere/workspacedataservice/shared/model/BearerToken.java new file mode 100644 index 000000000..b05bd433e --- /dev/null +++ b/service/src/main/java/org/databiosphere/workspacedataservice/shared/model/BearerToken.java @@ -0,0 +1,52 @@ +package org.databiosphere.workspacedataservice.shared.model; + +import java.util.Objects; + +/** + * Record class to represent an auth token; use this instead of String for more type safety and + * readability of argument lists. As of this writing, WDS includes both Nimbus and Apache Oltu as + * transitive dependencies, and both of those libraries include models to represent a token; but + * they also both have more complexity than we need, and we don't want to depend too much on + * transitive dependencies + */ +public class BearerToken { + + private final String value; + + private BearerToken(String value) { + this.value = value; + } + + /** returns an empty BearerToken; that is, a BearerToken whose `value` is null */ + public static BearerToken empty() { + return new BearerToken(null); + } + + /** returns a BearerToken with the specified `value` */ + public static BearerToken of(String val) { + return new BearerToken(val); + } + + public String getValue() { + return value; + } + + /** indicates if this BearerToken is empty; that is, has a null `value` */ + public boolean isEmpty() { + return value == null; + } + + /** indicates if this BearerToken is not empty; that is, has a non-null `value` */ + public boolean nonEmpty() { + return !isEmpty(); + } + + // generated by IntelliJ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BearerToken that = (BearerToken) o; + return Objects.equals(value, that.value); + } +} diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/sourcewds/HttpWorkspaceDataServiceClientFactory.java b/service/src/main/java/org/databiosphere/workspacedataservice/sourcewds/HttpWorkspaceDataServiceClientFactory.java index 1b792bc3b..aa04d378d 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/sourcewds/HttpWorkspaceDataServiceClientFactory.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/sourcewds/HttpWorkspaceDataServiceClientFactory.java @@ -7,6 +7,7 @@ import org.databiosphere.workspacedata.api.CloningApi; import org.databiosphere.workspacedata.client.ApiClient; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,12 +35,12 @@ private ApiClient getApiClient(String authToken, String workspaceDataServiceUrl) } // grab the current user's bearer token (see BearerTokenFilter) or use parameter value - String token = TokenContextUtil.getToken(authToken); + BearerToken token = TokenContextUtil.getToken(authToken); // add the user's bearer token to the client - if (token != null) { + if (token.nonEmpty()) { LOGGER.debug("setting access token for workspace data service request"); - apiClient.setAccessToken(token); + apiClient.setAccessToken(token.getValue()); } else { LOGGER.warn("No access token found for workspace data service request."); } diff --git a/service/src/main/java/org/databiosphere/workspacedataservice/workspacemanager/HttpWorkspaceManagerClientFactory.java b/service/src/main/java/org/databiosphere/workspacedataservice/workspacemanager/HttpWorkspaceManagerClientFactory.java index bbd9a023a..70962fa25 100644 --- a/service/src/main/java/org/databiosphere/workspacedataservice/workspacemanager/HttpWorkspaceManagerClientFactory.java +++ b/service/src/main/java/org/databiosphere/workspacedataservice/workspacemanager/HttpWorkspaceManagerClientFactory.java @@ -7,6 +7,7 @@ import javax.ws.rs.client.Client; import org.apache.commons.lang3.StringUtils; import org.databiosphere.workspacedataservice.sam.TokenContextUtil; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,12 +34,12 @@ private ApiClient getApiClient(String authToken) { } // grab the current user's bearer token (see BearerTokenFilter) or use parameter value - String token = TokenContextUtil.getToken(authToken); + BearerToken token = TokenContextUtil.getToken(authToken); // add the user's bearer token to the client - if (token != null) { + if (token.nonEmpty()) { LOGGER.debug("setting access token for workspace manager request"); - apiClient.setAccessToken(token); + apiClient.setAccessToken(token.getValue()); } else { LOGGER.warn("No access token found for workspace manager request."); } diff --git a/service/src/test/java/org/databiosphere/workspacedataservice/jobexec/QuartzJobTest.java b/service/src/test/java/org/databiosphere/workspacedataservice/jobexec/QuartzJobTest.java index 95472fe93..cb5580aab 100644 --- a/service/src/test/java/org/databiosphere/workspacedataservice/jobexec/QuartzJobTest.java +++ b/service/src/test/java/org/databiosphere/workspacedataservice/jobexec/QuartzJobTest.java @@ -2,7 +2,7 @@ import static org.databiosphere.workspacedataservice.shared.model.Schedulable.ARG_TOKEN; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -71,7 +71,7 @@ protected JobDao getJobDao() { @Override protected void executeInternal(UUID jobId, JobExecutionContext context) { - assertEquals(expectedToken, TokenContextUtil.getToken()); + assertEquals(expectedToken, TokenContextUtil.getToken().getValue()); } } @@ -87,11 +87,13 @@ void tokenIsStashedAndCleaned() throws JobExecutionException { when(mockContext.getJobDetail()).thenReturn(jobDetail); // assert that no token exists via TokenContextUtil - assertNull(TokenContextUtil.getToken(), "no token should exist before running the job"); + assertTrue( + TokenContextUtil.getToken().isEmpty(), "no token should exist before running the job"); // execute the TestableQuartzJob, which asserts that the token passed properly from the // JobDataMap into job context and is therefore retrievable via TokenContextUtil new TestableQuartzJob(expectedToken).execute(mockContext); // assert that the token is cleaned up and no longer reachable via TokenContextUtil - assertNull(TokenContextUtil.getToken(), "no token should exist after running the job"); + assertTrue( + TokenContextUtil.getToken().isEmpty(), "no token should exist after running the job"); } } diff --git a/service/src/test/java/org/databiosphere/workspacedataservice/sam/TokenContextUtilTest.java b/service/src/test/java/org/databiosphere/workspacedataservice/sam/TokenContextUtilTest.java index 7b40a0161..6893945e2 100644 --- a/service/src/test/java/org/databiosphere/workspacedataservice/sam/TokenContextUtilTest.java +++ b/service/src/test/java/org/databiosphere/workspacedataservice/sam/TokenContextUtilTest.java @@ -2,13 +2,14 @@ import static org.databiosphere.workspacedataservice.sam.BearerTokenFilter.ATTRIBUTE_NAME_TOKEN; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST; import org.apache.commons.lang3.RandomStringUtils; import org.databiosphere.workspacedataservice.jobexec.JobContextHolder; import org.databiosphere.workspacedataservice.service.model.exception.AuthenticationException; +import org.databiosphere.workspacedataservice.shared.model.BearerToken; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestAttributes; @@ -19,7 +20,7 @@ class TokenContextUtilTest { @Test void nonNullInitialValue() { - String expected = RandomStringUtils.randomAlphanumeric(10); + BearerToken expected = BearerToken.of(RandomStringUtils.randomAlphanumeric(10)); // set a dummy value into request attributes MockHttpServletRequest request = new MockHttpServletRequest(); @@ -33,7 +34,8 @@ void nonNullInitialValue() { JobContextHolder.setAttribute(ATTRIBUTE_NAME_TOKEN, "dummy job value"); // call getToken with a non-null initialValue - String actual = TokenContextUtil.getToken(expected, () -> "dummy orElse value"); + BearerToken actual = + TokenContextUtil.getToken(expected, () -> BearerToken.of("dummy orElse value")); assertEquals(expected, actual); } finally { JobContextHolder.destroy(); @@ -42,12 +44,12 @@ void nonNullInitialValue() { @Test void valueInRequest() { - String expected = RandomStringUtils.randomAlphanumeric(10); + BearerToken expected = BearerToken.of(RandomStringUtils.randomAlphanumeric(10)); // set the expected token into request attributes MockHttpServletRequest request = new MockHttpServletRequest(); RequestAttributes requestAttributes = new ServletRequestAttributes(request); - requestAttributes.setAttribute(ATTRIBUTE_NAME_TOKEN, expected, SCOPE_REQUEST); + requestAttributes.setAttribute(ATTRIBUTE_NAME_TOKEN, expected.getValue(), SCOPE_REQUEST); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); try { @@ -56,7 +58,8 @@ void valueInRequest() { JobContextHolder.setAttribute(ATTRIBUTE_NAME_TOKEN, "dummy job value"); // call getToken with a null initialValue - String actual = TokenContextUtil.getToken(null, () -> "dummy orElse value"); + BearerToken actual = + TokenContextUtil.getToken(null, () -> BearerToken.of("dummy orElse value")); assertEquals(expected, actual); } finally { JobContextHolder.destroy(); @@ -65,7 +68,7 @@ void valueInRequest() { @Test void valueInJob() { - String expected = RandomStringUtils.randomAlphanumeric(10); + BearerToken expected = BearerToken.of(RandomStringUtils.randomAlphanumeric(10)); // set request attributes to empty RequestContextHolder.setRequestAttributes(null); @@ -73,9 +76,10 @@ void valueInJob() { try { // set the expected token into job attributes JobContextHolder.init(); - JobContextHolder.setAttribute(ATTRIBUTE_NAME_TOKEN, expected); + JobContextHolder.setAttribute(ATTRIBUTE_NAME_TOKEN, expected.getValue()); // call getToken with a null initialValue - String actual = TokenContextUtil.getToken(null, () -> "dummy orElse value"); + BearerToken actual = + TokenContextUtil.getToken(null, () -> BearerToken.of("dummy orElse value")); assertEquals(expected, actual); } finally { JobContextHolder.destroy(); @@ -84,13 +88,13 @@ void valueInJob() { @Test void callOrElse() { - String expected = RandomStringUtils.randomAlphanumeric(10); + BearerToken expected = BearerToken.of(RandomStringUtils.randomAlphanumeric(10)); // set request attributes to empty RequestContextHolder.setRequestAttributes(null); // ensure job attributes are empty JobContextHolder.destroy(); // call getToken with a null initialValue and an orElse that returns the expected value - String actual = TokenContextUtil.getToken(null, () -> expected); + BearerToken actual = TokenContextUtil.getToken(null, () -> expected); assertEquals(expected, actual); } @@ -117,13 +121,12 @@ void throwInOrElse() { @Test void noOrElseSpecified() { - String expected = RandomStringUtils.randomAlphanumeric(10); // set request attributes to empty RequestContextHolder.setRequestAttributes(null); // ensure job attributes are empty JobContextHolder.destroy(); // call getToken with a null initialValue and an orElse that returns the expected value - String actual = TokenContextUtil.getToken((String) null); - assertNull(actual); + BearerToken actual = TokenContextUtil.getToken((String) null); + assertTrue(actual.isEmpty()); } } diff --git a/service/src/test/java/org/databiosphere/workspacedataservice/shared/model/BearerTokenTest.java b/service/src/test/java/org/databiosphere/workspacedataservice/shared/model/BearerTokenTest.java new file mode 100644 index 000000000..0f831c78b --- /dev/null +++ b/service/src/test/java/org/databiosphere/workspacedataservice/shared/model/BearerTokenTest.java @@ -0,0 +1,42 @@ +package org.databiosphere.workspacedataservice.shared.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class BearerTokenTest { + + @Test + void nullIsEmpty() { + assertTrue(BearerToken.of(null).isEmpty()); + } + + // empty string will cause 401s in practice, but BearerToken is only lightweight validation + @Test + void emptyStringIsNotEmpty() { + assertTrue(BearerToken.of("").nonEmpty()); + } + + // whitespace-only string will cause 401s in practice, but BearerToken is only lightweight + // validation + @Test + void whitespaceIsNotEmpty() { + assertTrue(BearerToken.of(" ").nonEmpty()); + } + + @Test + void populatedStringIsNotEmpty() { + assertTrue(BearerToken.of("Hello World").nonEmpty()); + } + + @Test + void emptyConstructorIsEmpty() { + assertTrue(BearerToken.empty().isEmpty()); + } + + @Test + void emptyConstructorNonEmptyIsFalse() { + assertFalse(BearerToken.empty().nonEmpty()); + } +}