mirror of
https://github.com/apache/nifi.git
synced 2025-02-10 12:05:22 +00:00
NIFI-6085 - Added /access/logout endpoint to allow JWT auth tokens to be removed correctly. Added some tests. Found an error in the KeyDAO which did not allow key deletion.
NIFI-6085 - Updated logOut method to use NiFiUserUtils and updated tests. NIFI-6085 - Added some more integration tests. NIFI-6085 Suppressed stacktrace when token is used after being invalidated. This closes #3362. Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
parent
376c344edb
commit
cf6f517250
@ -161,6 +161,7 @@ public class StandardKeyDAO implements KeyDAO {
|
||||
try {
|
||||
// add each authority for the specified user
|
||||
statement = connection.prepareStatement(DELETE_KEYS);
|
||||
statement.setString(1, identity);
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException sqle) {
|
||||
throw new DataAccessException(sqle);
|
||||
|
@ -175,6 +175,13 @@
|
||||
<artifactId>nifi-web-security</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web-security</artifactId>
|
||||
<version>1.10.0-SNAPSHOT</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web-optimistic-locking</artifactId>
|
||||
|
@ -344,6 +344,9 @@ public class AccessResource extends ApplicationResource {
|
||||
.build();
|
||||
httpServletResponse.sendRedirect(logoutUri.toString());
|
||||
}
|
||||
|
||||
String authorizationHeader = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION);
|
||||
jwtService.logOut(authorizationHeader);
|
||||
}
|
||||
|
||||
@GET
|
||||
@ -744,6 +747,35 @@ public class AccessResource extends ApplicationResource {
|
||||
return generateCreatedResponse(uri, token).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
@Produces(MediaType.WILDCARD)
|
||||
@Path("/logout")
|
||||
@ApiOperation(
|
||||
value = "Performs a logout for other providers that have been issued a JWT.",
|
||||
notes = NON_GUARANTEED_ENDPOINT
|
||||
)
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(code = 200, message = "User was logged out successfully."),
|
||||
@ApiResponse(code = 500, message = "Client failed to log out."),
|
||||
}
|
||||
)
|
||||
public Response logOut(@Context HttpServletRequest httpServletRequest, @Context HttpServletResponse httpServletResponse) {
|
||||
if (!httpServletRequest.isSecure()) {
|
||||
throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS.");
|
||||
}
|
||||
|
||||
String authorizationHeader = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION);
|
||||
final String token = StringUtils.substringAfterLast(authorizationHeader, " ");
|
||||
try {
|
||||
jwtService.logOut(token);
|
||||
return generateOkResponse().build();
|
||||
} catch (final JwtException e) {
|
||||
return Response.serverError().build();
|
||||
}
|
||||
}
|
||||
|
||||
private long validateTokenExpiration(long proposedTokenExpiration, String identity) {
|
||||
final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
|
||||
final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
package org.apache.nifi.integration.accesscontrol;
|
||||
|
||||
import org.apache.nifi.web.security.jwt.JwtServiceTest;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
import org.apache.nifi.integration.util.NiFiTestServer;
|
||||
@ -48,14 +50,19 @@ import javax.ws.rs.core.Response;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Access token endpoint test.
|
||||
*/
|
||||
public class ITAccessTokenEndpoint {
|
||||
|
||||
private final String user = "unregistered-user@nifi";
|
||||
private final String password = "password";
|
||||
private static final String CLIENT_ID = "token-endpoint-id";
|
||||
private static final String CONTEXT_PATH = "/nifi-api";
|
||||
|
||||
@ -114,7 +121,7 @@ public class ITAccessTokenEndpoint {
|
||||
}
|
||||
|
||||
// -----------
|
||||
// LOGIN CONIG
|
||||
// LOGIN CONFIG
|
||||
// -----------
|
||||
/**
|
||||
* Test getting access configuration.
|
||||
@ -254,7 +261,7 @@ public class ITAccessTokenEndpoint {
|
||||
// verify unknown
|
||||
Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
|
||||
|
||||
response = TOKEN_USER.testCreateToken(accessTokenUrl, "unregistered-user@nifi", "password");
|
||||
response = TOKEN_USER.testCreateToken(accessTokenUrl, user, password);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
@ -279,6 +286,202 @@ public class ITAccessTokenEndpoint {
|
||||
Assert.assertEquals("ACTIVE", accessStatus.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogOutSuccess() throws Exception {
|
||||
String accessStatusUrl = BASE_URL + "/access";
|
||||
String accessTokenUrl = BASE_URL + "/access/token";
|
||||
String logoutUrl = BASE_URL + "/access/logout";
|
||||
|
||||
Response response = TOKEN_USER.testGet(accessStatusUrl);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
AccessStatusEntity accessStatusEntity = response.readEntity(AccessStatusEntity.class);
|
||||
AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus();
|
||||
|
||||
// verify unknown
|
||||
Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
|
||||
|
||||
response = TOKEN_USER.testCreateToken(accessTokenUrl, user, password);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
|
||||
// get the token
|
||||
String token = response.readEntity(String.class);
|
||||
|
||||
// authorization header
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "Bearer " + token);
|
||||
|
||||
// check the status with the token
|
||||
response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
accessStatusEntity = response.readEntity(AccessStatusEntity.class);
|
||||
accessStatus = accessStatusEntity.getAccessStatus();
|
||||
|
||||
// verify unregistered
|
||||
Assert.assertEquals("ACTIVE", accessStatus.getStatus());
|
||||
|
||||
|
||||
// log out
|
||||
response = TOKEN_USER.testGetWithHeaders(logoutUrl, null, headers);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
// ensure we can no longer use our token
|
||||
response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogOutNoTokenHeader() throws Exception {
|
||||
String accessStatusUrl = BASE_URL + "/access";
|
||||
String accessTokenUrl = BASE_URL + "/access/token";
|
||||
String logoutUrl = BASE_URL + "/access/logout";
|
||||
|
||||
Response response = TOKEN_USER.testGet(accessStatusUrl);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
AccessStatusEntity accessStatusEntity = response.readEntity(AccessStatusEntity.class);
|
||||
AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus();
|
||||
|
||||
// verify unknown
|
||||
Assert.assertEquals("UNKNOWN", accessStatus.getStatus());
|
||||
|
||||
response = TOKEN_USER.testCreateToken(accessTokenUrl, user, password);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
|
||||
// get the token
|
||||
String token = response.readEntity(String.class);
|
||||
|
||||
// authorization header
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "Bearer " + token);
|
||||
|
||||
// check the status with the token
|
||||
response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
accessStatusEntity = response.readEntity(AccessStatusEntity.class);
|
||||
accessStatus = accessStatusEntity.getAccessStatus();
|
||||
|
||||
// verify unregistered
|
||||
Assert.assertEquals("ACTIVE", accessStatus.getStatus());
|
||||
|
||||
|
||||
// log out should fail as we provided no token for logout to use
|
||||
response = TOKEN_USER.testGetWithHeaders(logoutUrl, null, null);
|
||||
Assert.assertEquals(500, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogOutUnknownToken() throws Exception {
|
||||
// Arrange
|
||||
final String ALG_HEADER = "{\"alg\":\"HS256\"}";
|
||||
final int EXPIRATION_SECONDS = 60;
|
||||
Calendar now = Calendar.getInstance();
|
||||
final long currentTime = (long) (now.getTimeInMillis() / 1000.0);
|
||||
final long TOKEN_ISSUED_AT = currentTime;
|
||||
final long TOKEN_EXPIRATION_SECONDS = currentTime + EXPIRATION_SECONDS;
|
||||
|
||||
// Always use LinkedHashMap to enforce order of the keys because the signature depends on order
|
||||
Map<String, Object> claims = new LinkedHashMap<>();
|
||||
claims.put("sub", "unknownuser");
|
||||
claims.put("iss", "MockIdentityProvider");
|
||||
claims.put("aud", "MockIdentityProvider");
|
||||
claims.put("preferred_username", "unknownuser");
|
||||
claims.put("kid", 1);
|
||||
claims.put("exp", TOKEN_EXPIRATION_SECONDS);
|
||||
claims.put("iat", TOKEN_ISSUED_AT);
|
||||
final String EXPECTED_PAYLOAD = new JSONObject(claims).toString();
|
||||
|
||||
String accessStatusUrl = BASE_URL + "/access";
|
||||
String accessTokenUrl = BASE_URL + "/access/token";
|
||||
String logoutUrl = BASE_URL + "/access/logout";
|
||||
|
||||
Response response = TOKEN_USER.testCreateToken(accessTokenUrl, user, password);
|
||||
Response responseA = TOKEN_USER.testCreateToken(accessTokenUrl, "jack", password);
|
||||
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
// get the token
|
||||
String token = response.readEntity(String.class);
|
||||
// authorization header
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "Bearer " + token);
|
||||
// check the status with the token
|
||||
response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
|
||||
// Generate a token that will not match signatures with the generated token.
|
||||
final String UNKNOWN_USER_TOKEN = JwtServiceTest.generateHS256Token(ALG_HEADER, EXPECTED_PAYLOAD, true, true);
|
||||
Map<String, String> badHeaders = new HashMap<>();
|
||||
badHeaders.put("Authorization", "Bearer " + UNKNOWN_USER_TOKEN);
|
||||
|
||||
// Log out should fail as we provide a bad token to use, signatures will mismatch
|
||||
response = TOKEN_USER.testGetWithHeaders(logoutUrl, null, badHeaders);
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogOutSplicedTokenSignature() throws Exception {
|
||||
// Arrange
|
||||
final String ALG_HEADER = "{\"alg\":\"HS256\"}";
|
||||
final int EXPIRATION_SECONDS = 60;
|
||||
Calendar now = Calendar.getInstance();
|
||||
final long currentTime = (long) (now.getTimeInMillis() / 1000.0);
|
||||
final long TOKEN_ISSUED_AT = currentTime;
|
||||
final long TOKEN_EXPIRATION_SECONDS = currentTime + EXPIRATION_SECONDS;
|
||||
|
||||
String accessTokenUrl = BASE_URL + "/access/token";
|
||||
String logoutUrl = BASE_URL + "/access/logout";
|
||||
|
||||
Response response = TOKEN_USER.testCreateToken(accessTokenUrl, user, password);
|
||||
// ensure the request is successful
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
// replace the user in the token with an unknown user
|
||||
String realToken = response.readEntity(String.class);
|
||||
String realSignature = realToken.split("\\.")[2];
|
||||
|
||||
// Generate a token that we will add a valid signature from a different token
|
||||
// Always use LinkedHashMap to enforce order of the keys because the signature depends on order
|
||||
Map<String, Object> claims = new LinkedHashMap<>();
|
||||
claims.put("sub", "unknownuser");
|
||||
claims.put("iss", "MockIdentityProvider");
|
||||
claims.put("aud", "MockIdentityProvider");
|
||||
claims.put("preferred_username", "unknownuser");
|
||||
claims.put("kid", 1);
|
||||
claims.put("exp", TOKEN_EXPIRATION_SECONDS);
|
||||
claims.put("iat", TOKEN_ISSUED_AT);
|
||||
final String EXPECTED_PAYLOAD = new JSONObject(claims).toString();
|
||||
final String tempToken = JwtServiceTest.generateHS256Token(ALG_HEADER, EXPECTED_PAYLOAD, true, true);
|
||||
|
||||
// Splice this token with the real token from above
|
||||
String[] splitToken = tempToken.split("\\.");
|
||||
StringJoiner joiner = new StringJoiner(".");
|
||||
joiner.add(splitToken[0]);
|
||||
joiner.add(splitToken[1]);
|
||||
joiner.add(realSignature);
|
||||
String splicedUserToken = joiner.toString();
|
||||
|
||||
Map<String, String> badHeaders = new HashMap<>();
|
||||
badHeaders.put("Authorization", "Bearer " + splicedUserToken);
|
||||
|
||||
// Log out should fail as we provide a bad token to use, signatures will mismatch
|
||||
response = TOKEN_USER.testGetWithHeaders(logoutUrl, null, badHeaders);
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanup() throws Exception {
|
||||
// shutdown the server
|
||||
|
@ -31,6 +31,18 @@
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>jaxb2-maven-plugin</artifactId>
|
||||
|
@ -27,16 +27,16 @@ import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.SigningKeyResolverAdapter;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Calendar;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.admin.service.AdministrationException;
|
||||
import org.apache.nifi.admin.service.KeyService;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
import org.apache.nifi.key.Key;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -76,7 +76,19 @@ public class JwtService {
|
||||
} catch (JwtException e) {
|
||||
logger.debug("The Base64 encoded JWT: " + base64EncodedToken);
|
||||
final String errorMessage = "There was an error validating the JWT";
|
||||
logger.error(errorMessage, e);
|
||||
|
||||
// A common attack is someone trying to use a token after the user is logged out
|
||||
// No need to show a stacktrace for an expected and handled scenario
|
||||
String causeMessage = e.getLocalizedMessage();
|
||||
if (e.getCause() != null) {
|
||||
causeMessage += "\n\tCaused by: " + e.getCause().getLocalizedMessage();
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.error(errorMessage, e);
|
||||
} else {
|
||||
logger.error(errorMessage);
|
||||
logger.error(causeMessage);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@ -157,4 +169,19 @@ public class JwtService {
|
||||
throw new JwtException(errorMessage, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void logOut(String authorizationHeader) {
|
||||
if (authorizationHeader == null || authorizationHeader.isEmpty()) {
|
||||
throw new JwtException("Log out failed: The required Authorization header was not present in the request to log out user.");
|
||||
}
|
||||
|
||||
String identity = NiFiUserUtils.getNiFiUserIdentity();
|
||||
|
||||
try {
|
||||
keyService.deleteKey(identity);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to log out user: " + identity + ". Failed to remove their token from database.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,16 +21,24 @@ import org.apache.commons.codec.CharEncoding;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.nifi.admin.service.AdministrationException;
|
||||
import org.apache.nifi.admin.service.KeyService;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.key.Key;
|
||||
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
|
||||
import org.codehaus.jettison.json.JSONObject;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@ -44,6 +52,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class JwtServiceTest {
|
||||
@ -136,11 +146,11 @@ public class JwtServiceTest {
|
||||
// Class under test
|
||||
private JwtService jwtService;
|
||||
|
||||
private String generateHS256Token(String rawHeader, String rawPayload, boolean isValid, boolean isSigned) {
|
||||
public static String generateHS256Token(String rawHeader, String rawPayload, boolean isValid, boolean isSigned) {
|
||||
return generateHS256Token(rawHeader, rawPayload, HMAC_SECRET, isValid, isSigned);
|
||||
}
|
||||
|
||||
private String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid,
|
||||
private static String generateHS256Token(String rawHeader, String rawPayload, String hmacSecret, boolean isValid,
|
||||
boolean isSigned) {
|
||||
try {
|
||||
logger.info("Generating token for " + rawHeader + " + " + rawPayload);
|
||||
@ -162,7 +172,7 @@ public class JwtServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
private String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException,
|
||||
private static String generateHMAC(String hmacSecret, String body) throws NoSuchAlgorithmException,
|
||||
UnsupportedEncodingException, InvalidKeyException {
|
||||
Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secret_key = new SecretKeySpec(hmacSecret.getBytes("UTF-8"), "HmacSHA256");
|
||||
@ -177,15 +187,38 @@ public class JwtServiceTest {
|
||||
key.setIdentity(DEFAULT_IDENTITY);
|
||||
key.setKey(HMAC_SECRET);
|
||||
|
||||
mockKeyService = Mockito.mock(KeyService.class);
|
||||
when(mockKeyService.getKey(anyInt())).thenReturn(key);
|
||||
Answer<Key> keyAnswer = new Answer<Key>() {
|
||||
Key answerKey = key;
|
||||
@Override
|
||||
public Key answer(InvocationOnMock invocation) throws Throwable {
|
||||
if(invocation.getMethod().equals(KeyService.class.getMethod("deleteKey", String.class))) {
|
||||
answerKey = null;
|
||||
}
|
||||
return answerKey;
|
||||
}
|
||||
};
|
||||
|
||||
StandardNiFiUser nifiUser = mock(StandardNiFiUser.class);
|
||||
when(nifiUser.getIdentity()).thenReturn(DEFAULT_IDENTITY);
|
||||
NiFiUserDetails nifiUserDetails = mock(NiFiUserDetails.class);
|
||||
when(nifiUserDetails.getNiFiUser()).thenReturn(nifiUser);
|
||||
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
when(securityContext.getAuthentication()).thenReturn(authentication);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(nifiUserDetails);
|
||||
|
||||
mockKeyService = mock(KeyService.class);
|
||||
when(mockKeyService.getKey(anyInt())).thenAnswer(keyAnswer);
|
||||
when(mockKeyService.getOrCreateKey(anyString())).thenReturn(key);
|
||||
doAnswer(keyAnswer).when(mockKeyService).deleteKey(anyString());
|
||||
jwtService = new JwtService(mockKeyService);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
|
||||
jwtService = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -425,13 +458,13 @@ public class JwtServiceTest {
|
||||
public void testShouldNotGenerateTokenWithMissingKey() throws Exception {
|
||||
// Arrange
|
||||
final int EXPIRATION_MILLIS = 60000;
|
||||
LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken("alopresto",
|
||||
LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(DEFAULT_IDENTITY,
|
||||
EXPIRATION_MILLIS,
|
||||
"MockIdentityProvider");
|
||||
logger.debug("Generating token for " + loginAuthenticationToken);
|
||||
|
||||
// Set up the bad key service
|
||||
KeyService missingKeyService = Mockito.mock(KeyService.class);
|
||||
KeyService missingKeyService = mock(KeyService.class);
|
||||
when(missingKeyService.getOrCreateKey(anyString())).thenThrow(new AdministrationException("Could not find a "
|
||||
+ "key for that user"));
|
||||
jwtService = new JwtService(missingKeyService);
|
||||
@ -442,4 +475,50 @@ public class JwtServiceTest {
|
||||
// Assert
|
||||
// Should throw exception
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testShouldLogOutUser() throws Exception {
|
||||
|
||||
// Arrange
|
||||
expectedException.expect(JwtException.class);
|
||||
expectedException.expectMessage("Unable to validate the access token.");
|
||||
|
||||
// Token expires in 60 seconds
|
||||
final int EXPIRATION_MILLIS = 60000;
|
||||
LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(DEFAULT_IDENTITY,
|
||||
EXPIRATION_MILLIS,
|
||||
"MockIdentityProvider");
|
||||
logger.debug("Generating token for " + loginAuthenticationToken);
|
||||
|
||||
// Act
|
||||
String token = jwtService.generateSignedToken(loginAuthenticationToken);
|
||||
logger.debug("Generated JWT: " + token);
|
||||
String authID = jwtService.getAuthenticationFromToken(token);
|
||||
assertEquals(DEFAULT_IDENTITY, authID);
|
||||
logger.debug("Logging out user: " + DEFAULT_IDENTITY);
|
||||
jwtService.logOut(token);
|
||||
logger.debug("Logged out user: " + DEFAULT_IDENTITY);
|
||||
jwtService.getAuthenticationFromToken(token);
|
||||
|
||||
// Assert
|
||||
// Should throw exception when user is not found
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogoutWhenAuthTokenIsEmptyShouldThrowError() throws Exception {
|
||||
// Arrange
|
||||
expectedException.expect(JwtException.class);
|
||||
expectedException.expectMessage("Log out failed: The required Authorization header was not present in the request to log out user.");
|
||||
|
||||
// Act
|
||||
jwtService.logOut(null);
|
||||
|
||||
// Assert
|
||||
// Should throw exception when authorization header is null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -117,6 +117,11 @@
|
||||
*/
|
||||
this.logoutCtrl = {
|
||||
logout: function () {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '../nifi-api/access/logout',
|
||||
dataType: 'json'
|
||||
})
|
||||
nfStorage.removeItem("jwt");
|
||||
window.location = '../nifi/logout';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user