AUTH-87/AUTH-85: Move SSO token implementation back to Alfresco Community

Changed references to "token authentication" and "keycloak" to "identity-service", where appropriate.
This commit is contained in:
Gavin Cornwell
2018-03-16 17:18:47 +00:00
parent 6b7b449938
commit 8b1c112d61
13 changed files with 221 additions and 214 deletions

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import org.keycloak.adapters.BearerTokenRequestAuthenticator; import org.keycloak.adapters.BearerTokenRequestAuthenticator;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@@ -34,12 +34,17 @@ import org.apache.commons.logging.LogFactory;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
public class AlfrescoKeycloakAdapterConfig extends AdapterConfig implements InitializingBean /**
* Class to hold configuration for the Identity Service.
*
* @author Gavin Cornwell
*/
public class IdentityServiceConfig extends AdapterConfig implements InitializingBean
{ {
private static Log logger = LogFactory.getLog(AlfrescoKeycloakAdapterConfig.class); private static Log logger = LogFactory.getLog(IdentityServiceConfig.class);
private static final String CREDENTIALS_SECRET = "keycloak.credentials.secret"; private static final String CREDENTIALS_SECRET = "identity-service.credentials.secret";
private static final String CREDENTIALS_PROVIDER = "keycloak.credentials.provider"; private static final String CREDENTIALS_PROVIDER = "identity-service.credentials.provider";
private Properties globalProperties; private Properties globalProperties;

View File

@@ -23,35 +23,34 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
/** /**
* Creates an instance of a KeycloakDeployment object. * Creates an instance of a KeycloakDeployment object for communicating with the Identity Service.
* *
* @author Gavin Cornwell * @author Gavin Cornwell
*/ */
public class AlfrescoKeycloakDeploymentFactoryBean implements FactoryBean<KeycloakDeployment> public class IdentityServiceDeploymentFactoryBean implements FactoryBean<KeycloakDeployment>
{ {
private static Log logger = LogFactory.getLog(AlfrescoKeycloakDeploymentFactoryBean.class); private static Log logger = LogFactory.getLog(IdentityServiceDeploymentFactoryBean.class);
private AdapterConfig keycloakAdapterConfig; private IdentityServiceConfig identityServiceConfig;
public void setAdapterConfig(AdapterConfig adapterConfig) public void setIdentityServiceConfig(IdentityServiceConfig config)
{ {
this.keycloakAdapterConfig = adapterConfig; this.identityServiceConfig = config;
} }
@Override @Override
public KeycloakDeployment getObject() throws Exception public KeycloakDeployment getObject() throws Exception
{ {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(this.keycloakAdapterConfig); KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(this.identityServiceConfig);
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
{ {

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -33,13 +33,13 @@ import javax.servlet.http.HttpServletRequest;
import org.keycloak.adapters.servlet.ServletHttpFacade; import org.keycloak.adapters.servlet.ServletHttpFacade;
/** /**
* Keycloak HttpFacade wrapper so we can re-use Keycloak authenticator classes. * HttpFacade wrapper so we can re-use Keycloak authenticator classes.
* *
* @author Gavin Cornwell * @author Gavin Cornwell
*/ */
public class AlfrescoKeycloakHttpFacade extends ServletHttpFacade public class IdentityServiceHttpFacade extends ServletHttpFacade
{ {
public AlfrescoKeycloakHttpFacade(HttpServletRequest request) public IdentityServiceHttpFacade(HttpServletRequest request)
{ {
super(request, null); super(request, null);
} }

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@@ -41,13 +41,14 @@ import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
/** /**
* A {@link RemoteUserMapper} implementation that detects and validates JWTs. * A {@link RemoteUserMapper} implementation that detects and validates JWTs
* issued by the Alfresco Identity Service.
* *
* @author Gavin Cornwell * @author Gavin Cornwell
*/ */
public class TokenRemoteUserMapper implements RemoteUserMapper, ActivateableBean public class IdentityServiceRemoteUserMapper implements RemoteUserMapper, ActivateableBean
{ {
private static Log logger = LogFactory.getLog(TokenRemoteUserMapper.class); private static Log logger = LogFactory.getLog(IdentityServiceRemoteUserMapper.class);
/** Is the mapper enabled */ /** Is the mapper enabled */
private boolean isEnabled; private boolean isEnabled;
@@ -92,7 +93,7 @@ public class TokenRemoteUserMapper implements RemoteUserMapper, ActivateableBean
this.personService = personService; this.personService = personService;
} }
public void setKeycloakDeployment(KeycloakDeployment deployment) public void setIdentityServiceDeployment(KeycloakDeployment deployment)
{ {
this.keycloakDeployment = deployment; this.keycloakDeployment = deployment;
} }
@@ -149,7 +150,7 @@ public class TokenRemoteUserMapper implements RemoteUserMapper, ActivateableBean
{ {
String userName = null; String userName = null;
AlfrescoKeycloakHttpFacade facade = new AlfrescoKeycloakHttpFacade(request); IdentityServiceHttpFacade facade = new IdentityServiceHttpFacade(request);
// try authenticating with bearer token first // try authenticating with bearer token first
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())

View File

@@ -70,139 +70,139 @@
</property> </property>
</bean> </bean>
<bean name="keycloakAdpapterConfig" class="org.alfresco.repo.security.authentication.token.AlfrescoKeycloakAdapterConfig"> <bean name="identityServiceConfig" class="org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig">
<property name="globalProperties"> <property name="globalProperties">
<ref bean="global-properties" /> <ref bean="global-properties" />
</property> </property>
<property name="realm"> <property name="realm">
<value>${keycloak.realm}</value> <value>${identity-service.realm}</value>
</property> </property>
<property name="realmKey"> <property name="realmKey">
<value>${keycloak.realm-public-key:#{null}}</value> <value>${identity-service.realm-public-key:#{null}}</value>
</property> </property>
<property name="authServerUrl"> <property name="authServerUrl">
<value>${keycloak.auth-server-url}</value> <value>${identity-service.auth-server-url}</value>
</property> </property>
<property name="sslRequired"> <property name="sslRequired">
<value>${keycloak.ssl-required:external}</value> <value>${identity-service.ssl-required:external}</value>
</property> </property>
<property name="confidentialPort"> <property name="confidentialPort">
<value>${keycloak.confidential-port:0}</value> <value>${identity-service.confidential-port:0}</value>
</property> </property>
<property name="resource"> <property name="resource">
<value>${keycloak.resource}</value> <value>${identity-service.resource}</value>
</property> </property>
<property name="useResourceRoleMappings"> <property name="useResourceRoleMappings">
<value>${keycloak.use-resource-role-mappings:false}</value> <value>${identity-service.use-resource-role-mappings:false}</value>
</property> </property>
<property name="cors"> <property name="cors">
<value>${keycloak.enable-cors:false}</value> <value>${identity-service.enable-cors:false}</value>
</property> </property>
<property name="corsMaxAge"> <property name="corsMaxAge">
<value>${keycloak.cors-max-age:-1}</value> <value>${identity-service.cors-max-age:-1}</value>
</property> </property>
<property name="corsAllowedHeaders"> <property name="corsAllowedHeaders">
<value>${keycloak.cors-allowed-headers:#{null}}</value> <value>${identity-service.cors-allowed-headers:#{null}}</value>
</property> </property>
<property name="corsAllowedMethods"> <property name="corsAllowedMethods">
<value>${keycloak.cors-allowed-methods:#{null}}</value> <value>${identity-service.cors-allowed-methods:#{null}}</value>
</property> </property>
<property name="corsExposedHeaders"> <property name="corsExposedHeaders">
<value>${keycloak.cors-exposed-headers:#{null}}</value> <value>${identity-service.cors-exposed-headers:#{null}}</value>
</property> </property>
<property name="exposeToken"> <property name="exposeToken">
<value>${keycloak.expose-token:false}</value> <value>${identity-service.expose-token:false}</value>
</property> </property>
<property name="bearerOnly"> <property name="bearerOnly">
<value>${keycloak.bearer-only:false}</value> <value>${identity-service.bearer-only:false}</value>
</property> </property>
<property name="autodetectBearerOnly"> <property name="autodetectBearerOnly">
<value>${keycloak.autodetect-bearer-only:false}</value> <value>${identity-service.autodetect-bearer-only:false}</value>
</property> </property>
<property name="enableBasicAuth"> <property name="enableBasicAuth">
<value>${keycloak.enable-basic-auth:false}</value> <value>${identity-service.enable-basic-auth:false}</value>
</property> </property>
<property name="publicClient"> <property name="publicClient">
<value>${keycloak.public-client:false}</value> <value>${identity-service.public-client:false}</value>
</property> </property>
<property name="allowAnyHostname"> <property name="allowAnyHostname">
<value>${keycloak.allow-any-hostname:false}</value> <value>${identity-service.allow-any-hostname:false}</value>
</property> </property>
<property name="disableTrustManager"> <property name="disableTrustManager">
<value>${keycloak.disable-trust-manager:false}</value> <value>${identity-service.disable-trust-manager:false}</value>
</property> </property>
<property name="truststore"> <property name="truststore">
<value>${keycloak.truststore:#{null}}</value> <value>${identity-service.truststore:#{null}}</value>
</property> </property>
<property name="truststorePassword"> <property name="truststorePassword">
<value>${keycloak.truststore-password:#{null}}</value> <value>${identity-service.truststore-password:#{null}}</value>
</property> </property>
<property name="clientKeystore"> <property name="clientKeystore">
<value>${keycloak.client-keystore:#{null}}</value> <value>${identity-service.client-keystore:#{null}}</value>
</property> </property>
<property name="clientKeystorePassword"> <property name="clientKeystorePassword">
<value>${keycloak.client-keystore-password:#{null}}</value> <value>${identity-service.client-keystore-password:#{null}}</value>
</property> </property>
<property name="clientKeyPassword"> <property name="clientKeyPassword">
<value>${keycloak.client-key-password:#{null}}</value> <value>${identity-service.client-key-password:#{null}}</value>
</property> </property>
<property name="connectionPoolSize"> <property name="connectionPoolSize">
<value>${keycloak.connection-pool-size:20}</value> <value>${identity-service.connection-pool-size:20}</value>
</property> </property>
<property name="alwaysRefreshToken"> <property name="alwaysRefreshToken">
<value>${keycloak.always-refresh-token:false}</value> <value>${identity-service.always-refresh-token:false}</value>
</property> </property>
<property name="registerNodeAtStartup"> <property name="registerNodeAtStartup">
<value>${keycloak.register-node-at-startup:false}</value> <value>${identity-service.register-node-at-startup:false}</value>
</property> </property>
<property name="registerNodePeriod"> <property name="registerNodePeriod">
<value>${keycloak.register-node-period:-1}</value> <value>${identity-service.register-node-period:-1}</value>
</property> </property>
<property name="tokenStore"> <property name="tokenStore">
<value>${keycloak.token-store:#{null}}</value> <value>${identity-service.token-store:#{null}}</value>
</property> </property>
<property name="principalAttribute"> <property name="principalAttribute">
<value>${keycloak.principal-attribute:#{null}}</value> <value>${identity-service.principal-attribute:#{null}}</value>
</property> </property>
<property name="turnOffChangeSessionIdOnLogin"> <property name="turnOffChangeSessionIdOnLogin">
<value>${keycloak.turn-off-change-session-id-on-login:false}</value> <value>${identity-service.turn-off-change-session-id-on-login:false}</value>
</property> </property>
<property name="tokenMinimumTimeToLive"> <property name="tokenMinimumTimeToLive">
<value>${keycloak.token-minimum-time-to-live:0}</value> <value>${identity-service.token-minimum-time-to-live:0}</value>
</property> </property>
<property name="minTimeBetweenJwksRequests"> <property name="minTimeBetweenJwksRequests">
<value>${keycloak.min-time-between-jwks-requests:10}</value> <value>${identity-service.min-time-between-jwks-requests:10}</value>
</property> </property>
<property name="publicKeyCacheTtl"> <property name="publicKeyCacheTtl">
<value>${keycloak.public-key-cache-ttl:86400}</value> <value>${identity-service.public-key-cache-ttl:86400}</value>
</property> </property>
<property name="pkce"> <property name="pkce">
<value>${keycloak.enable-pkce:false}</value> <value>${identity-service.enable-pkce:false}</value>
</property> </property>
<property name="ignoreOAuthQueryParameter"> <property name="ignoreOAuthQueryParameter">
<value>${keycloak.ignore-oauth-query-parameter:false}</value> <value>${identity-service.ignore-oauth-query-parameter:false}</value>
</property> </property>
</bean> </bean>
<bean name="keycloakDeployment" class="org.alfresco.repo.security.authentication.token.AlfrescoKeycloakDeploymentFactoryBean"> <bean name="identityServiceDeployment" class="org.alfresco.repo.security.authentication.identityservice.IdentityServiceDeploymentFactoryBean">
<property name="adapterConfig"> <property name="identityServiceConfig">
<ref bean="keycloakAdpapterConfig" /> <ref bean="identityServiceConfig" />
</property> </property>
</bean> </bean>
<!-- Enable control over mapping between request and user ID --> <!-- Enable control over mapping between request and user ID -->
<bean id="remoteUserMapper" class="org.alfresco.repo.security.authentication.token.TokenRemoteUserMapper"> <bean id="remoteUserMapper" class="org.alfresco.repo.security.authentication.identityservice.IdentityServiceRemoteUserMapper">
<property name="active"> <property name="active">
<value>${token.authentication.enabled}</value> <value>${identity-service.authentication.enabled}</value>
</property> </property>
<property name="validationFailureSilent"> <property name="validationFailureSilent">
<value>${token.authentication.validation.failure.silent}</value> <value>${identity-service.authentication.validation.failure.silent}</value>
</property> </property>
<property name="personService"> <property name="personService">
<ref bean="PersonService" /> <ref bean="PersonService" />
</property> </property>
<property name="keycloakDeployment"> <property name="identityServiceDeployment">
<ref bean="keycloakDeployment" /> <ref bean="identityServiceDeployment" />
</property> </property>
</bean> </bean>

View File

@@ -0,0 +1,10 @@
identity-service.authentication.enabled=true
identity-service.authentication.validation.failure.silent=true
identity-service.authentication.defaultAdministratorUserNames=admin
# Identity Service configuration
identity-service.auth-server-url=http://localhost:8180/auth
identity-service.realm=springboot
identity-service.ssl-required=none
identity-service.resource=activiti
identity-service.public-client=true

View File

@@ -1,10 +0,0 @@
token.authentication.enabled=true
token.authentication.validation.failure.silent=true
token.authentication.defaultAdministratorUserNames=admin
# Keycloak configuration
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.realm=springboot
keycloak.ssl-required=none
keycloak.resource=activiti
keycloak.public-client=true

View File

@@ -37,8 +37,8 @@ import org.alfresco.repo.security.authentication.ResetPasswordServiceImplTest;
import org.alfresco.repo.security.authentication.UpgradePasswordHashTest; import org.alfresco.repo.security.authentication.UpgradePasswordHashTest;
import org.alfresco.repo.security.authentication.external.DefaultRemoteUserMapperTest; import org.alfresco.repo.security.authentication.external.DefaultRemoteUserMapperTest;
import org.alfresco.repo.security.authentication.external.LocalAuthenticationServiceTest; import org.alfresco.repo.security.authentication.external.LocalAuthenticationServiceTest;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceRemoteUserMapperTest;
import org.alfresco.repo.security.authentication.subsystems.SubsystemChainingFtpAuthenticatorTest; import org.alfresco.repo.security.authentication.subsystems.SubsystemChainingFtpAuthenticatorTest;
import org.alfresco.repo.security.authentication.token.TokenRemoteUserMapperTest;
import org.alfresco.repo.security.authority.AuthorityBridgeTableAsynchronouslyRefreshedCacheTest; import org.alfresco.repo.security.authority.AuthorityBridgeTableAsynchronouslyRefreshedCacheTest;
import org.alfresco.repo.security.authority.AuthorityServiceTest; import org.alfresco.repo.security.authority.AuthorityServiceTest;
import org.alfresco.repo.security.authority.DuplicateAuthorityTest; import org.alfresco.repo.security.authority.DuplicateAuthorityTest;
@@ -101,7 +101,7 @@ public class SecurityTestSuite extends TestSuite
suite.addTestSuite(FixedAclUpdaterTest.class); suite.addTestSuite(FixedAclUpdaterTest.class);
suite.addTestSuite(DefaultRemoteUserMapperTest.class); suite.addTestSuite(DefaultRemoteUserMapperTest.class);
suite.addTestSuite(TokenRemoteUserMapperTest.class); suite.addTestSuite(IdentityServiceRemoteUserMapperTest.class);
suite.addTestSuite(SubsystemChainingFtpAuthenticatorTest.class); suite.addTestSuite(SubsystemChainingFtpAuthenticatorTest.class);
suite.addTest(new JUnit4TestAdapter(LocalAuthenticationServiceTest.class)); suite.addTest(new JUnit4TestAdapter(LocalAuthenticationServiceTest.class));

View File

@@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.authentication.token; package org.alfresco.repo.security.authentication.identityservice;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -43,6 +43,7 @@ import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager; import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.external.RemoteUserMapper; import org.alfresco.repo.security.authentication.external.RemoteUserMapper;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceConfig;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@@ -57,14 +58,15 @@ import org.keycloak.representations.AccessToken;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
/** /**
* Tests the token based authentication subsystem. * Tests the Identity Service based authentication subsystem.
* *
* @author Gavin Cornwell * @author Gavin Cornwell
*/ */
public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest public class IdentityServiceRemoteUserMapperTest extends AbstractChainedSubsystemTest
{ {
private static final String REMOTE_USER_MAPPER_BEAN_NAME = "remoteUserMapper"; private static final String REMOTE_USER_MAPPER_BEAN_NAME = "remoteUserMapper";
private static final String KEYCLOAK_DEPLOYMENT_BEAN_NAME = "keycloakDeployment"; 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_USERNAME = "testuser";
private static final String TEST_USER_EMAIL = "testuser@mail.com"; private static final String TEST_USER_EMAIL = "testuser@mail.com";
@@ -73,7 +75,7 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
private static final String BEARER_PREFIX = "Bearer "; private static final String BEARER_PREFIX = "Bearer ";
private static final String BASIC_PREFIX = "Basic "; private static final String BASIC_PREFIX = "Basic ";
private static final String CONFIG_SILENT_ERRORS = "token.authentication.validation.failure.silent"; private static final String CONFIG_SILENT_ERRORS = "identity-service.authentication.validation.failure.silent";
private static final String PASSWORD_GRANT_RESPONSE = "{" + private static final String PASSWORD_GRANT_RESPONSE = "{" +
"\"access_token\": \"%s\"," + "\"access_token\": \"%s\"," +
@@ -89,7 +91,7 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
ChildApplicationContextFactory childApplicationContextFactory; ChildApplicationContextFactory childApplicationContextFactory;
private KeyPair keyPair; private KeyPair keyPair;
private AlfrescoKeycloakAdapterConfig keycloakAdapterConfig; private IdentityServiceConfig identityServiceConfig;
/* (non-Javadoc) /* (non-Javadoc)
* @see junit.framework.TestCase#setUp() * @see junit.framework.TestCase#setUp()
@@ -100,18 +102,18 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
// switch authentication to use token auth // switch authentication to use token auth
childApplicationContextManager = (DefaultChildApplicationContextManager) ctx.getBean("Authentication"); childApplicationContextManager = (DefaultChildApplicationContextManager) ctx.getBean("Authentication");
childApplicationContextManager.stop(); childApplicationContextManager.stop();
childApplicationContextManager.setProperty("chain", "token1:token"); childApplicationContextManager.setProperty("chain", "identity-service1:identity-service");
childApplicationContextFactory = getChildApplicationContextFactory(childApplicationContextManager, "token1"); childApplicationContextFactory = getChildApplicationContextFactory(childApplicationContextManager, "identity-service1");
// generate keys for test // generate keys for test
this.keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); this.keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
// hardcode the realm public key in the Keycloak deployment bean to stop it fetching keys // hardcode the realm public key in the deployment bean to stop it fetching keys
applyHardcodedPublicKey(this.keyPair.getPublic()); applyHardcodedPublicKey(this.keyPair.getPublic());
// extract config // extract config
this.keycloakAdapterConfig = (AlfrescoKeycloakAdapterConfig)childApplicationContextFactory. this.identityServiceConfig = (IdentityServiceConfig)childApplicationContextFactory.
getApplicationContext().getBean("keycloakAdpapterConfig"); getApplicationContext().getBean(CONFIG_BEAN_NAME);
} }
/* (non-Javadoc) /* (non-Javadoc)
@@ -128,13 +130,13 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
public void testKeycloakConfig() throws Exception public void testKeycloakConfig() throws Exception
{ {
// check string overrides // check string overrides
assertEquals("keycloak.auth-server-url", "http://192.168.0.1:8180/auth", assertEquals("identity-service.auth-server-url", "http://192.168.0.1:8180/auth",
this.keycloakAdapterConfig.getAuthServerUrl()); this.identityServiceConfig.getAuthServerUrl());
assertEquals("keycloak.realm", "test", assertEquals("identity-service.realm", "test",
this.keycloakAdapterConfig.getRealm()); this.identityServiceConfig.getRealm());
assertEquals("keycloak.realm-public-key", assertEquals("identity-service.realm-public-key",
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWLQxipXNe6cLnVPGy7l" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWLQxipXNe6cLnVPGy7l" +
"BgyR51bDiK7Jso8Rmh2TB+bmO4fNaMY1ETsxECSM0f6NTV0QHks9+gBe+pB6JNeM" + "BgyR51bDiK7Jso8Rmh2TB+bmO4fNaMY1ETsxECSM0f6NTV0QHks9+gBe+pB6JNeM" +
"uPmaE/M/MsE9KUif9L2ChFq3zor6s2foFv2DTiTkij+1aQF9fuIjDNH4FC6L252W" + "uPmaE/M/MsE9KUif9L2ChFq3zor6s2foFv2DTiTkij+1aQF9fuIjDNH4FC6L252W" +
@@ -142,117 +144,117 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
"P6W8xMP0PoEJNAAp79anz2jk2HP2PvC2qdjVsphdTk3JG5qQMB0WJUh4Kjgabd4j" + "P6W8xMP0PoEJNAAp79anz2jk2HP2PvC2qdjVsphdTk3JG5qQMB0WJUh4Kjgabd4j" +
"QJ77U8gTRswKgNHRRPWhruiIcmmkP+zI0ozNW6rxH3PF4L7M9rXmfcmUcBcKf+Yx" + "QJ77U8gTRswKgNHRRPWhruiIcmmkP+zI0ozNW6rxH3PF4L7M9rXmfcmUcBcKf+Yx" +
"jwIDAQAB", "jwIDAQAB",
this.keycloakAdapterConfig.getRealmKey()); this.identityServiceConfig.getRealmKey());
assertEquals("keycloak.ssl-required", "external", assertEquals("identity-service.ssl-required", "external",
this.keycloakAdapterConfig.getSslRequired()); this.identityServiceConfig.getSslRequired());
assertEquals("keycloak.resource", "test", assertEquals("identity-service.resource", "test",
this.keycloakAdapterConfig.getResource()); this.identityServiceConfig.getResource());
assertEquals("keycloak.cors-allowed-headers", "Authorization", assertEquals("identity-service.cors-allowed-headers", "Authorization",
this.keycloakAdapterConfig.getCorsAllowedHeaders()); this.identityServiceConfig.getCorsAllowedHeaders());
assertEquals("keycloak.cors-allowed-methods", "POST, PUT, DELETE, GET", assertEquals("identity-service.cors-allowed-methods", "POST, PUT, DELETE, GET",
this.keycloakAdapterConfig.getCorsAllowedMethods()); this.identityServiceConfig.getCorsAllowedMethods());
assertEquals("keycloak.cors-exposed-headers", "WWW-Authenticate, My-custom-exposed-Header", assertEquals("identity-service.cors-exposed-headers", "WWW-Authenticate, My-custom-exposed-Header",
this.keycloakAdapterConfig.getCorsExposedHeaders()); this.identityServiceConfig.getCorsExposedHeaders());
assertEquals("keycloak.truststore", assertEquals("identity-service.truststore",
"classpath:/alfresco/subsystems/tokenAuthentication/keystore.jks", "classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks",
this.keycloakAdapterConfig.getTruststore()); this.identityServiceConfig.getTruststore());
assertEquals("keycloak.truststore-password", "password", assertEquals("identity-service.truststore-password", "password",
this.keycloakAdapterConfig.getTruststorePassword()); this.identityServiceConfig.getTruststorePassword());
assertEquals("keycloak.client-keystore", assertEquals("identity-service.client-keystore",
"classpath:/alfresco/subsystems/tokenAuthentication/keystore.jks", "classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks",
this.keycloakAdapterConfig.getClientKeystore()); this.identityServiceConfig.getClientKeystore());
assertEquals("keycloak.client-keystore-password", "password", assertEquals("identity-service.client-keystore-password", "password",
this.keycloakAdapterConfig.getClientKeystorePassword()); this.identityServiceConfig.getClientKeystorePassword());
assertEquals("keycloak.client-key-password", "password", assertEquals("identity-service.client-key-password", "password",
this.keycloakAdapterConfig.getClientKeyPassword()); this.identityServiceConfig.getClientKeyPassword());
assertEquals("keycloak.token-store", "SESSION", assertEquals("identity-service.token-store", "SESSION",
this.keycloakAdapterConfig.getTokenStore()); this.identityServiceConfig.getTokenStore());
assertEquals("keycloak.principal-attribute", "preferred_username", assertEquals("identity-service.principal-attribute", "preferred_username",
this.keycloakAdapterConfig.getPrincipalAttribute()); this.identityServiceConfig.getPrincipalAttribute());
// check number overrides // check number overrides
assertEquals("keycloak.confidential-port", 100, assertEquals("identity-service.confidential-port", 100,
this.keycloakAdapterConfig.getConfidentialPort()); this.identityServiceConfig.getConfidentialPort());
assertEquals("keycloak.cors-max-age", 1000, assertEquals("identity-service.cors-max-age", 1000,
this.keycloakAdapterConfig.getCorsMaxAge()); this.identityServiceConfig.getCorsMaxAge());
assertEquals("keycloak.connection-pool-size", 5, assertEquals("identity-service.connection-pool-size", 5,
this.keycloakAdapterConfig.getConnectionPoolSize()); this.identityServiceConfig.getConnectionPoolSize());
assertEquals("keycloak.register-node-period", 50, assertEquals("identity-service.register-node-period", 50,
this.keycloakAdapterConfig.getRegisterNodePeriod()); this.identityServiceConfig.getRegisterNodePeriod());
assertEquals("keycloak.token-minimum-time-to-live", 10, assertEquals("identity-service.token-minimum-time-to-live", 10,
this.keycloakAdapterConfig.getTokenMinimumTimeToLive()); this.identityServiceConfig.getTokenMinimumTimeToLive());
assertEquals("keycloak.min-time-between-jwks-requests", 60, assertEquals("identity-service.min-time-between-jwks-requests", 60,
this.keycloakAdapterConfig.getMinTimeBetweenJwksRequests()); this.identityServiceConfig.getMinTimeBetweenJwksRequests());
assertEquals("keycloak.public-key-cache-ttl", 3600, assertEquals("identity-service.public-key-cache-ttl", 3600,
this.keycloakAdapterConfig.getPublicKeyCacheTtl()); this.identityServiceConfig.getPublicKeyCacheTtl());
// check boolean overrides // check boolean overrides
assertFalse("keycloak.public-client", assertFalse("identity-service.public-client",
this.keycloakAdapterConfig.isPublicClient()); this.identityServiceConfig.isPublicClient());
assertTrue("keycloak.use-resource-role-mappings", assertTrue("identity-service.use-resource-role-mappings",
this.keycloakAdapterConfig.isUseResourceRoleMappings()); this.identityServiceConfig.isUseResourceRoleMappings());
assertTrue("keycloak.enable-cors", assertTrue("identity-service.enable-cors",
this.keycloakAdapterConfig.isCors()); this.identityServiceConfig.isCors());
assertTrue("keycloak.expose-token", assertTrue("identity-service.expose-token",
this.keycloakAdapterConfig.isExposeToken()); this.identityServiceConfig.isExposeToken());
assertTrue("keycloak.bearer-only", assertTrue("identity-service.bearer-only",
this.keycloakAdapterConfig.isBearerOnly()); this.identityServiceConfig.isBearerOnly());
assertTrue("keycloak.autodetect-bearer-only", assertTrue("identity-service.autodetect-bearer-only",
this.keycloakAdapterConfig.isAutodetectBearerOnly()); this.identityServiceConfig.isAutodetectBearerOnly());
assertTrue("keycloak.enable-basic-auth", assertTrue("identity-service.enable-basic-auth",
this.keycloakAdapterConfig.isEnableBasicAuth()); this.identityServiceConfig.isEnableBasicAuth());
assertTrue("keycloak.allow-any-hostname", assertTrue("identity-service.allow-any-hostname",
this.keycloakAdapterConfig.isAllowAnyHostname()); this.identityServiceConfig.isAllowAnyHostname());
assertTrue("keycloak.disable-trust-manager", assertTrue("identity-service.disable-trust-manager",
this.keycloakAdapterConfig.isDisableTrustManager()); this.identityServiceConfig.isDisableTrustManager());
assertTrue("keycloak.always-refresh-token", assertTrue("identity-service.always-refresh-token",
this.keycloakAdapterConfig.isAlwaysRefreshToken()); this.identityServiceConfig.isAlwaysRefreshToken());
assertTrue("keycloak.register-node-at-startup", assertTrue("identity-service.register-node-at-startup",
this.keycloakAdapterConfig.isRegisterNodeAtStartup()); this.identityServiceConfig.isRegisterNodeAtStartup());
assertTrue("keycloak.enable-pkce", assertTrue("identity-service.enable-pkce",
this.keycloakAdapterConfig.isPkce()); this.identityServiceConfig.isPkce());
assertTrue("keycloak.ignore-oauth-query-parameter", assertTrue("identity-service.ignore-oauth-query-parameter",
this.keycloakAdapterConfig.isIgnoreOAuthQueryParameter()); this.identityServiceConfig.isIgnoreOAuthQueryParameter());
assertTrue("keycloak.turn-off-change-session-id-on-login", assertTrue("identity-service.turn-off-change-session-id-on-login",
this.keycloakAdapterConfig.getTurnOffChangeSessionIdOnLogin()); this.identityServiceConfig.getTurnOffChangeSessionIdOnLogin());
// check credentials overrides // check credentials overrides
Map<String, Object> credentials = this.keycloakAdapterConfig.getCredentials(); Map<String, Object> credentials = this.identityServiceConfig.getCredentials();
assertNotNull("Expected a credentials map", credentials); assertNotNull("Expected a credentials map", credentials);
assertFalse("Expected to retrieve a populated credentials map", credentials.isEmpty()); assertFalse("Expected to retrieve a populated credentials map", credentials.isEmpty());
assertEquals("keycloak.credentials.secret", "11111", credentials.get("secret")); assertEquals("identity-service.credentials.secret", "11111", credentials.get("secret"));
assertEquals("keycloak.credentials.provider", "secret", credentials.get("provider")); assertEquals("identity-service.credentials.provider", "secret", credentials.get("provider"));
} }
public void testValidToken() throws Exception public void testValidToken() throws Exception
@@ -390,7 +392,7 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
// override the http client on the keycloak deployment // override the http client on the keycloak deployment
KeycloakDeployment deployment = (KeycloakDeployment)childApplicationContextFactory.getApplicationContext(). KeycloakDeployment deployment = (KeycloakDeployment)childApplicationContextFactory.getApplicationContext().
getBean(KEYCLOAK_DEPLOYMENT_BEAN_NAME); getBean(DEPLOYMENT_BEAN_NAME);
deployment.setClient(mockHttpClient); deployment.setClient(mockHttpClient);
// validate correct user was found // validate correct user was found
@@ -474,7 +476,7 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
*/ */
private String generateToken(boolean expired) throws Exception private String generateToken(boolean expired) throws Exception
{ {
String issuerUrl = this.keycloakAdapterConfig.getAuthServerUrl() + "/realms/" + this.keycloakAdapterConfig.getRealm(); String issuerUrl = this.identityServiceConfig.getAuthServerUrl() + "/realms/" + this.identityServiceConfig.getRealm();
AccessToken token = new AccessToken(); AccessToken token = new AccessToken();
token.type("Bearer"); token.type("Bearer");
@@ -505,7 +507,7 @@ public class TokenRemoteUserMapperTest extends AbstractChainedSubsystemTest
private void applyHardcodedPublicKey(PublicKey publicKey) private void applyHardcodedPublicKey(PublicKey publicKey)
{ {
KeycloakDeployment deployment = (KeycloakDeployment)childApplicationContextFactory.getApplicationContext(). KeycloakDeployment deployment = (KeycloakDeployment)childApplicationContextFactory.getApplicationContext().
getBean(KEYCLOAK_DEPLOYMENT_BEAN_NAME); getBean(DEPLOYMENT_BEAN_NAME);
HardcodedPublicKeyLocator publicKeyLocator = new HardcodedPublicKeyLocator(publicKey); HardcodedPublicKeyLocator publicKeyLocator = new HardcodedPublicKeyLocator(publicKey);
deployment.setPublicKeyLocator(publicKeyLocator); deployment.setPublicKeyLocator(publicKeyLocator);
} }

View File

@@ -1,45 +1,45 @@
# Test token authentication overrides # Test identity service authentication overrides
keycloak.auth-server-url=http://192.168.0.1:8180/auth identity-service.auth-server-url=http://192.168.0.1:8180/auth
keycloak.realm=test identity-service.realm=test
keycloak.realm-public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWLQxipXNe6cLnVPGy7l\ identity-service.realm-public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWLQxipXNe6cLnVPGy7l\
BgyR51bDiK7Jso8Rmh2TB+bmO4fNaMY1ETsxECSM0f6NTV0QHks9+gBe+pB6JNeM\ BgyR51bDiK7Jso8Rmh2TB+bmO4fNaMY1ETsxECSM0f6NTV0QHks9+gBe+pB6JNeM\
uPmaE/M/MsE9KUif9L2ChFq3zor6s2foFv2DTiTkij+1aQF9fuIjDNH4FC6L252W\ uPmaE/M/MsE9KUif9L2ChFq3zor6s2foFv2DTiTkij+1aQF9fuIjDNH4FC6L252W\
ydZzh+f73Xuy5evdPj+wrPYqWyP7sKd+4Q9EIILWAuTDvKEjwyZmIyfM/nUn6ltD\ ydZzh+f73Xuy5evdPj+wrPYqWyP7sKd+4Q9EIILWAuTDvKEjwyZmIyfM/nUn6ltD\
P6W8xMP0PoEJNAAp79anz2jk2HP2PvC2qdjVsphdTk3JG5qQMB0WJUh4Kjgabd4j\ P6W8xMP0PoEJNAAp79anz2jk2HP2PvC2qdjVsphdTk3JG5qQMB0WJUh4Kjgabd4j\
QJ77U8gTRswKgNHRRPWhruiIcmmkP+zI0ozNW6rxH3PF4L7M9rXmfcmUcBcKf+Yx\ QJ77U8gTRswKgNHRRPWhruiIcmmkP+zI0ozNW6rxH3PF4L7M9rXmfcmUcBcKf+Yx\
jwIDAQAB jwIDAQAB
keycloak.ssl-required=external identity-service.ssl-required=external
keycloak.resource=test identity-service.resource=test
keycloak.public-client=false identity-service.public-client=false
keycloak.confidential-port=100 identity-service.confidential-port=100
keycloak.use-resource-role-mappings=true identity-service.use-resource-role-mappings=true
keycloak.enable-cors=true identity-service.enable-cors=true
keycloak.cors-max-age=1000 identity-service.cors-max-age=1000
keycloak.cors-allowed-headers=Authorization identity-service.cors-allowed-headers=Authorization
keycloak.cors-allowed-methods=POST, PUT, DELETE, GET identity-service.cors-allowed-methods=POST, PUT, DELETE, GET
keycloak.cors-exposed-headers=WWW-Authenticate, My-custom-exposed-Header identity-service.cors-exposed-headers=WWW-Authenticate, My-custom-exposed-Header
keycloak.expose-token=true identity-service.expose-token=true
keycloak.bearer-only=true identity-service.bearer-only=true
keycloak.autodetect-bearer-only=true identity-service.autodetect-bearer-only=true
keycloak.enable-basic-auth=true identity-service.enable-basic-auth=true
keycloak.allow-any-hostname=true identity-service.allow-any-hostname=true
keycloak.disable-trust-manager=true identity-service.disable-trust-manager=true
keycloak.truststore=classpath:/alfresco/subsystems/tokenAuthentication/keystore.jks identity-service.truststore=classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks
keycloak.truststore-password=password identity-service.truststore-password=password
keycloak.client-keystore=classpath:/alfresco/subsystems/tokenAuthentication/keystore.jks identity-service.client-keystore=classpath:/alfresco/subsystems/identityServiceAuthentication/keystore.jks
keycloak.client-keystore-password=password identity-service.client-keystore-password=password
keycloak.client-key-password=password identity-service.client-key-password=password
keycloak.connection-pool-size=5 identity-service.connection-pool-size=5
keycloak.always-refresh-token=true identity-service.always-refresh-token=true
keycloak.register-node-at-startup=true identity-service.register-node-at-startup=true
keycloak.register-node-period=50 identity-service.register-node-period=50
keycloak.token-store=SESSION identity-service.token-store=SESSION
keycloak.principal-attribute=preferred_username identity-service.principal-attribute=preferred_username
keycloak.turn-off-change-session-id-on-login=true identity-service.turn-off-change-session-id-on-login=true
keycloak.token-minimum-time-to-live=10 identity-service.token-minimum-time-to-live=10
keycloak.min-time-between-jwks-requests=60 identity-service.min-time-between-jwks-requests=60
keycloak.public-key-cache-ttl=3600 identity-service.public-key-cache-ttl=3600
keycloak.enable-pkce=true identity-service.enable-pkce=true
keycloak.ignore-oauth-query-parameter=true identity-service.ignore-oauth-query-parameter=true
keycloak.credentials.secret=11111 identity-service.credentials.secret=11111
keycloak.credentials.provider=secret identity-service.credentials.provider=secret

View File

@@ -255,6 +255,6 @@ log4j.logger.org.alfresco.repo.site.SiteServiceImpl=DEBUG
log4j.logger.org.alfresco.repo.action.ActionServiceImpl=DEBUG log4j.logger.org.alfresco.repo.action.ActionServiceImpl=DEBUG
log4j.logger.org.alfresco.repo.security.person.PersonServiceImpl=DEBUG log4j.logger.org.alfresco.repo.security.person.PersonServiceImpl=DEBUG
# token authentication # identity service authentication
log4j.logger.org.alfresco.repo.security.authentication.token=debug log4j.logger.org.alfresco.repo.security.authentication.identityservice=debug
log4j.logger.org.keycloak=debug log4j.logger.org.keycloak=debug