mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-01 14:41:46 +00:00
ACS- 4752 keycloak migration - resource server role (#1818)
This commit is contained in:
@@ -25,6 +25,8 @@
|
||||
*/
|
||||
package org.alfresco;
|
||||
|
||||
import org.alfresco.repo.security.authentication.identityservice.LazyInstantiatingIdentityServiceFacadeUnitTest;
|
||||
import org.alfresco.repo.security.authentication.identityservice.SpringBasedIdentityServiceFacadeUnitTest;
|
||||
import org.alfresco.util.testing.category.DBTests;
|
||||
import org.alfresco.util.testing.category.NonBuildTests;
|
||||
import org.junit.experimental.categories.Categories;
|
||||
@@ -136,7 +138,8 @@ import org.junit.runners.Suite;
|
||||
org.alfresco.repo.search.impl.solr.facet.FacetQNameUtilsTest.class,
|
||||
org.alfresco.util.BeanExtenderUnitTest.class,
|
||||
org.alfresco.repo.solr.SOLRTrackingComponentUnitTest.class,
|
||||
org.alfresco.repo.security.authentication.identityservice.SpringOAuth2ClientUnitTest.class,
|
||||
LazyInstantiatingIdentityServiceFacadeUnitTest.class,
|
||||
SpringBasedIdentityServiceFacadeUnitTest.class,
|
||||
org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class,
|
||||
org.alfresco.repo.security.authentication.PasswordHashingTest.class,
|
||||
org.alfresco.repo.security.authority.script.ScriptAuthorityService_RegExTest.class,
|
||||
|
@@ -34,8 +34,7 @@ import java.net.ConnectException;
|
||||
import org.alfresco.error.ExceptionStackUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceAuthenticationComponent.OAuth2Client;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceAuthenticationComponent.OAuth2Client.CredentialsVerificationException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.CredentialsVerificationException;
|
||||
import org.alfresco.repo.security.sync.UserRegistrySynchronizer;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
@@ -65,7 +64,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
@Autowired
|
||||
private PersonService personService;
|
||||
|
||||
private OAuth2Client mockOAuth2Client;
|
||||
private IdentityServiceFacade mockIdentityServiceFacade;
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
@@ -76,8 +75,8 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
authComponent.setNodeService(nodeService);
|
||||
authComponent.setPersonService(personService);
|
||||
|
||||
mockOAuth2Client = mock(OAuth2Client.class);
|
||||
authComponent.setOAuth2Client(mockOAuth2Client);
|
||||
mockIdentityServiceFacade = mock(IdentityServiceFacade.class);
|
||||
authComponent.setIdentityServiceFacade(mockIdentityServiceFacade);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -90,7 +89,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
public void testAuthenticationFail()
|
||||
{
|
||||
doThrow(new CredentialsVerificationException("Failed"))
|
||||
.when(mockOAuth2Client)
|
||||
.when(mockIdentityServiceFacade)
|
||||
.verifyCredentials("username", "password");
|
||||
|
||||
authComponent.authenticateImpl("username", "password".toCharArray());
|
||||
@@ -100,7 +99,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
public void testAuthenticationFail_connectionException()
|
||||
{
|
||||
doThrow(new CredentialsVerificationException("Couldn't connect to server", new ConnectException("ConnectionRefused")))
|
||||
.when(mockOAuth2Client)
|
||||
.when(mockIdentityServiceFacade)
|
||||
.verifyCredentials("username", "password");
|
||||
|
||||
try
|
||||
@@ -119,7 +118,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
public void testAuthenticationFail_otherException()
|
||||
{
|
||||
doThrow(new RuntimeException("Some other errors!"))
|
||||
.when(mockOAuth2Client)
|
||||
.when(mockIdentityServiceFacade)
|
||||
.verifyCredentials("username", "password");
|
||||
|
||||
authComponent.authenticateImpl("username", "password".toCharArray());
|
||||
@@ -128,7 +127,7 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
@Test
|
||||
public void testAuthenticationPass()
|
||||
{
|
||||
doNothing().when(mockOAuth2Client).verifyCredentials("username", "password");
|
||||
doNothing().when(mockIdentityServiceFacade).verifyCredentials("username", "password");
|
||||
|
||||
authComponent.authenticateImpl("username", "password".toCharArray());
|
||||
|
||||
@@ -137,9 +136,9 @@ public class IdentityServiceAuthenticationComponentTest extends BaseSpringTest
|
||||
}
|
||||
|
||||
@Test (expected= AuthenticationException.class)
|
||||
public void testFallthroughWhenOAuth2ClientIsNull()
|
||||
public void testFallthroughWhenIdentityServiceFacadeIsNull()
|
||||
{
|
||||
authComponent.setOAuth2Client(null);
|
||||
authComponent.setIdentityServiceFacade(null);
|
||||
authComponent.authenticateImpl("username", "password".toCharArray());
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -25,377 +25,89 @@
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication.identityservice;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.alfresco.repo.management.subsystems.AbstractChainedSubsystemTest;
|
||||
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
|
||||
import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
|
||||
import junit.framework.TestCase;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
|
||||
import org.keycloak.common.util.Base64;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenException;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||
|
||||
/**
|
||||
* Tests the Identity Service based authentication subsystem.
|
||||
*
|
||||
* @author Gavin Cornwell
|
||||
*/
|
||||
public class IdentityServiceRemoteUserMapperTest extends AbstractChainedSubsystemTest
|
||||
public class IdentityServiceRemoteUserMapperTest extends TestCase
|
||||
{
|
||||
private static final String REMOTE_USER_MAPPER_BEAN_NAME = "remoteUserMapper";
|
||||
private static final String DEPLOYMENT_BEAN_NAME = "identityServiceDeployment";
|
||||
private static final String CONFIG_BEAN_NAME = "identityServiceConfig";
|
||||
|
||||
private static final String TEST_USER_USERNAME = "testuser";
|
||||
private static final String TEST_USER_EMAIL = "testuser@mail.com";
|
||||
|
||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
private static final String BASIC_PREFIX = "Basic ";
|
||||
|
||||
private static final String CONFIG_SILENT_ERRORS = "identity-service.authentication.validation.failure.silent";
|
||||
|
||||
private static final String PASSWORD_GRANT_RESPONSE = "{" +
|
||||
"\"access_token\": \"%s\"," +
|
||||
"\"expires_in\": 300," +
|
||||
"\"refresh_expires_in\": 1800," +
|
||||
"\"refresh_token\": \"%s\"," +
|
||||
"\"token_type\": \"bearer\"," +
|
||||
"\"not-before-policy\": 0," +
|
||||
"\"session_state\": \"71c2c5ba-9c98-49fc-882f-dedcf80ee1b5\"}";
|
||||
|
||||
ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
DefaultChildApplicationContextManager childApplicationContextManager;
|
||||
ChildApplicationContextFactory childApplicationContextFactory;
|
||||
|
||||
private KeyPair keyPair;
|
||||
private IdentityServiceConfig identityServiceConfig;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
public void testValidToken()
|
||||
{
|
||||
// switch authentication to use token auth
|
||||
childApplicationContextManager = (DefaultChildApplicationContextManager) ctx.getBean("Authentication");
|
||||
childApplicationContextManager.stop();
|
||||
childApplicationContextManager.setProperty("chain", "identity-service1:identity-service");
|
||||
childApplicationContextFactory = getChildApplicationContextFactory(childApplicationContextManager, "identity-service1");
|
||||
|
||||
// generate keys for test
|
||||
this.keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
|
||||
// hardcode the realm public key in the deployment bean to stop it fetching keys
|
||||
applyHardcodedPublicKey(this.keyPair.getPublic());
|
||||
|
||||
// extract config
|
||||
this.identityServiceConfig = (IdentityServiceConfig)childApplicationContextFactory.
|
||||
getApplicationContext().getBean(CONFIG_BEAN_NAME);
|
||||
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("VaLiD-ToKeN", () -> "johny"));
|
||||
|
||||
HttpServletRequest mockRequest = createMockTokenRequest("VaLiD-ToKeN");
|
||||
|
||||
final String user = mapper.getRemoteUser(mockRequest);
|
||||
assertEquals("johny", user);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
public void testWrongTokenWithSilentValidation()
|
||||
{
|
||||
childApplicationContextManager.destroy();
|
||||
childApplicationContextManager = null;
|
||||
childApplicationContextFactory = null;
|
||||
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {throw new TokenException("Expected ");}));
|
||||
mapper.setValidationFailureSilent(true);
|
||||
|
||||
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
||||
|
||||
final String user = mapper.getRemoteUser(mockRequest);
|
||||
assertNull(user);
|
||||
}
|
||||
|
||||
public void testKeycloakConfig() throws Exception
|
||||
public void testWrongTokenWithoutSilentValidation()
|
||||
{
|
||||
//Get the host of the IDS test server
|
||||
String ip = "localhost";
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface iface = interfaces.nextElement();
|
||||
// filters out 127.0.0.1 and inactive interfaces
|
||||
if (iface.isLoopback() || !iface.isUp())
|
||||
continue;
|
||||
final IdentityServiceRemoteUserMapper mapper = givenMapper(Map.of("WrOnG-ToKeN", () -> {throw new TokenException("Expected");}));
|
||||
mapper.setValidationFailureSilent(false);
|
||||
|
||||
Enumeration<InetAddress> addresses = iface.getInetAddresses();
|
||||
while(addresses.hasMoreElements()) {
|
||||
InetAddress addr = addresses.nextElement();
|
||||
if(Pattern.matches("([0-9]{1,3}\\.){3}[0-9]{1,3}", addr.getHostAddress())){
|
||||
ip = addr.getHostAddress();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
HttpServletRequest mockRequest = createMockTokenRequest("WrOnG-ToKeN");
|
||||
|
||||
// check string overrides
|
||||
assertEquals("identity-service.auth-server-url", "http://"+ip+":8999/auth",
|
||||
this.identityServiceConfig.getAuthServerUrl());
|
||||
|
||||
assertEquals("identity-service.realm", "alfresco",
|
||||
this.identityServiceConfig.getRealm());
|
||||
assertThatExceptionOfType(AuthenticationException.class)
|
||||
.isThrownBy(() -> mapper.getRemoteUser(mockRequest))
|
||||
.havingCause().withNoCause().withMessage("Expected");
|
||||
}
|
||||
|
||||
assertEquals("identity-service.realm-public-key",
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWLQxipXNe6cLnVPGy7l" +
|
||||
"BgyR51bDiK7Jso8Rmh2TB+bmO4fNaMY1ETsxECSM0f6NTV0QHks9+gBe+pB6JNeM" +
|
||||
"uPmaE/M/MsE9KUif9L2ChFq3zor6s2foFv2DTiTkij+1aQF9fuIjDNH4FC6L252W" +
|
||||
"ydZzh+f73Xuy5evdPj+wrPYqWyP7sKd+4Q9EIILWAuTDvKEjwyZmIyfM/nUn6ltD" +
|
||||
"P6W8xMP0PoEJNAAp79anz2jk2HP2PvC2qdjVsphdTk3JG5qQMB0WJUh4Kjgabd4j" +
|
||||
"QJ77U8gTRswKgNHRRPWhruiIcmmkP+zI0ozNW6rxH3PF4L7M9rXmfcmUcBcKf+Yx" +
|
||||
"jwIDAQAB",
|
||||
this.identityServiceConfig.getRealmKey());
|
||||
|
||||
assertEquals("identity-service.ssl-required", "external",
|
||||
this.identityServiceConfig.getSslRequired());
|
||||
|
||||
assertEquals("identity-service.resource", "test",
|
||||
this.identityServiceConfig.getResource());
|
||||
|
||||
assertEquals("identity-service.cors-allowed-headers", "Authorization",
|
||||
this.identityServiceConfig.getCorsAllowedHeaders());
|
||||
|
||||
assertEquals("identity-service.cors-allowed-methods", "POST, PUT, DELETE, GET",
|
||||
this.identityServiceConfig.getCorsAllowedMethods());
|
||||
|
||||
assertEquals("identity-service.cors-exposed-headers", "WWW-Authenticate, My-custom-exposed-Header",
|
||||
this.identityServiceConfig.getCorsExposedHeaders());
|
||||
|
||||
assertEquals("identity-service.truststore",
|
||||
"classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks",
|
||||
this.identityServiceConfig.getTruststore());
|
||||
|
||||
assertEquals("identity-service.truststore-password", "password",
|
||||
this.identityServiceConfig.getTruststorePassword());
|
||||
|
||||
assertEquals("identity-service.client-keystore",
|
||||
"classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks",
|
||||
this.identityServiceConfig.getClientKeystore());
|
||||
|
||||
assertEquals("identity-service.client-keystore-password", "password",
|
||||
this.identityServiceConfig.getClientKeystorePassword());
|
||||
|
||||
assertEquals("identity-service.client-key-password", "password",
|
||||
this.identityServiceConfig.getClientKeyPassword());
|
||||
|
||||
assertEquals("identity-service.token-store", "SESSION",
|
||||
this.identityServiceConfig.getTokenStore());
|
||||
|
||||
assertEquals("identity-service.principal-attribute", "preferred_username",
|
||||
this.identityServiceConfig.getPrincipalAttribute());
|
||||
|
||||
// check number overrides
|
||||
assertEquals("identity-service.confidential-port", 100,
|
||||
this.identityServiceConfig.getConfidentialPort());
|
||||
|
||||
assertEquals("identity-service.cors-max-age", 1000,
|
||||
this.identityServiceConfig.getCorsMaxAge());
|
||||
|
||||
assertEquals("identity-service.connection-pool-size", 5,
|
||||
this.identityServiceConfig.getConnectionPoolSize());
|
||||
|
||||
assertEquals("identity-service.register-node-period", 50,
|
||||
this.identityServiceConfig.getRegisterNodePeriod());
|
||||
|
||||
assertEquals("identity-service.token-minimum-time-to-live", 10,
|
||||
this.identityServiceConfig.getTokenMinimumTimeToLive());
|
||||
|
||||
assertEquals("identity-service.min-time-between-jwks-requests", 60,
|
||||
this.identityServiceConfig.getMinTimeBetweenJwksRequests());
|
||||
|
||||
assertEquals("identity-service.public-key-cache-ttl", 3600,
|
||||
this.identityServiceConfig.getPublicKeyCacheTtl());
|
||||
private IdentityServiceRemoteUserMapper givenMapper(Map<String, Supplier<String>> tokenToUser)
|
||||
{
|
||||
final IdentityServiceFacade facade = mock(IdentityServiceFacade.class);
|
||||
when(facade.extractUsernameFromToken(anyString()))
|
||||
.thenAnswer(i ->
|
||||
ofNullable(tokenToUser.get(i.getArgument(0, String.class)))
|
||||
.map(Supplier::get));
|
||||
|
||||
assertEquals("identity-service.client-connection-timeout", 3000,
|
||||
this.identityServiceConfig.getClientConnectionTimeout());
|
||||
final PersonService personService = mock(PersonService.class);
|
||||
when(personService.getUserIdentifier(anyString())).thenAnswer(i -> i.getArgument(0, String.class));
|
||||
|
||||
assertEquals("identity-service.client-socket-timeout", 1000,
|
||||
this.identityServiceConfig.getClientSocketTimeout());
|
||||
final IdentityServiceRemoteUserMapper mapper = new IdentityServiceRemoteUserMapper();
|
||||
mapper.setIdentityServiceFacade(facade);
|
||||
mapper.setPersonService(personService);
|
||||
mapper.setActive(true);
|
||||
mapper.setBearerTokenResolver(new DefaultBearerTokenResolver());
|
||||
|
||||
// check boolean overrides
|
||||
assertFalse("identity-service.public-client",
|
||||
this.identityServiceConfig.isPublicClient());
|
||||
|
||||
assertTrue("identity-service.use-resource-role-mappings",
|
||||
this.identityServiceConfig.isUseResourceRoleMappings());
|
||||
|
||||
assertTrue("identity-service.enable-cors",
|
||||
this.identityServiceConfig.isCors());
|
||||
|
||||
assertTrue("identity-service.expose-token",
|
||||
this.identityServiceConfig.isExposeToken());
|
||||
|
||||
assertTrue("identity-service.bearer-only",
|
||||
this.identityServiceConfig.isBearerOnly());
|
||||
|
||||
assertTrue("identity-service.autodetect-bearer-only",
|
||||
this.identityServiceConfig.isAutodetectBearerOnly());
|
||||
|
||||
assertTrue("identity-service.enable-basic-auth",
|
||||
this.identityServiceConfig.isEnableBasicAuth());
|
||||
|
||||
assertTrue("identity-service.allow-any-hostname",
|
||||
this.identityServiceConfig.isAllowAnyHostname());
|
||||
|
||||
assertTrue("identity-service.disable-trust-manager",
|
||||
this.identityServiceConfig.isDisableTrustManager());
|
||||
|
||||
assertTrue("identity-service.always-refresh-token",
|
||||
this.identityServiceConfig.isAlwaysRefreshToken());
|
||||
|
||||
assertTrue("identity-service.register-node-at-startup",
|
||||
this.identityServiceConfig.isRegisterNodeAtStartup());
|
||||
|
||||
assertTrue("identity-service.enable-pkce",
|
||||
this.identityServiceConfig.isPkce());
|
||||
|
||||
assertTrue("identity-service.ignore-oauth-query-parameter",
|
||||
this.identityServiceConfig.isIgnoreOAuthQueryParameter());
|
||||
|
||||
assertTrue("identity-service.turn-off-change-session-id-on-login",
|
||||
this.identityServiceConfig.getTurnOffChangeSessionIdOnLogin());
|
||||
|
||||
// check credentials overrides
|
||||
Map<String, Object> credentials = this.identityServiceConfig.getCredentials();
|
||||
assertNotNull("Expected a credentials map", credentials);
|
||||
assertFalse("Expected to retrieve a populated credentials map", credentials.isEmpty());
|
||||
assertEquals("identity-service.credentials.secret", "11111", credentials.get("secret"));
|
||||
assertEquals("identity-service.credentials.provider", "secret", credentials.get("provider"));
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public void testValidToken() throws Exception
|
||||
{
|
||||
// create token
|
||||
String jwt = generateToken(false);
|
||||
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(jwt);
|
||||
|
||||
// validate correct user was found
|
||||
assertEquals(TEST_USER_USERNAME, ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
public void testWrongPublicKey() throws Exception
|
||||
{
|
||||
// generate and apply an incorrect public key
|
||||
childApplicationContextFactory.stop();
|
||||
applyHardcodedPublicKey(KeyPairGenerator.getInstance("RSA").generateKeyPair().getPublic());
|
||||
|
||||
// create token
|
||||
String jwt = generateToken(false);
|
||||
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(jwt);
|
||||
|
||||
// ensure null is returned if the public key is wrong
|
||||
assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
public void testWrongPublicKeyWithError() throws Exception
|
||||
{
|
||||
// generate and apply an incorrect public key
|
||||
childApplicationContextFactory.stop();
|
||||
childApplicationContextFactory.setProperty(CONFIG_SILENT_ERRORS, "false");
|
||||
applyHardcodedPublicKey(KeyPairGenerator.getInstance("RSA").generateKeyPair().getPublic());
|
||||
|
||||
// create token
|
||||
String jwt = generateToken(false);
|
||||
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(jwt);
|
||||
|
||||
// ensure user mapper falls through instead of throwing an exception
|
||||
String user = ((RemoteUserMapper)childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest);
|
||||
assertEquals("Returned user should be null when wrong public key is used.", null, user);
|
||||
}
|
||||
|
||||
public void testInvalidJwt() throws Exception
|
||||
{
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest("thisisnotaJWT");
|
||||
|
||||
// ensure null is returned if the JWT is invalid
|
||||
assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
public void testMissingToken() throws Exception
|
||||
{
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest("");
|
||||
|
||||
// ensure null is returned if the token is missing
|
||||
assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
public void testExpiredToken() throws Exception
|
||||
{
|
||||
// create token
|
||||
String jwt = generateToken(true);
|
||||
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(jwt);
|
||||
|
||||
// ensure null is returned if the token has expired
|
||||
assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
public void testExpiredTokenWithError() throws Exception
|
||||
{
|
||||
// turn on validation failure reporting
|
||||
childApplicationContextFactory.stop();
|
||||
childApplicationContextFactory.setProperty(CONFIG_SILENT_ERRORS, "false");
|
||||
applyHardcodedPublicKey(this.keyPair.getPublic());
|
||||
|
||||
// create token
|
||||
String jwt = generateToken(true);
|
||||
|
||||
// create mock request object
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(jwt);
|
||||
|
||||
// ensure an exception is thrown with correct description
|
||||
String user = ((RemoteUserMapper)childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest);
|
||||
assertEquals("Returned user should be null when the token is expired.", null, user);
|
||||
}
|
||||
|
||||
public void testMissingHeader() throws Exception
|
||||
{
|
||||
// create mock request object with no Authorization header
|
||||
HttpServletRequest mockRequest = createMockTokenRequest(null);
|
||||
|
||||
// ensure null is returned if the header was missing
|
||||
assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
|
||||
REMOTE_USER_MAPPER_BEAN_NAME)).getRemoteUser(mockRequest));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility method for creating a mocked Servlet request with a token.
|
||||
*
|
||||
@@ -412,99 +124,12 @@ public class IdentityServiceRemoteUserMapperTest extends AbstractChainedSubsyste
|
||||
{
|
||||
authHeaderValues.add(BEARER_PREFIX + token);
|
||||
}
|
||||
|
||||
when(mockRequest.getHeaders(AUTHORIZATION_HEADER)).thenReturn(authHeaderValues.elements());
|
||||
|
||||
return mockRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for creating a mocked Servlet request with basic auth.
|
||||
*
|
||||
* @return The mocked request object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private HttpServletRequest createMockBasicRequest()
|
||||
{
|
||||
// Mock a request with the token in the Authorization header (if supplied)
|
||||
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
|
||||
|
||||
Vector<String> authHeaderValues = new Vector<>(1);
|
||||
String userPwd = TEST_USER_USERNAME + ":" + TEST_USER_USERNAME;
|
||||
authHeaderValues.add(BASIC_PREFIX + Base64.encodeBytes(userPwd.getBytes()));
|
||||
|
||||
// NOTE: as getHeaders gets called twice provide two separate Enumeration objects so that
|
||||
// an empty result is not returned for the second invocation.
|
||||
when(mockRequest.getHeaders(AUTHORIZATION_HEADER)).thenReturn(authHeaderValues.elements(),
|
||||
authHeaderValues.elements());
|
||||
|
||||
return mockRequest;
|
||||
}
|
||||
|
||||
private HttpClient createMockHttpClient() throws Exception
|
||||
{
|
||||
// mock HttpClient object and set on keycloak deployment to avoid basic auth
|
||||
// attempting to get a token using HTTP POST
|
||||
HttpClient mockHttpClient = mock(HttpClient.class);
|
||||
HttpResponse mockHttpResponse = mock(HttpResponse.class);
|
||||
StatusLine mockStatusLine = mock(StatusLine.class);
|
||||
HttpEntity mockHttpEntity = mock(HttpEntity.class);
|
||||
|
||||
// for the purpose of this test use the same token for access and refresh
|
||||
String token = generateToken(false);
|
||||
String jsonResponse = String.format(PASSWORD_GRANT_RESPONSE, token, token);
|
||||
ByteArrayInputStream jsonResponseStream = new ByteArrayInputStream(jsonResponse.getBytes());
|
||||
|
||||
when(mockHttpClient.execute(any())).thenReturn(mockHttpResponse);
|
||||
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
|
||||
when(mockHttpResponse.getEntity()).thenReturn(mockHttpEntity);
|
||||
when(mockStatusLine.getStatusCode()).thenReturn(200);
|
||||
when(mockHttpEntity.getContent()).thenReturn(jsonResponseStream);
|
||||
|
||||
return mockHttpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to create tokens for testing.
|
||||
*
|
||||
* @param expired Determines whether to create an expired JWT
|
||||
* @return The string representation of the JWT
|
||||
*/
|
||||
private String generateToken(boolean expired) throws Exception
|
||||
{
|
||||
String issuerUrl = this.identityServiceConfig.getAuthServerUrl() + "/realms/" + this.identityServiceConfig.getRealm();
|
||||
|
||||
AccessToken token = new AccessToken();
|
||||
token.type("Bearer");
|
||||
token.id("1234");
|
||||
token.subject("abc123");
|
||||
token.issuer(issuerUrl);
|
||||
token.setPreferredUsername(TEST_USER_USERNAME);
|
||||
token.setEmail(TEST_USER_EMAIL);
|
||||
token.setGivenName("Joe");
|
||||
token.setFamilyName("Bloggs");
|
||||
|
||||
if (expired)
|
||||
{
|
||||
token.expiration(Time.currentTime() - 60);
|
||||
}
|
||||
|
||||
String jwt = new JWSBuilder()
|
||||
.jsonContent(token)
|
||||
.rsa256(keyPair.getPrivate());
|
||||
when(mockRequest.getHeaders(AUTHORIZATION_HEADER))
|
||||
.thenReturn(authHeaderValues.elements());
|
||||
when(mockRequest.getHeader(AUTHORIZATION_HEADER))
|
||||
.thenReturn(authHeaderValues.isEmpty() ? null : authHeaderValues.get(0));
|
||||
|
||||
return jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the keycloak deployment bean and applies a hardcoded public key locator using the
|
||||
* provided public key.
|
||||
*/
|
||||
private void applyHardcodedPublicKey(PublicKey publicKey)
|
||||
{
|
||||
KeycloakDeployment deployment = (KeycloakDeployment)childApplicationContextFactory.getApplicationContext().
|
||||
getBean(DEPLOYMENT_BEAN_NAME);
|
||||
HardcodedPublicKeyLocator publicKeyLocator = new HardcodedPublicKeyLocator(publicKey);
|
||||
deployment.setPublicKeyLocator(publicKeyLocator);
|
||||
return mockRequest;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication.identityservice;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.LazyInstantiatingIdentityServiceFacade;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LazyInstantiatingIdentityServiceFacadeUnitTest
|
||||
{
|
||||
private static final String USER_NAME = "marlon";
|
||||
private static final String PASSWORD = "brando";
|
||||
private static final String TOKEN = "token";
|
||||
@Test
|
||||
public void shouldRecoverFromInitialAuthorizationServerUnavailability()
|
||||
{
|
||||
final IdentityServiceFacade targetFacade = mock(IdentityServiceFacade.class);
|
||||
final LazyInstantiatingIdentityServiceFacade facade = new LazyInstantiatingIdentityServiceFacade(faultySupplier(3, targetFacade));
|
||||
|
||||
assertThatExceptionOfType(IdentityServiceFacadeException.class)
|
||||
.isThrownBy(() -> facade.extractUsernameFromToken(TOKEN))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #1");
|
||||
verifyNoInteractions(targetFacade);
|
||||
|
||||
assertThatExceptionOfType(IdentityServiceFacadeException.class)
|
||||
.isThrownBy(() -> facade.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #2");
|
||||
verifyNoInteractions(targetFacade);
|
||||
|
||||
assertThatExceptionOfType(IdentityServiceFacadeException.class)
|
||||
.isThrownBy(() -> facade.extractUsernameFromToken(TOKEN))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #3");
|
||||
verifyNoInteractions(targetFacade);
|
||||
|
||||
facade.verifyCredentials(USER_NAME, PASSWORD);
|
||||
verify(targetFacade).verifyCredentials(USER_NAME, PASSWORD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAvoidCreatingMultipleInstanceOfOAuth2AuthorizedClientManager()
|
||||
{
|
||||
final IdentityServiceFacade targetFacade = mock(IdentityServiceFacade.class);
|
||||
final Supplier<IdentityServiceFacade> supplier = mock(Supplier.class);
|
||||
when(supplier.get()).thenReturn(targetFacade);
|
||||
|
||||
final LazyInstantiatingIdentityServiceFacade facade = new LazyInstantiatingIdentityServiceFacade(supplier);
|
||||
|
||||
facade.verifyCredentials(USER_NAME, PASSWORD);
|
||||
facade.extractUsernameFromToken(TOKEN);
|
||||
facade.verifyCredentials(USER_NAME, PASSWORD);
|
||||
facade.extractUsernameFromToken(TOKEN);
|
||||
facade.verifyCredentials(USER_NAME, PASSWORD);
|
||||
verify(supplier, times(1)).get();
|
||||
verify(targetFacade, times(3)).verifyCredentials(USER_NAME, PASSWORD);
|
||||
verify(targetFacade, times(2)).extractUsernameFromToken(TOKEN);
|
||||
}
|
||||
|
||||
private Supplier<IdentityServiceFacade> faultySupplier(int numberOfInitialFailures, IdentityServiceFacade facade)
|
||||
{
|
||||
final int[] counter = new int[]{0};
|
||||
return () -> {
|
||||
if (counter[0]++ < numberOfInitialFailures)
|
||||
{
|
||||
throw new RuntimeException("Expected failure #" + counter[0]);
|
||||
}
|
||||
return facade;
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication.identityservice;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.CredentialsVerificationException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.TokenException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.SpringBasedIdentityServiceFacade;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
|
||||
public class SpringBasedIdentityServiceFacadeUnitTest
|
||||
{
|
||||
private static final String USER_NAME = "user";
|
||||
private static final String PASSWORD = "password";
|
||||
private static final String TOKEN = "tEsT-tOkEn";
|
||||
|
||||
@Test
|
||||
public void shouldThrowVerificationExceptionOnFailure()
|
||||
{
|
||||
final OAuth2AuthorizedClientManager authClientManager = mock(OAuth2AuthorizedClientManager.class);
|
||||
final JwtDecoder jwtDecoder = mock(JwtDecoder.class);
|
||||
when(authClientManager.authorize(any())).thenThrow(new RuntimeException("Expected"));
|
||||
|
||||
final SpringBasedIdentityServiceFacade facade = new SpringBasedIdentityServiceFacade(authClientManager, jwtDecoder);
|
||||
|
||||
assertThatExceptionOfType(CredentialsVerificationException.class)
|
||||
.isThrownBy(() -> facade.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowTokenExceptionOnFailure()
|
||||
{
|
||||
final OAuth2AuthorizedClientManager authClientManager = mock(OAuth2AuthorizedClientManager.class);
|
||||
final JwtDecoder jwtDecoder = mock(JwtDecoder.class);
|
||||
when(jwtDecoder.decode(TOKEN)).thenThrow(new RuntimeException("Expected"));
|
||||
|
||||
final SpringBasedIdentityServiceFacade facade = new SpringBasedIdentityServiceFacade(authClientManager, jwtDecoder);
|
||||
|
||||
assertThatExceptionOfType(TokenException.class)
|
||||
.isThrownBy(() -> facade.extractUsernameFromToken(TOKEN))
|
||||
.havingCause().withNoCause().withMessage("Expected");
|
||||
}
|
||||
}
|
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2023 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.security.authentication.identityservice;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceAuthenticationComponent.OAuth2Client.CredentialsVerificationException;
|
||||
import org.alfresco.repo.security.authentication.identityservice.OAuth2ClientFactoryBean.SpringOAuth2Client;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
||||
public class SpringOAuth2ClientUnitTest
|
||||
{
|
||||
private static final String USER_NAME = "user";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
@Test
|
||||
public void shouldRecoverFromInitialAuthorizationServerUnavailability()
|
||||
{
|
||||
final OAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);
|
||||
when(authorizedClient.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
|
||||
final OAuth2AuthorizedClientManager authClientManager = mock(OAuth2AuthorizedClientManager.class);
|
||||
when(authClientManager.authorize(any())).thenReturn(authorizedClient);
|
||||
|
||||
final SpringOAuth2Client client = new SpringOAuth2Client(faultySupplier(3, authClientManager));
|
||||
|
||||
assertThatExceptionOfType(CredentialsVerificationException.class)
|
||||
.isThrownBy(() -> client.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #1");
|
||||
verifyNoInteractions(authClientManager);
|
||||
|
||||
assertThatExceptionOfType(CredentialsVerificationException.class)
|
||||
.isThrownBy(() -> client.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #2");
|
||||
verifyNoInteractions(authClientManager);
|
||||
|
||||
assertThatExceptionOfType(CredentialsVerificationException.class)
|
||||
.isThrownBy(() -> client.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected failure #3");
|
||||
verifyNoInteractions(authClientManager);
|
||||
|
||||
client.verifyCredentials(USER_NAME, PASSWORD);
|
||||
verify(authClientManager).authorize(argThat(r -> r.getPrincipal() != null && USER_NAME.equals(r.getPrincipal().getPrincipal())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowVerificationExceptionOnFailure()
|
||||
{
|
||||
final OAuth2AuthorizedClientManager authClientManager = mock(OAuth2AuthorizedClientManager.class);
|
||||
when(authClientManager.authorize(any())).thenThrow(new RuntimeException("Expected"));
|
||||
|
||||
final SpringOAuth2Client client = new SpringOAuth2Client(() -> authClientManager);
|
||||
|
||||
assertThatExceptionOfType(CredentialsVerificationException.class)
|
||||
.isThrownBy(() -> client.verifyCredentials(USER_NAME, PASSWORD))
|
||||
.havingCause().withNoCause().withMessage("Expected");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAvoidCreatingMultipleInstanceOfOAuth2AuthorizedClientManager()
|
||||
{
|
||||
final OAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class);
|
||||
when(authorizedClient.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
|
||||
final OAuth2AuthorizedClientManager authClientManager = mock(OAuth2AuthorizedClientManager.class);
|
||||
when(authClientManager.authorize(any())).thenReturn(authorizedClient);
|
||||
final Supplier<OAuth2AuthorizedClientManager> supplier = mock(Supplier.class);
|
||||
when(supplier.get()).thenReturn(authClientManager);
|
||||
|
||||
final SpringOAuth2Client client = new SpringOAuth2Client(supplier);
|
||||
|
||||
client.verifyCredentials(USER_NAME, PASSWORD);
|
||||
client.verifyCredentials(USER_NAME, PASSWORD);
|
||||
client.verifyCredentials(USER_NAME, PASSWORD);
|
||||
verify(supplier, times(1)).get();
|
||||
verify(authClientManager, times(3)).authorize(any());
|
||||
}
|
||||
|
||||
private Supplier<OAuth2AuthorizedClientManager> faultySupplier(int numberOfInitialFailures, OAuth2AuthorizedClientManager authClientManager)
|
||||
{
|
||||
final int[] counter = new int[]{0};
|
||||
return () -> {
|
||||
if (counter[0]++ < numberOfInitialFailures)
|
||||
{
|
||||
throw new RuntimeException("Expected failure #" + counter[0]);
|
||||
}
|
||||
return authClientManager;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user