mirror of https://github.com/apache/nifi.git
NIFI-9322 Refactored OIDC and SAML Access Resources
- Removed parent AccessResource from OIDCAccessResource and SAMLAccessResource to avoid unexpected inherited methods - Moved Token Expiration validation from AccessResource to StandardBearerTokenProvider Signed-off-by: Nathan Gough <thenatog@gmail.com> This closes #5489.
This commit is contained in:
parent
28cd5d1300
commit
9865ea2bfb
|
@ -106,7 +106,7 @@ public class AccessResource extends ApplicationResource {
|
|||
private BearerTokenResolver bearerTokenResolver;
|
||||
private KnoxService knoxService;
|
||||
private KerberosService kerberosService;
|
||||
protected LogoutRequestManager logoutRequestManager;
|
||||
private LogoutRequestManager logoutRequestManager;
|
||||
|
||||
/**
|
||||
* Retrieves the access configuration for this NiFi.
|
||||
|
@ -348,8 +348,7 @@ public class AccessResource extends ApplicationResource {
|
|||
final String expirationFromProperties = properties.getKerberosAuthenticationExpiration();
|
||||
long expiration = Math.round(FormatUtils.getPreciseTimeDuration(expirationFromProperties, TimeUnit.MILLISECONDS));
|
||||
final String rawIdentity = authentication.getName();
|
||||
String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
|
||||
expiration = validateTokenExpiration(expiration, mappedIdentity);
|
||||
final String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
|
||||
|
||||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(mappedIdentity, expiration, "KerberosService");
|
||||
final String token = bearerTokenProvider.getBearerToken(loginAuthenticationToken);
|
||||
|
@ -416,8 +415,8 @@ public class AccessResource extends ApplicationResource {
|
|||
// attempt to authenticate
|
||||
final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password));
|
||||
final String rawIdentity = authenticationResponse.getIdentity();
|
||||
String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
|
||||
long expiration = validateTokenExpiration(authenticationResponse.getExpiration(), mappedIdentity);
|
||||
final String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
|
||||
final long expiration = authenticationResponse.getExpiration();
|
||||
|
||||
// create the authentication token
|
||||
loginAuthenticationToken = new LoginAuthenticationToken(mappedIdentity, expiration, authenticationResponse.getIssuer());
|
||||
|
@ -506,7 +505,7 @@ public class AccessResource extends ApplicationResource {
|
|||
httpServletResponse.sendRedirect(getNiFiLogoutCompleteUri());
|
||||
}
|
||||
|
||||
LogoutRequest completeLogoutRequest(final HttpServletResponse httpServletResponse) {
|
||||
private LogoutRequest completeLogoutRequest(final HttpServletResponse httpServletResponse) {
|
||||
LogoutRequest logoutRequest = null;
|
||||
|
||||
final Optional<String> cookieValue = getLogoutRequestIdentifier();
|
||||
|
@ -522,24 +521,7 @@ public class AccessResource extends ApplicationResource {
|
|||
return logoutRequest;
|
||||
}
|
||||
|
||||
long validateTokenExpiration(long proposedTokenExpiration, String identity) {
|
||||
final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
|
||||
final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
|
||||
|
||||
if (proposedTokenExpiration > maxExpiration) {
|
||||
logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", maxExpiration,
|
||||
proposedTokenExpiration, identity));
|
||||
proposedTokenExpiration = maxExpiration;
|
||||
} else if (proposedTokenExpiration < minExpiration) {
|
||||
logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", minExpiration,
|
||||
proposedTokenExpiration, identity));
|
||||
proposedTokenExpiration = minExpiration;
|
||||
}
|
||||
|
||||
return proposedTokenExpiration;
|
||||
}
|
||||
|
||||
String getNiFiLogoutCompleteUri() {
|
||||
private String getNiFiLogoutCompleteUri() {
|
||||
return getNiFiUri() + "logout-complete";
|
||||
}
|
||||
|
||||
|
@ -548,7 +530,7 @@ public class AccessResource extends ApplicationResource {
|
|||
*
|
||||
* @param httpServletResponse HTTP Servlet Response
|
||||
*/
|
||||
protected void removeLogoutRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
private void removeLogoutRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
|
@ -557,7 +539,7 @@ public class AccessResource extends ApplicationResource {
|
|||
*
|
||||
* @return Optional Logout Request Identifier
|
||||
*/
|
||||
protected Optional<String> getLogoutRequestIdentifier() {
|
||||
private Optional<String> getLogoutRequestIdentifier() {
|
||||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ import java.util.regex.Pattern;
|
|||
value = OIDCEndpoints.OIDC_ACCESS_ROOT,
|
||||
description = "Endpoints for obtaining an access token or checking access status."
|
||||
)
|
||||
public class OIDCAccessResource extends AccessResource {
|
||||
public class OIDCAccessResource extends ApplicationResource {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OIDCAccessResource.class);
|
||||
private static final String OIDC_ID_TOKEN_AUTHN_ERROR = "Unable to exchange authorization for ID token: ";
|
||||
|
@ -115,7 +115,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
public void oidcRequest(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
forwardToLoginMessagePage(httpServletRequest, httpServletResponse, AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
forwardToLoginMessagePage(httpServletRequest, httpServletResponse, AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
public Response oidcExchange(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new AuthenticationNotSupportedException(AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
throw new AuthenticationNotSupportedException(AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
}
|
||||
|
||||
// ensure oidc is enabled
|
||||
|
@ -238,7 +238,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
)
|
||||
public void oidcLogout(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) throws Exception {
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new IllegalStateException(AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
throw new IllegalStateException(AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
}
|
||||
|
||||
if (!oidcService.isOidcEnabled()) {
|
||||
|
@ -468,7 +468,7 @@ public class OIDCAccessResource extends AccessResource {
|
|||
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, pageTitle, AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, pageTitle, AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.nifi.idp.IdpType;
|
|||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.api.cookie.ApplicationCookieName;
|
||||
import org.apache.nifi.web.security.logout.LogoutRequest;
|
||||
import org.apache.nifi.web.security.logout.LogoutRequestManager;
|
||||
import org.apache.nifi.web.security.saml.SAMLCredentialStore;
|
||||
import org.apache.nifi.web.security.saml.SAMLEndpoints;
|
||||
import org.apache.nifi.web.security.saml.SAMLService;
|
||||
|
@ -61,7 +62,7 @@ import java.util.stream.Collectors;
|
|||
value = SAMLEndpoints.SAML_ACCESS_ROOT,
|
||||
description = "Endpoints for authenticating, obtaining an access token or logging out of a configured SAML authentication provider."
|
||||
)
|
||||
public class SAMLAccessResource extends AccessResource {
|
||||
public class SAMLAccessResource extends ApplicationResource {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SAMLAccessResource.class);
|
||||
private static final String SAML_METADATA_MEDIA_TYPE = "application/samlmetadata+xml";
|
||||
|
@ -73,6 +74,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
private SAMLStateManager samlStateManager;
|
||||
private SAMLCredentialStore samlCredentialStore;
|
||||
private IdpUserGroupService idpUserGroupService;
|
||||
private LogoutRequestManager logoutRequestManager;
|
||||
|
||||
@GET
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
|
@ -85,7 +87,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
public Response samlMetadata(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new AuthenticationNotSupportedException(AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
throw new AuthenticationNotSupportedException(AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
}
|
||||
|
||||
// ensure saml is enabled
|
||||
|
@ -212,7 +214,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
// create the login token
|
||||
final String rawIdentity = samlService.getUserIdentity(samlCredential);
|
||||
final String mappedIdentity = IdentityMappingUtil.mapIdentity(rawIdentity, IdentityMappingUtil.getIdentityMappings(properties));
|
||||
final long expiration = validateTokenExpiration(samlService.getAuthExpiration(), mappedIdentity);
|
||||
final long expiration = samlService.getAuthExpiration();
|
||||
final String issuer = samlCredential.getRemoteEntityID();
|
||||
|
||||
final LoginAuthenticationToken loginToken = new LoginAuthenticationToken(mappedIdentity, mappedIdentity, expiration, issuer);
|
||||
|
@ -255,7 +257,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new AuthenticationNotSupportedException(AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
throw new AuthenticationNotSupportedException(AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
}
|
||||
|
||||
// ensure saml is enabled
|
||||
|
@ -446,19 +448,21 @@ public class SAMLAccessResource extends AccessResource {
|
|||
assert(isSamlEnabled(httpServletRequest, httpServletResponse, !LOGGING_IN));
|
||||
|
||||
// complete the logout request if one exists
|
||||
final LogoutRequest completedLogoutRequest = completeLogoutRequest(httpServletResponse);
|
||||
final Optional<String> cookieValue = getLogoutRequestIdentifier();
|
||||
if (cookieValue.isPresent()) {
|
||||
final String logoutRequestIdentifier = cookieValue.get();
|
||||
final LogoutRequest logoutRequest = logoutRequestManager.complete(logoutRequestIdentifier);
|
||||
|
||||
// if a logout request was completed, then delete the stored SAMLCredential for that user
|
||||
if (completedLogoutRequest != null) {
|
||||
final String userIdentity = completedLogoutRequest.getMappedUserIdentity();
|
||||
final String mappedUserIdentity = logoutRequest.getMappedUserIdentity();
|
||||
samlCredentialStore.delete(mappedUserIdentity);
|
||||
idpUserGroupService.deleteUserGroups(mappedUserIdentity);
|
||||
|
||||
logger.info("Removing cached SAML information for " + userIdentity);
|
||||
samlCredentialStore.delete(userIdentity);
|
||||
|
||||
logger.info("Removing cached SAML Groups for " + userIdentity);
|
||||
idpUserGroupService.deleteUserGroups(userIdentity);
|
||||
logger.info("Logout Request [{}] Identity [{}] SAML Local Logout Completed", logoutRequestIdentifier, mappedUserIdentity);
|
||||
} else {
|
||||
logger.warn("Logout Request Cookie [{}] not found", ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER.getCookieName());
|
||||
}
|
||||
|
||||
removeLogoutRequestCookie(httpServletResponse);
|
||||
// redirect to logout landing page
|
||||
httpServletResponse.sendRedirect(getNiFiLogoutCompleteUri());
|
||||
}
|
||||
|
@ -488,7 +492,7 @@ public class SAMLAccessResource extends AccessResource {
|
|||
|
||||
// only consider user specific access over https
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, pageTitle, AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
forwardToMessagePage(httpServletRequest, httpServletResponse, pageTitle, AccessResource.AUTHENTICATION_NOT_ENABLED_MSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -508,6 +512,18 @@ public class SAMLAccessResource extends AccessResource {
|
|||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.SAML_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
private void removeLogoutRequestCookie(final HttpServletResponse httpServletResponse) {
|
||||
applicationCookieService.removeCookie(getCookieResourceUri(), httpServletResponse, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
private Optional<String> getLogoutRequestIdentifier() {
|
||||
return applicationCookieService.getCookieValue(httpServletRequest, ApplicationCookieName.LOGOUT_REQUEST_IDENTIFIER);
|
||||
}
|
||||
|
||||
private String getNiFiLogoutCompleteUri() {
|
||||
return getNiFiUri() + "logout-complete";
|
||||
}
|
||||
|
||||
public void setSamlService(SAMLService samlService) {
|
||||
this.samlService = samlService;
|
||||
}
|
||||
|
@ -531,4 +547,8 @@ public class SAMLAccessResource extends AccessResource {
|
|||
protected NiFiProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setLogoutRequestManager(LogoutRequestManager logoutRequestManager) {
|
||||
this.logoutRequestManager = logoutRequestManager;
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
@ -44,6 +46,10 @@ public class StandardBearerTokenProvider implements BearerTokenProvider {
|
|||
|
||||
private static final String URL_ENCODED_CHARACTER_SET = StandardCharsets.UTF_8.name();
|
||||
|
||||
private static final Duration MAXIMUM_EXPIRATION = Duration.ofHours(12);
|
||||
|
||||
private static final Duration MINIMUM_EXPIRATION = Duration.ofMinutes(1);
|
||||
|
||||
private final JwsSignerProvider jwsSignerProvider;
|
||||
|
||||
public StandardBearerTokenProvider(final JwsSignerProvider jwsSignerProvider) {
|
||||
|
@ -64,7 +70,7 @@ public class StandardBearerTokenProvider implements BearerTokenProvider {
|
|||
|
||||
final String issuer = getUrlEncoded(loginAuthenticationToken.getIssuer());
|
||||
final Date now = new Date();
|
||||
final Date expirationTime = new Date(loginAuthenticationToken.getExpiration());
|
||||
final Date expirationTime = getExpirationTime(loginAuthenticationToken);
|
||||
final JWTClaimsSet claims = new JWTClaimsSet.Builder()
|
||||
.jwtID(UUID.randomUUID().toString())
|
||||
.subject(subject)
|
||||
|
@ -78,6 +84,24 @@ public class StandardBearerTokenProvider implements BearerTokenProvider {
|
|||
return getSignedBearerToken(claims);
|
||||
}
|
||||
|
||||
private Date getExpirationTime(final LoginAuthenticationToken loginAuthenticationToken) {
|
||||
Instant expiration = Instant.ofEpochMilli(loginAuthenticationToken.getExpiration());
|
||||
|
||||
final Instant maximumExpiration = Instant.now().plus(MAXIMUM_EXPIRATION);
|
||||
final Instant minimumExpiration = Instant.now().plus(MINIMUM_EXPIRATION);
|
||||
|
||||
final String identity = loginAuthenticationToken.getName();
|
||||
if (expiration.isAfter(maximumExpiration)) {
|
||||
LOGGER.warn("Identity [{}] Token Expiration [{}] greater than maximum [{}]", identity, expiration, MAXIMUM_EXPIRATION);
|
||||
expiration = maximumExpiration;
|
||||
} else if (expiration.isBefore(minimumExpiration)) {
|
||||
LOGGER.warn("Identity [{}] Token Expiration [{}] less than minimum [{}]", identity, expiration, MINIMUM_EXPIRATION);
|
||||
expiration = minimumExpiration;
|
||||
}
|
||||
|
||||
return Date.from(expiration);
|
||||
}
|
||||
|
||||
private String getSignedBearerToken(final JWTClaimsSet claims) {
|
||||
final Date expirationTime = claims.getExpirationTime();
|
||||
final JwsSignerContainer jwsSignerContainer = jwsSignerProvider.getJwsSignerContainer(expirationTime.toInstant());
|
||||
|
|
|
@ -19,8 +19,7 @@ package org.apache.nifi.web.security.token;
|
|||
import org.apache.nifi.security.util.CertificateUtils;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token.
|
||||
|
@ -57,8 +56,7 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
|
|||
this.identity = identity;
|
||||
this.username = username;
|
||||
this.issuer = issuer;
|
||||
Calendar now = Calendar.getInstance();
|
||||
this.expiration = now.getTimeInMillis() + expiration;
|
||||
this.expiration = Instant.now().plusMillis(expiration).toEpochMilli();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,20 +96,15 @@ public class LoginAuthenticationToken extends AbstractAuthenticationToken {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
Calendar expirationTime = Calendar.getInstance();
|
||||
expirationTime.setTimeInMillis(getExpiration());
|
||||
long remainingTime = expirationTime.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS");
|
||||
dateFormat.setTimeZone(expirationTime.getTimeZone());
|
||||
String expirationTimeString = dateFormat.format(expirationTime.getTime());
|
||||
final Instant expirationTime = Instant.ofEpochMilli(expiration);
|
||||
long remainingTime = expirationTime.toEpochMilli() - Instant.now().toEpochMilli();
|
||||
|
||||
return new StringBuilder("LoginAuthenticationToken for ")
|
||||
.append(getName())
|
||||
.append(" issued by ")
|
||||
.append(getIssuer())
|
||||
.append(" expiring at ")
|
||||
.append(expirationTimeString)
|
||||
.append(expirationTime)
|
||||
.append(" [")
|
||||
.append(getExpiration())
|
||||
.append(" ms, ")
|
||||
|
|
|
@ -27,34 +27,41 @@ import com.nimbusds.jwt.SignedJWT;
|
|||
import org.apache.nifi.web.security.jwt.jws.JwsSignerContainer;
|
||||
import org.apache.nifi.web.security.jwt.jws.JwsSignerProvider;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.junit.MockitoJUnitRunner;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class StandardBearerTokenProviderTest {
|
||||
private static final String USERNAME = "USERNAME";
|
||||
|
||||
private static final String IDENTITY = "IDENTITY";
|
||||
|
||||
private static final long EXPIRATION = 60;
|
||||
private static final Duration EXPIRATION = Duration.ofHours(1);
|
||||
|
||||
private static final Duration MAXIMUM_DURATION_EXCEEDED = Duration.parse("PT12H5M");
|
||||
|
||||
private static final Duration MINIMUM_DURATION_EXCEEDED = Duration.parse("PT30S");
|
||||
|
||||
private static final String ISSUER = "ISSUER";
|
||||
|
||||
|
@ -73,7 +80,7 @@ public class StandardBearerTokenProviderTest {
|
|||
|
||||
private JWSSigner jwsSigner;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setProvider() throws NoSuchAlgorithmException {
|
||||
provider = new StandardBearerTokenProvider(jwsSignerProvider);
|
||||
|
||||
|
@ -86,24 +93,76 @@ public class StandardBearerTokenProviderTest {
|
|||
|
||||
@Test
|
||||
public void testGetBearerToken() throws ParseException, JOSEException {
|
||||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(IDENTITY, USERNAME, EXPIRATION, ISSUER);
|
||||
final String keyIdentifier = UUID.randomUUID().toString();
|
||||
final JwsSignerContainer jwsSignerContainer = new JwsSignerContainer(keyIdentifier, JWS_ALGORITHM, jwsSigner);
|
||||
when(jwsSignerProvider.getJwsSignerContainer(isA(Instant.class))).thenReturn(jwsSignerContainer);
|
||||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(IDENTITY, USERNAME, EXPIRATION.toMillis(), ISSUER);
|
||||
setSignerProvider();
|
||||
|
||||
final String bearerToken = provider.getBearerToken(loginAuthenticationToken);
|
||||
|
||||
final SignedJWT signedJwt = SignedJWT.parse(bearerToken);
|
||||
assertTrue("Verification Failed", signedJwt.verify(jwsVerifier));
|
||||
|
||||
final SignedJWT signedJwt = assertTokenVerified(bearerToken);
|
||||
final JWTClaimsSet claims = signedJwt.getJWTClaimsSet();
|
||||
assertNotNull("Issue Time not found", claims.getIssueTime());
|
||||
assertNotNull("Not Before Time Time not found", claims.getNotBeforeTime());
|
||||
assertNotNull("Expiration Time Time not found", claims.getExpirationTime());
|
||||
assertNotNull(claims.getIssueTime(), "Issue Time not found");
|
||||
assertNotNull(claims.getNotBeforeTime(), "Not Before Time not found");
|
||||
|
||||
final Date claimExpirationTime = claims.getExpirationTime();
|
||||
assertNotNull(claimExpirationTime, "Expiration Time not found");
|
||||
|
||||
final Date loginExpirationTime = new Date(loginAuthenticationToken.getExpiration());
|
||||
assertEquals(loginExpirationTime.toString(), claimExpirationTime.toString(), "Expiration Time not matched");
|
||||
|
||||
assertEquals(ISSUER, claims.getIssuer());
|
||||
assertEquals(Collections.singletonList(ISSUER), claims.getAudience());
|
||||
assertEquals(IDENTITY, claims.getSubject());
|
||||
assertEquals(USERNAME, claims.getClaim(SupportedClaim.PREFERRED_USERNAME.getClaim()));
|
||||
assertNotNull("JSON Web Token Identifier not found", claims.getJWTID());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBearerTokenExpirationMaximum() throws ParseException, JOSEException {
|
||||
final long expiration = MAXIMUM_DURATION_EXCEEDED.toMillis();
|
||||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(IDENTITY, USERNAME, expiration, ISSUER);
|
||||
setSignerProvider();
|
||||
|
||||
final String bearerToken = provider.getBearerToken(loginAuthenticationToken);
|
||||
|
||||
final SignedJWT signedJwt = assertTokenVerified(bearerToken);
|
||||
final JWTClaimsSet claims = signedJwt.getJWTClaimsSet();
|
||||
final Date claimExpirationTime = claims.getExpirationTime();
|
||||
assertNotNull(claimExpirationTime, "Expiration Time not found");
|
||||
|
||||
final Date loginExpirationTime = new Date(loginAuthenticationToken.getExpiration());
|
||||
assertNotSame(loginExpirationTime.toString(), claimExpirationTime.toString(), "Expiration Time matched");
|
||||
|
||||
assertTrue(claimExpirationTime.toInstant().isBefore(loginExpirationTime.toInstant()), "Claim Expiration after Login Expiration");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBearerTokenExpirationMinimum() throws ParseException, JOSEException {
|
||||
final long expiration = MINIMUM_DURATION_EXCEEDED.toMillis();
|
||||
final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(IDENTITY, USERNAME, expiration, ISSUER);
|
||||
setSignerProvider();
|
||||
|
||||
final String bearerToken = provider.getBearerToken(loginAuthenticationToken);
|
||||
|
||||
final SignedJWT signedJwt = assertTokenVerified(bearerToken);
|
||||
final JWTClaimsSet claims = signedJwt.getJWTClaimsSet();
|
||||
final Date claimExpirationTime = claims.getExpirationTime();
|
||||
assertNotNull(claimExpirationTime, "Expiration Time not found");
|
||||
|
||||
final Date loginExpirationTime = new Date(loginAuthenticationToken.getExpiration());
|
||||
assertNotSame(loginExpirationTime.toString(), claimExpirationTime.toString(), "Expiration Time matched");
|
||||
|
||||
assertTrue(claimExpirationTime.toInstant().isAfter(loginExpirationTime.toInstant()), "Claim Expiration before Login Expiration");
|
||||
}
|
||||
|
||||
private void setSignerProvider() {
|
||||
final String keyIdentifier = UUID.randomUUID().toString();
|
||||
final JwsSignerContainer jwsSignerContainer = new JwsSignerContainer(keyIdentifier, JWS_ALGORITHM, jwsSigner);
|
||||
when(jwsSignerProvider.getJwsSignerContainer(isA(Instant.class))).thenReturn(jwsSignerContainer);
|
||||
}
|
||||
|
||||
private SignedJWT assertTokenVerified(final String bearerToken) throws ParseException, JOSEException {
|
||||
final SignedJWT signedJwt = SignedJWT.parse(bearerToken);
|
||||
assertTrue(signedJwt.verify(jwsVerifier), "Verification Failed");
|
||||
return signedJwt;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue