Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2015-07-20 12:21:57 -04:00
commit 548c0db0ae
21 changed files with 773 additions and 686 deletions

View file

@ -1,5 +1,8 @@
language: java
jdk:
- oraclejdk8
cache:
directories:
- $HOME/.m2
@ -7,4 +10,10 @@ cache:
before_cache:
- rm -rf $HOME/.m2/repository/org/keycloak
install: mvn install -Pdistribution -DskipTests=true -B -V
script:
- mvn test -B
- mvn -file testsuite/integration-arquillian test -B
sudo: false

View file

@ -1,73 +1,73 @@
package org.keycloak.representations.idm;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RequiredActionProviderRepresentation {
private String alias;
private String name;
private String providerId;
private boolean enabled;
private boolean defaultAction;
private Map<String, String> config = new HashMap<String, String>();
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
/**
* Used for display purposes. Probably should clean this code up and make alias and name the same, but
* the old code references an Enum and the admin console creates a "friendly" name for each enum.
*
* @return
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isDefaultAction() {
return defaultAction;
}
public void setDefaultAction(boolean defaultAction) {
this.defaultAction = defaultAction;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}
package org.keycloak.representations.idm;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RequiredActionProviderRepresentation {
private String alias;
private String name;
private String providerId;
private boolean enabled;
private boolean defaultAction;
private Map<String, String> config = new HashMap<String, String>();
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
/**
* Used for display purposes. Probably should clean this code up and make alias and name the same, but
* the old code references an Enum and the admin console creates a "friendly" name for each enum.
*
* @return
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isDefaultAction() {
return defaultAction;
}
public void setDefaultAction(boolean defaultAction) {
this.defaultAction = defaultAction;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View file

@ -27,4 +27,10 @@
<outputDirectory>modules</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>../../shared-cli/adapter-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -27,4 +27,10 @@
<outputDirectory>modules/system/layers/base</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>../../shared-cli/adapter-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -0,0 +1,4 @@
/subsystem=security/security-domain=keycloak/:add
/subsystem=security/security-domain=keycloak/authentication=classic/:add(login-modules=[{ "code" => "org.keycloak.adapters.jboss.KeycloakLoginModule","flag" => "required"}])
/extension=org.keycloak.keycloak-adapter-subsystem/:add(module=org.keycloak.keycloak-adapter-subsystem)
/subsystem=keycloak:add

View file

@ -28,4 +28,10 @@
<outputDirectory>modules/system/layers/base</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>../../shared-cli/adapter-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -27,4 +27,10 @@
<outputDirectory>modules/system/layers/base</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>../../shared-cli/adapter-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -40,6 +40,14 @@
<source>src/main/providers/README.txt</source>
<outputDirectory>standalone/configuration/providers</outputDirectory>
</file>
<file>
<source>cli/keycloak-prepare.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
<file>
<source>cli/keycloak-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -0,0 +1,2 @@
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
/subsystem=keycloak-server:add(web-context=auth)

View file

@ -0,0 +1,2 @@
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true,enabled=true)
/subsystem=logging/logger=org.jboss.resteasy.resteasy_jaxrs.i18n/:add(level=ERROR)

View file

@ -44,22 +44,6 @@
</xsl:copy>
</xsl:template>
<xsl:template match="//sec:security-domains">
<xsl:copy>
<xsl:apply-templates select="node()[name(.)='security-domain']"/>
<security-domain name="keycloak">
<authentication>
<login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
</authentication>
</security-domain>
<security-domain name="sp" cache-type="default">
<authentication>
<login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
</authentication>
</security-domain>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $log)]">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>

View file

@ -62,6 +62,10 @@
<source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
<outputDirectory>standalone/configuration</outputDirectory>
</file>
<file>
<source>cli/keycloak-install.cli</source>
<outputDirectory>bin</outputDirectory>
</file>
</files>
</assembly>

View file

@ -0,0 +1,3 @@
/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
/subsystem=keycloak-server:add(web-context=auth)

View file

@ -48,6 +48,15 @@ $ unzip keycloak-as7-adapter-dist.zip
After adding the Keycloak modules, you must then enable the Keycloak Subsystem within your app server's server configuration:
<literal>domain.xml</literal> or <literal>standalone.xml</literal>.
</para>
<para>
There is a CLI script that will help you modify your server configuration. Start the server and run the script
from the server's bin directory:
<programlisting>
$ cd $JBOSS_HOME/bin
$ jboss-cli.sh -c --file=adapter-install.cli
</programlisting>
The script will add the extension, subsystem, and optional security-domain as described below.
</para>
<para>
<programlisting><![CDATA[
<server xmlns="urn:jboss:domain:1.4">
@ -65,8 +74,7 @@ $ unzip keycloak-as7-adapter-dist.zip
</programlisting>
</para>
<para>
Finally, you must specify a shared keycloak security domain.
This security domain should be used with EJBs and other components when you need the security context created
The keycloak security domain should be used with EJBs and other components when you need the security context created
in the secured web tier to be propagated to the EJBs (other EE component) you are invoking. Otherwise
this configuration is optional.
</para>

View file

@ -49,9 +49,9 @@
<literal>keycloak-overlay-&project.version;.zip</literal> or <literal>keycloak-overlay-&project.version;.tar.gz</literal>.
Once downloaded extract into the root directory of your WildFly installation. To start WildFly with Keycloak
run:
<programlisting>keycloak-&project.version;/bin/standalone.sh --server-config=standalone-keycloak.xml</programlisting>
<programlisting>&lt;WILDFLY_HOME&gt;/bin/standalone.sh --server-config=standalone-keycloak.xml</programlisting>
or:
<programlisting>keycloak-&project.version;/bin/standalone.bat --server-config=standalone-keycloak.xml</programlisting>
<programlisting>&lt;WILDFLY_HOME&gt;/bin/standalone.bat --server-config=standalone-keycloak.xml</programlisting>
</para>
<para>
Once the server is started log into the admin console at
@ -60,15 +60,9 @@
enter in a new password.
</para>
<para>
To add Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) open
<literal>standalone/configuration/standalone-keycloak.xml</literal> and the configuration you want to add it
to, for example <literal>standalone/configuration/standalone.xml</literal>. From <literal>standalone-keycloak.xml</literal>
you need to copy 3 elements:
<itemizedlist>
<listitem><literal>&lt;extension module="org.keycloak.keycloak-server-subsystem"/&gt;</literal></listitem>
<listitem><literal>&lt;datasource jndi-name="java:jboss/datasources/KeycloakDS" ...&gt;</literal></listitem>
<listitem><literal>&lt;subsystem xmlns="urn:jboss:domain:keycloak-server:1.1" ...&gt;</literal></listitem>
</itemizedlist>
To add Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) start the server with
the desired server-config. Then execute the following CLI script:
<programlisting>&lt;WILDFLY_HOME&gt;/bin/jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
</para>
</section>
<section>
@ -76,6 +70,19 @@
<para>
Same procedure as WildFly 9.0.0.Final, but download <literal>keycloak-overlay-eap6-&project.version;.zip</literal> or <literal>keycloak-overlay-eap6-&project.version;.tar.gz</literal>.
</para>
<para>
However, for EAP, adding Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) requires two CLI scripts. Start the server with
the desired server-config. Then execute the following CLI scripts with a restart in between:
<orderedlist>
<listitem>
<programlisting>&lt;EAP_HOME&gt;/bin/jboss-cli.sh -c --file=keycloak-prepare.cli</programlisting>
</listitem>
<listitem>Restart the server with the same server-config.</listitem>
<listitem>
<programlisting>&lt;EAP_HOME&gt;/bin/jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
</listitem>
</orderedlist>
</para>
</section>
<section>
<title id="demo_install">Install Development Bundle</title>

View file

@ -9,30 +9,48 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* Bean holding the {@link KeycloakDeployment} and {@link AdapterDeploymentContext} for this
* Spring application context. The Keycloak deployment is loaded from the required
* <code>WEB-INF/keycloak.json</code> file generated by Keycloak.
* <code>keycloak.json</code> resource file.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
* @version $Revision: 1 $
*/
public class AdapterDeploymentContextBean implements ApplicationContextAware, InitializingBean {
private static final String KEYCLOAK_CONFIG_FILE = "keycloak.json";
private static final String KEYCLOAK_CONFIG_WEB_RESOURCE = "WEB-INF/" + KEYCLOAK_CONFIG_FILE;
private static final String KEYCLOAK_CONFIG_CLASSPATH_RESOURCE = "classpath:" + KEYCLOAK_CONFIG_FILE;
private ApplicationContext applicationContext;
private AdapterDeploymentContext deploymentContext;
private KeycloakDeployment deployment;
@Override
public void afterPropertiesSet() throws Exception {
Resource resource = applicationContext.getResource("WEB-INF/keycloak.json");
InputStream is = resource.getInputStream();
this.deployment = KeycloakDeploymentBuilder.build(is);
this.deployment = loadKeycloakDeployment();
this.deploymentContext = new AdapterDeploymentContext(deployment);
}
private KeycloakDeployment loadKeycloakDeployment() throws IOException {
Resource resource = applicationContext.getResource(KEYCLOAK_CONFIG_WEB_RESOURCE);
if (!resource.isReadable()) {
resource= applicationContext.getResource(KEYCLOAK_CONFIG_CLASSPATH_RESOURCE);
}
if (!resource.isReadable()) {
throw new FileNotFoundException(String.format("Unable to locate Keycloak from %s or %s", KEYCLOAK_CONFIG_WEB_RESOURCE, KEYCLOAK_CONFIG_CLASSPATH_RESOURCE));
}
return KeycloakDeploymentBuilder.build(resource.getInputStream());
}
/**
* Returns the Keycloak {@link AdapterDeploymentContext} for this application context.
*

View file

@ -623,6 +623,7 @@ public class LoginActionsService {
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
String actionCookieValue = getActionCookie();
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
session.sessions().removeClientSession(realm, clientSession);
return session.getProvider(LoginFormsProvider.class)
.setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
.createInfoPage();
@ -657,6 +658,7 @@ public class LoginActionsService {
String actionCookieValue = getActionCookie();
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
session.sessions().removeClientSession(realm, clientSession);
return session.getProvider(LoginFormsProvider.class)
.setSuccess(Messages.EMAIL_VERIFIED)
.createInfoPage();

View file

@ -1,58 +1,58 @@
package org.keycloak.utils;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory;
import org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory;
import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.CredentialRepresentation;
/**
* used to set an execution a state based on type.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CredentialHelper {
public static void setRequiredCredential(KeycloakSession session, String type, RealmModel realm) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED;
authenticationRequirement(session, realm, type, requirement);
}
public static void setAlternativeCredential(KeycloakSession session, String type, RealmModel realm) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.ALTERNATIVE;
authenticationRequirement(session, realm, type, requirement);
}
public static void authenticationRequirement(KeycloakSession session, RealmModel realm, String type, AuthenticationExecutionModel.Requirement requirement) {
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
for (AuthenticationExecutionModel execution : realm.getAuthenticationExecutions(flow.getId())) {
String providerId = execution.getAuthenticator();
ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(session, providerId);
if (factory == null) continue;
if (type.equals(factory.getReferenceCategory())) {
execution.setRequirement(requirement);
realm.updateAuthenticatorExecution(execution);
}
}
}
}
public static ConfigurableAuthenticatorFactory getConfigurableAuthenticatorFactory(KeycloakSession session, String providerId) {
ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, providerId);
if (factory == null) {
factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, providerId);
}
return factory;
}
}
package org.keycloak.utils;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory;
import org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory;
import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.CredentialRepresentation;
/**
* used to set an execution a state based on type.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CredentialHelper {
public static void setRequiredCredential(KeycloakSession session, String type, RealmModel realm) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.REQUIRED;
authenticationRequirement(session, realm, type, requirement);
}
public static void setAlternativeCredential(KeycloakSession session, String type, RealmModel realm) {
AuthenticationExecutionModel.Requirement requirement = AuthenticationExecutionModel.Requirement.ALTERNATIVE;
authenticationRequirement(session, realm, type, requirement);
}
public static void authenticationRequirement(KeycloakSession session, RealmModel realm, String type, AuthenticationExecutionModel.Requirement requirement) {
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
for (AuthenticationExecutionModel execution : realm.getAuthenticationExecutions(flow.getId())) {
String providerId = execution.getAuthenticator();
ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(session, providerId);
if (factory == null) continue;
if (type.equals(factory.getReferenceCategory())) {
execution.setRequirement(requirement);
realm.updateAuthenticatorExecution(execution);
}
}
}
}
public static ConfigurableAuthenticatorFactory getConfigurableAuthenticatorFactory(KeycloakSession session, String providerId) {
ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, providerId);
if (factory == null) {
factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, providerId);
}
return factory;
}
}

View file

@ -5,6 +5,7 @@ import com.icegreen.greenmail.util.ServerSetup;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;
public class MailServer {
@ -22,9 +23,20 @@ public class MailServer {
if (greenMail.waitForIncomingEmail(Long.MAX_VALUE, c + 1)) {
MimeMessage message = greenMail.getReceivedMessages()[c++];
System.out.println("-------------------------------------------------------");
System.out.println("Received mail to " + message.getRecipients(RecipientType.TO)[0]);
System.out.println();
System.out.println(message.getContent());
if (message.getContent() instanceof MimeMultipart) {
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
for (int i = 0; i < mimeMultipart.getCount(); i++) {
System.out.println("----");
System.out.println(mimeMultipart.getBodyPart(i).getContentType() + ":");
System.out.println();
System.out.println(mimeMultipart.getBodyPart(i).getContent());
}
} else {
System.out.println();
System.out.println(message.getContent());
}
System.out.println("-------------------------------------------------------");
}
}

View file

@ -1,223 +1,223 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.actions;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.authentication.requiredactions.UpdateTotp;
import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountTotpPage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginConfigTotpPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginTotpPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.utils.CredentialHelper;
import org.openqa.selenium.WebDriver;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RequiredActionTotpSetupTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
CredentialHelper.setRequiredCredential(manager.getSession(), CredentialRepresentation.TOTP, appRealm);
//appRealm.addRequiredCredential(CredentialRepresentation.TOTP);
RequiredActionProviderModel requiredAction = appRealm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
requiredAction.setDefaultAction(true);
appRealm.updateRequiredActionProvider(requiredAction);
appRealm.setResetPasswordAllowed(true);
}
});
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected LoginTotpPage loginTotpPage;
@WebResource
protected LoginConfigTotpPage totpPage;
@WebResource
protected AccountTotpPage accountTotpPage;
@WebResource
protected OAuthClient oauth;
@WebResource
protected RegisterPage registerPage;
protected TimeBasedOTP totp = new TimeBasedOTP();
@Test
public void setupTotpRegister() {
loginPage.open();
loginPage.clickRegister();
registerPage.register("firstName", "lastName", "email@mail.com", "setupTotp", "password", "password");
String userId = events.expectRegister("setupTotp", "email@mail.com").assertEvent().getUserId();
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp").assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).session(sessionId).detail(Details.USERNAME, "setuptotp").assertEvent();
}
@Test
public void setupTotpExisting() {
loginPage.open();
loginPage.login("test-user@localhost", "password");
totpPage.assertCurrent();
String totpSecret = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpSecret));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Event loginEvent = events.expectLogin().session(sessionId).assertEvent();
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).assertEvent();
loginPage.open();
loginPage.login("test-user@localhost", "password");
String src = driver.getPageSource();
loginTotpPage.login(totp.generate(totpSecret));
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().assertEvent();
}
@Test
public void setupTotpRegisteredAfterTotpRemoval() {
// Register new user
loginPage.open();
loginPage.clickRegister();
registerPage.register("firstName2", "lastName2", "email2@mail.com", "setupTotp2", "password2", "password2");
String userId = events.expectRegister("setupTotp2", "email2@mail.com").assertEvent().getUserId();
// Configure totp
totpPage.assertCurrent();
String totpCode = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpCode));
// After totp config, user should be on the app page
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
// Logout
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).user(userId).assertEvent();
// Try to login after logout
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Totp is already configured, thus one-time password is needed, login page should be loaded
Assert.assertTrue(loginPage.isCurrent());
Assert.assertFalse(totpPage.isCurrent());
// Login with one-time password
loginTotpPage.login(totp.generate(totpCode));
loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
// Open account page
accountTotpPage.open();
accountTotpPage.assertCurrent();
// Remove google authentificator
accountTotpPage.removeTotp();
events.expectAccount(EventType.REMOVE_TOTP).user(userId).assertEvent();
// Logout
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).user(userId).assertEvent();
// Try to login
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Since the authentificator was removed, it has to be set up again
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).session(sessionId).detail(Details.USERNAME, "setuptotp2").assertEvent();
}
}
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.actions;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.authentication.requiredactions.UpdateTotp;
import org.keycloak.events.Details;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountTotpPage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginConfigTotpPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginTotpPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.utils.CredentialHelper;
import org.openqa.selenium.WebDriver;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RequiredActionTotpSetupTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
CredentialHelper.setRequiredCredential(manager.getSession(), CredentialRepresentation.TOTP, appRealm);
//appRealm.addRequiredCredential(CredentialRepresentation.TOTP);
RequiredActionProviderModel requiredAction = appRealm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
requiredAction.setDefaultAction(true);
appRealm.updateRequiredActionProvider(requiredAction);
appRealm.setResetPasswordAllowed(true);
}
});
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected AppPage appPage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected LoginTotpPage loginTotpPage;
@WebResource
protected LoginConfigTotpPage totpPage;
@WebResource
protected AccountTotpPage accountTotpPage;
@WebResource
protected OAuthClient oauth;
@WebResource
protected RegisterPage registerPage;
protected TimeBasedOTP totp = new TimeBasedOTP();
@Test
public void setupTotpRegister() {
loginPage.open();
loginPage.clickRegister();
registerPage.register("firstName", "lastName", "email@mail.com", "setupTotp", "password", "password");
String userId = events.expectRegister("setupTotp", "email@mail.com").assertEvent().getUserId();
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp").assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).session(sessionId).detail(Details.USERNAME, "setuptotp").assertEvent();
}
@Test
public void setupTotpExisting() {
loginPage.open();
loginPage.login("test-user@localhost", "password");
totpPage.assertCurrent();
String totpSecret = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpSecret));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Event loginEvent = events.expectLogin().session(sessionId).assertEvent();
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).assertEvent();
loginPage.open();
loginPage.login("test-user@localhost", "password");
String src = driver.getPageSource();
loginTotpPage.login(totp.generate(totpSecret));
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().assertEvent();
}
@Test
public void setupTotpRegisteredAfterTotpRemoval() {
// Register new user
loginPage.open();
loginPage.clickRegister();
registerPage.register("firstName2", "lastName2", "email2@mail.com", "setupTotp2", "password2", "password2");
String userId = events.expectRegister("setupTotp2", "email2@mail.com").assertEvent().getUserId();
// Configure totp
totpPage.assertCurrent();
String totpCode = totpPage.getTotpSecret();
totpPage.configure(totp.generate(totpCode));
// After totp config, user should be on the app page
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
// Logout
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).user(userId).assertEvent();
// Try to login after logout
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Totp is already configured, thus one-time password is needed, login page should be loaded
Assert.assertTrue(loginPage.isCurrent());
Assert.assertFalse(totpPage.isCurrent());
// Login with one-time password
loginTotpPage.login(totp.generate(totpCode));
loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent();
// Open account page
accountTotpPage.open();
accountTotpPage.assertCurrent();
// Remove google authentificator
accountTotpPage.removeTotp();
events.expectAccount(EventType.REMOVE_TOTP).user(userId).assertEvent();
// Logout
oauth.openLogout();
events.expectLogout(loginEvent.getSessionId()).user(userId).assertEvent();
// Try to login
loginPage.open();
loginPage.login("setupTotp2", "password2");
// Since the authentificator was removed, it has to be set up again
totpPage.assertCurrent();
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp2").assertEvent().getSessionId();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).session(sessionId).detail(Details.USERNAME, "setuptotp2").assertEvent();
}
}

View file

@ -1,296 +1,296 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.composites;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.ApplicationServlet;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import java.security.PublicKey;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class CompositeRoleTest {
public static PublicKey realmPublicKey;
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmModel realm = manager.createRealm("test");
KeycloakModelUtils.generateRealmKeys(realm);
realmPublicKey = realm.getPublicKey();
realm.setSsoSessionIdleTimeout(3000);
realm.setAccessTokenLifespan(10000);
realm.setSsoSessionMaxLifespan(10000);
realm.setAccessCodeLifespanUserAction(1000);
realm.setAccessCodeLifespan(1000);
realm.setSslRequired(SslRequired.EXTERNAL);
realm.setEnabled(true);
realm.addRequiredCredential(UserCredentialModel.PASSWORD);
final RoleModel realmRole1 = realm.addRole("REALM_ROLE_1");
final RoleModel realmRole2 = realm.addRole("REALM_ROLE_2");
final RoleModel realmRole3 = realm.addRole("REALM_ROLE_3");
final RoleModel realmComposite1 = realm.addRole("REALM_COMPOSITE_1");
realmComposite1.addCompositeRole(realmRole1);
final UserModel realmComposite1User = session.users().addUser(realm, "REALM_COMPOSITE_1_USER");
realmComposite1User.setEnabled(true);
realmComposite1User.updateCredential(UserCredentialModel.password("password"));
realmComposite1User.grantRole(realmComposite1);
final UserModel realmRole1User = session.users().addUser(realm, "REALM_ROLE_1_USER");
realmRole1User.setEnabled(true);
realmRole1User.updateCredential(UserCredentialModel.password("password"));
realmRole1User.grantRole(realmRole1);
final ClientModel realmComposite1Application = new ClientManager(manager).createClient(realm, "REALM_COMPOSITE_1_APPLICATION");
realmComposite1Application.setFullScopeAllowed(false);
realmComposite1Application.setEnabled(true);
realmComposite1Application.addScopeMapping(realmComposite1);
realmComposite1Application.addRedirectUri("http://localhost:8081/app/*");
realmComposite1Application.setBaseUrl("http://localhost:8081/app");
realmComposite1Application.setManagementUrl("http://localhost:8081/app/logout");
realmComposite1Application.setSecret("password");
final ClientModel realmRole1Application = new ClientManager(manager).createClient(realm, "REALM_ROLE_1_APPLICATION");
realmRole1Application.setFullScopeAllowed(false);
realmRole1Application.setEnabled(true);
realmRole1Application.addScopeMapping(realmRole1);
realmRole1Application.addRedirectUri("http://localhost:8081/app/*");
realmRole1Application.setBaseUrl("http://localhost:8081/app");
realmRole1Application.setManagementUrl("http://localhost:8081/app/logout");
realmRole1Application.setSecret("password");
final ClientModel appRoleApplication = new ClientManager(manager).createClient(realm, "APP_ROLE_APPLICATION");
appRoleApplication.setFullScopeAllowed(false);
appRoleApplication.setEnabled(true);
appRoleApplication.addRedirectUri("http://localhost:8081/app/*");
appRoleApplication.setBaseUrl("http://localhost:8081/app");
appRoleApplication.setManagementUrl("http://localhost:8081/app/logout");
appRoleApplication.setSecret("password");
final RoleModel appRole1 = appRoleApplication.addRole("APP_ROLE_1");
final RoleModel appRole2 = appRoleApplication.addRole("APP_ROLE_2");
final RoleModel realmAppCompositeRole = realm.addRole("REALM_APP_COMPOSITE_ROLE");
realmAppCompositeRole.addCompositeRole(appRole1);
final UserModel realmAppCompositeUser = session.users().addUser(realm, "REALM_APP_COMPOSITE_USER");
realmAppCompositeUser.setEnabled(true);
realmAppCompositeUser.updateCredential(UserCredentialModel.password("password"));
realmAppCompositeUser.grantRole(realmAppCompositeRole);
final UserModel realmAppRoleUser = session.users().addUser(realm, "REALM_APP_ROLE_USER");
realmAppRoleUser.setEnabled(true);
realmAppRoleUser.updateCredential(UserCredentialModel.password("password"));
realmAppRoleUser.grantRole(appRole2);
final ClientModel appCompositeApplication = new ClientManager(manager).createClient(realm, "APP_COMPOSITE_APPLICATION");
appCompositeApplication.setFullScopeAllowed(false);
appCompositeApplication.setEnabled(true);
appCompositeApplication.addRedirectUri("http://localhost:8081/app/*");
appCompositeApplication.setBaseUrl("http://localhost:8081/app");
appCompositeApplication.setManagementUrl("http://localhost:8081/app/logout");
appCompositeApplication.setSecret("password");
final RoleModel appCompositeRole = appCompositeApplication.addRole("APP_COMPOSITE_ROLE");
appCompositeApplication.addScopeMapping(appRole2);
appCompositeRole.addCompositeRole(realmRole1);
appCompositeRole.addCompositeRole(realmRole2);
appCompositeRole.addCompositeRole(realmRole3);
appCompositeRole.addCompositeRole(appRole1);
final UserModel appCompositeUser = session.users().addUser(realm, "APP_COMPOSITE_USER");
appCompositeUser.setEnabled(true);
appCompositeUser.updateCredential(UserCredentialModel.password("password"));
appCompositeUser.grantRole(realmAppCompositeRole);
appCompositeUser.grantRole(realmComposite1);
deployServlet("app", "/app", ApplicationServlet.class);
}
};
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected OAuthClient oauth;
@WebResource
protected LoginPage loginPage;
@Test
public void testAppCompositeUser() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_COMPOSITE_APPLICATION");
oauth.doLogin("APP_COMPOSITE_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmAppCompositeUser() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_ROLE_APPLICATION");
oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_ROLE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_ROLE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
}
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite.composites;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.ApplicationServlet;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import java.security.PublicKey;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class CompositeRoleTest {
public static PublicKey realmPublicKey;
@ClassRule
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmModel realm = manager.createRealm("test");
KeycloakModelUtils.generateRealmKeys(realm);
realmPublicKey = realm.getPublicKey();
realm.setSsoSessionIdleTimeout(3000);
realm.setAccessTokenLifespan(10000);
realm.setSsoSessionMaxLifespan(10000);
realm.setAccessCodeLifespanUserAction(1000);
realm.setAccessCodeLifespan(1000);
realm.setSslRequired(SslRequired.EXTERNAL);
realm.setEnabled(true);
realm.addRequiredCredential(UserCredentialModel.PASSWORD);
final RoleModel realmRole1 = realm.addRole("REALM_ROLE_1");
final RoleModel realmRole2 = realm.addRole("REALM_ROLE_2");
final RoleModel realmRole3 = realm.addRole("REALM_ROLE_3");
final RoleModel realmComposite1 = realm.addRole("REALM_COMPOSITE_1");
realmComposite1.addCompositeRole(realmRole1);
final UserModel realmComposite1User = session.users().addUser(realm, "REALM_COMPOSITE_1_USER");
realmComposite1User.setEnabled(true);
realmComposite1User.updateCredential(UserCredentialModel.password("password"));
realmComposite1User.grantRole(realmComposite1);
final UserModel realmRole1User = session.users().addUser(realm, "REALM_ROLE_1_USER");
realmRole1User.setEnabled(true);
realmRole1User.updateCredential(UserCredentialModel.password("password"));
realmRole1User.grantRole(realmRole1);
final ClientModel realmComposite1Application = new ClientManager(manager).createClient(realm, "REALM_COMPOSITE_1_APPLICATION");
realmComposite1Application.setFullScopeAllowed(false);
realmComposite1Application.setEnabled(true);
realmComposite1Application.addScopeMapping(realmComposite1);
realmComposite1Application.addRedirectUri("http://localhost:8081/app/*");
realmComposite1Application.setBaseUrl("http://localhost:8081/app");
realmComposite1Application.setManagementUrl("http://localhost:8081/app/logout");
realmComposite1Application.setSecret("password");
final ClientModel realmRole1Application = new ClientManager(manager).createClient(realm, "REALM_ROLE_1_APPLICATION");
realmRole1Application.setFullScopeAllowed(false);
realmRole1Application.setEnabled(true);
realmRole1Application.addScopeMapping(realmRole1);
realmRole1Application.addRedirectUri("http://localhost:8081/app/*");
realmRole1Application.setBaseUrl("http://localhost:8081/app");
realmRole1Application.setManagementUrl("http://localhost:8081/app/logout");
realmRole1Application.setSecret("password");
final ClientModel appRoleApplication = new ClientManager(manager).createClient(realm, "APP_ROLE_APPLICATION");
appRoleApplication.setFullScopeAllowed(false);
appRoleApplication.setEnabled(true);
appRoleApplication.addRedirectUri("http://localhost:8081/app/*");
appRoleApplication.setBaseUrl("http://localhost:8081/app");
appRoleApplication.setManagementUrl("http://localhost:8081/app/logout");
appRoleApplication.setSecret("password");
final RoleModel appRole1 = appRoleApplication.addRole("APP_ROLE_1");
final RoleModel appRole2 = appRoleApplication.addRole("APP_ROLE_2");
final RoleModel realmAppCompositeRole = realm.addRole("REALM_APP_COMPOSITE_ROLE");
realmAppCompositeRole.addCompositeRole(appRole1);
final UserModel realmAppCompositeUser = session.users().addUser(realm, "REALM_APP_COMPOSITE_USER");
realmAppCompositeUser.setEnabled(true);
realmAppCompositeUser.updateCredential(UserCredentialModel.password("password"));
realmAppCompositeUser.grantRole(realmAppCompositeRole);
final UserModel realmAppRoleUser = session.users().addUser(realm, "REALM_APP_ROLE_USER");
realmAppRoleUser.setEnabled(true);
realmAppRoleUser.updateCredential(UserCredentialModel.password("password"));
realmAppRoleUser.grantRole(appRole2);
final ClientModel appCompositeApplication = new ClientManager(manager).createClient(realm, "APP_COMPOSITE_APPLICATION");
appCompositeApplication.setFullScopeAllowed(false);
appCompositeApplication.setEnabled(true);
appCompositeApplication.addRedirectUri("http://localhost:8081/app/*");
appCompositeApplication.setBaseUrl("http://localhost:8081/app");
appCompositeApplication.setManagementUrl("http://localhost:8081/app/logout");
appCompositeApplication.setSecret("password");
final RoleModel appCompositeRole = appCompositeApplication.addRole("APP_COMPOSITE_ROLE");
appCompositeApplication.addScopeMapping(appRole2);
appCompositeRole.addCompositeRole(realmRole1);
appCompositeRole.addCompositeRole(realmRole2);
appCompositeRole.addCompositeRole(realmRole3);
appCompositeRole.addCompositeRole(appRole1);
final UserModel appCompositeUser = session.users().addUser(realm, "APP_COMPOSITE_USER");
appCompositeUser.setEnabled(true);
appCompositeUser.updateCredential(UserCredentialModel.password("password"));
appCompositeUser.grantRole(realmAppCompositeRole);
appCompositeUser.grantRole(realmComposite1);
deployServlet("app", "/app", ApplicationServlet.class);
}
};
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected OAuthClient oauth;
@WebResource
protected LoginPage loginPage;
@Test
public void testAppCompositeUser() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_COMPOSITE_APPLICATION");
oauth.doLogin("APP_COMPOSITE_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmAppCompositeUser() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_ROLE_APPLICATION");
oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_ROLE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
@Test
public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_ROLE_1_USER", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
Assert.assertEquals("bearer", response.getTokenType());
AccessToken token = oauth.verifyToken(response.getAccessToken());
Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
Assert.assertEquals(200, refreshResponse.getStatusCode());
}
}