Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2017-01-09 17:14:34 -05:00
commit 452611242c
75 changed files with 1375 additions and 1802 deletions

View file

@ -84,7 +84,7 @@
<outputDirectory></outputDirectory> <outputDirectory></outputDirectory>
</fileSet> </fileSet>
<fileSet> <fileSet>
<directory>src/main/cli</directory> <directory>${project.build.directory}/cli</directory>
<includes> <includes>
<include>*.cli</include> <include>*.cli</include>
</includes> </includes>
@ -107,11 +107,6 @@
<outputDirectory>bin</outputDirectory> <outputDirectory>bin</outputDirectory>
<destName>add-user-keycloak.bat</destName> <destName>add-user-keycloak.bat</destName>
</file> </file>
<file>
<source>${project.build.directory}/cli/default-keycloak-subsys-config.cli</source>
<outputDirectory>bin</outputDirectory>
<destName>default-keycloak-subsys-config.cli</destName>
</file>
</files> </files>
</assembly> </assembly>

View file

@ -129,6 +129,43 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<inherited>false</inherited>
<executions>
<execution>
<id>merge-cli</id>
<phase>prepare-package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<concat destfile="${project.build.directory}/cli/keycloak-install.cli" force="yes">
<fileset dir="${project.build.directory}/cli">
<include name="keycloak-install-base.cli"/>
</fileset>
<fileset dir="${project.build.directory}/cli">
<include name="default-keycloak-subsys-config.cli"/>
</fileset>
</concat>
<concat destfile="${project.build.directory}/cli/keycloak-install-ha.cli" force="yes">
<fileset dir="${project.build.directory}/cli">
<include name="keycloak-install-ha-base.cli"/>
</fileset>
<fileset dir="${project.build.directory}/cli">
<include name="default-keycloak-subsys-config.cli"/>
</fileset>
</concat>
<delete file="${project.build.directory}/cli/default-keycloak-subsys-config.cli"/>
<delete file="${project.build.directory}/cli/keycloak-install-base.cli"/>
<delete file="${project.build.directory}/cli/keycloak-install-ha-base.cli"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View file

@ -15,4 +15,3 @@ embed-server --server-config=standalone.xml
/subsystem=infinispan/cache-container=keycloak/local-cache=keys/eviction=EVICTION:add(max-entries=1000,strategy=LRU) /subsystem=infinispan/cache-container=keycloak/local-cache=keys/eviction=EVICTION:add(max-entries=1000,strategy=LRU)
/subsystem=infinispan/cache-container=keycloak/local-cache=keys/expiration=EXPIRATION:add(max-idle=3600000) /subsystem=infinispan/cache-container=keycloak/local-cache=keys/expiration=EXPIRATION:add(max-idle=3600000)
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem) /extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
run-batch --file=default-keycloak-subsys-config.cli

View file

@ -15,4 +15,3 @@ embed-server --server-config=standalone-ha.xml
/subsystem=infinispan/cache-container=keycloak/local-cache=keys/eviction=EVICTION:add(max-entries=1000,strategy=LRU) /subsystem=infinispan/cache-container=keycloak/local-cache=keys/eviction=EVICTION:add(max-entries=1000,strategy=LRU)
/subsystem=infinispan/cache-container=keycloak/local-cache=keys/expiration=EXPIRATION:add(max-idle=3600000) /subsystem=infinispan/cache-container=keycloak/local-cache=keys/expiration=EXPIRATION:add(max-idle=3600000)
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem) /extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
run-batch --file=default-keycloak-subsys-config.cli

View file

@ -18,7 +18,9 @@
package org.keycloak.federation.kerberos; package org.keycloak.federation.kerberos;
import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.representations.idm.ComponentRepresentation;
import java.util.Map; import java.util.Map;
@ -29,31 +31,39 @@ import java.util.Map;
*/ */
public abstract class CommonKerberosConfig { public abstract class CommonKerberosConfig {
protected ComponentModel componentModel; protected MultivaluedHashMap<String, String> userStorageConfig;
public CommonKerberosConfig(ComponentModel componentModel) { public CommonKerberosConfig(ComponentModel componentModel) {
this.componentModel = componentModel; this.userStorageConfig = componentModel.getConfig();
}
public CommonKerberosConfig(ComponentRepresentation componentRep) {
this.userStorageConfig = componentRep.getConfig();
}
protected MultivaluedHashMap<String, String> getConfig() {
return userStorageConfig;
} }
// Should be always true for KerberosFederationProvider // Should be always true for KerberosFederationProvider
public boolean isAllowKerberosAuthentication() { public boolean isAllowKerberosAuthentication() {
return Boolean.valueOf(componentModel.getConfig().getFirst(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION)); return Boolean.valueOf(getConfig().getFirst(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION));
} }
public String getKerberosRealm() { public String getKerberosRealm() {
return componentModel.getConfig().getFirst(KerberosConstants.KERBEROS_REALM); return getConfig().getFirst(KerberosConstants.KERBEROS_REALM);
} }
public String getServerPrincipal() { public String getServerPrincipal() {
return componentModel.getConfig().getFirst(KerberosConstants.SERVER_PRINCIPAL); return getConfig().getFirst(KerberosConstants.SERVER_PRINCIPAL);
} }
public String getKeyTab() { public String getKeyTab() {
return componentModel.getConfig().getFirst(KerberosConstants.KEYTAB); return getConfig().getFirst(KerberosConstants.KEYTAB);
} }
public boolean isDebug() { public boolean isDebug() {
return Boolean.valueOf(componentModel.getConfig().getFirst(KerberosConstants.DEBUG)); return Boolean.valueOf(getConfig().getFirst(KerberosConstants.DEBUG));
} }

View file

@ -20,6 +20,7 @@ package org.keycloak.federation.kerberos;
import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.storage.UserStorageProvider.EditMode; import org.keycloak.storage.UserStorageProvider.EditMode;
/** /**
@ -33,8 +34,12 @@ public class KerberosConfig extends CommonKerberosConfig {
super(component); super(component);
} }
public KerberosConfig(ComponentRepresentation component) {
super(component);
}
public EditMode getEditMode() { public EditMode getEditMode() {
String editModeString = componentModel.getConfig().getFirst(LDAPConstants.EDIT_MODE); String editModeString = getConfig().getFirst(LDAPConstants.EDIT_MODE);
if (editModeString == null) { if (editModeString == null) {
return EditMode.UNSYNCED; return EditMode.UNSYNCED;
} else { } else {
@ -43,11 +48,11 @@ public class KerberosConfig extends CommonKerberosConfig {
} }
public boolean isAllowPasswordAuthentication() { public boolean isAllowPasswordAuthentication() {
return Boolean.valueOf(componentModel.getConfig().getFirst(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION)); return Boolean.valueOf(getConfig().getFirst(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION));
} }
public boolean isUpdateProfileFirstLogin() { public boolean isUpdateProfileFirstLogin() {
return Boolean.valueOf(componentModel.getConfig().getFirst(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN)); return Boolean.valueOf(getConfig().getFirst(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN));
} }
} }

View file

@ -99,7 +99,12 @@ public class KerberosUsernamePasswordAuthenticator {
} }
protected void checkKerberosServerAvailable(LoginException le) { protected void checkKerberosServerAvailable(LoginException le) {
if (le.getMessage().contains("Port Unreachable")) { String message = le.getMessage().toUpperCase();
if (message.contains("PORT UNREACHABLE") ||
message.contains("CANNOT LOCATE") ||
message.contains("CANNOT CONTACT") ||
message.contains("CANNOT FIND") ||
message.contains("UNKNOWN ERROR")) {
throw new ModelException("Kerberos unreachable", le); throw new ModelException("Kerberos unreachable", le);
} }
} }

View file

@ -20,6 +20,7 @@ package org.keycloak.storage.ldap.kerberos;
import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.federation.kerberos.CommonKerberosConfig; import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.LDAPStorageProvider;
/** /**
@ -33,7 +34,11 @@ public class LDAPProviderKerberosConfig extends CommonKerberosConfig {
super(componentModel); super(componentModel);
} }
public LDAPProviderKerberosConfig(ComponentRepresentation componentRep) {
super(componentRep);
}
public boolean isUseKerberosForPasswordAuthentication() { public boolean isUseKerberosForPasswordAuthentication() {
return Boolean.valueOf(componentModel.getConfig().getFirst(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION)); return Boolean.valueOf(getConfig().getFirst(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION));
} }
} }

View file

@ -21,6 +21,7 @@ import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory; import org.keycloak.saml.common.PicketLinkLoggerFactory;
import org.keycloak.saml.common.constants.GeneralConstants; import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.SecurityActions;
import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.common.util.SystemPropertiesUtil; import org.keycloak.saml.common.util.SystemPropertiesUtil;
@ -32,8 +33,9 @@ import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent; import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.EventReaderDelegate; import javax.xml.stream.util.EventReaderDelegate;
import java.io.InputStream; import java.io.InputStream;
import java.security.AccessController; import javax.xml.transform.Source;
import java.security.PrivilegedAction; import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Node;
/** /**
* Base class for parsers * Base class for parsers
@ -45,26 +47,33 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
/** private static final ThreadLocal<XMLInputFactory> XML_INPUT_FACTORY = new ThreadLocal<XMLInputFactory>() {
* Get the JAXP {@link XMLInputFactory} @Override
* protected XMLInputFactory initialValue() {
* @return return getXMLInputFactory();
*/ }
protected XMLInputFactory getXMLInputFactory() {
boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false") /**
.equalsIgnoreCase("true"); * Get the JAXP {@link XMLInputFactory}
ClassLoader prevTCCL = getTCCL(); *
try { * @return
if (tccl_jaxp) { */
setTCCL(getClass().getClassLoader()); private XMLInputFactory getXMLInputFactory() {
} boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false")
return XMLInputFactory.newInstance(); .equalsIgnoreCase("true");
} finally { ClassLoader prevTCCL = SecurityActions.getTCCL();
if (tccl_jaxp) { try {
setTCCL(prevTCCL); if (tccl_jaxp) {
SecurityActions.setTCCL(AbstractParser.class.getClassLoader());
}
return XMLInputFactory.newInstance();
} finally {
if (tccl_jaxp) {
SecurityActions.setTCCL(prevTCCL);
}
} }
} }
} };
/** /**
* Parse an InputStream for payload * Parse an InputStream for payload
@ -81,6 +90,15 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
return parse(xmlEventReader); return parse(xmlEventReader);
} }
public Object parse(Source source) throws ParsingException {
XMLEventReader xmlEventReader = createEventReader(source);
return parse(xmlEventReader);
}
public Object parse(Node node) throws ParsingException {
return parse(new DOMSource(node));
}
public XMLEventReader createEventReader(InputStream configStream) throws ParsingException { public XMLEventReader createEventReader(InputStream configStream) throws ParsingException {
if (configStream == null) if (configStream == null)
throw logger.nullArgumentError("InputStream"); throw logger.nullArgumentError("InputStream");
@ -96,33 +114,23 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
return xmlEventReader; return xmlEventReader;
} }
private ClassLoader getTCCL() { public XMLEventReader createEventReader(Source source) throws ParsingException {
if (System.getSecurityManager() != null) { if (source == null)
return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { throw logger.nullArgumentError("Source");
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
});
} else {
return Thread.currentThread().getContextClassLoader();
}
}
private void setTCCL(final ClassLoader paramCl) { XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(source);
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() { try {
public Void run() { xmlEventReader = filterWhitespaces(xmlEventReader);
Thread.currentThread().setContextClassLoader(paramCl); } catch (XMLStreamException e) {
return null; throw logger.parserException(e);
}
});
} else {
Thread.currentThread().setContextClassLoader(paramCl);
} }
return xmlEventReader;
} }
protected XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws XMLStreamException { protected XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws XMLStreamException {
XMLInputFactory xmlInputFactory = getXMLInputFactory(); XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get();
xmlEventReader = xmlInputFactory.createFilteredReader(xmlEventReader, new EventFilter() { xmlEventReader = xmlInputFactory.createFilteredReader(xmlEventReader, new EventFilter() {
public boolean accept(XMLEvent xmlEvent) { public boolean accept(XMLEvent xmlEvent) {

View file

@ -97,10 +97,9 @@ public class DocumentUtil {
* @throws ParserConfigurationException * @throws ParserConfigurationException
*/ */
public static Document createDocument() throws ConfigurationException { public static Document createDocument() throws ConfigurationException {
DocumentBuilderFactory factory = getDocumentBuilderFactory();
DocumentBuilder builder; DocumentBuilder builder;
try { try {
builder = factory.newDocumentBuilder(); builder = getDocumentBuilder();
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
throw new ConfigurationException(e); throw new ConfigurationException(e);
} }
@ -118,8 +117,7 @@ public class DocumentUtil {
*/ */
public static Document createDocumentWithBaseNamespace(String baseNamespace, String localPart) throws ProcessingException { public static Document createDocumentWithBaseNamespace(String baseNamespace, String localPart) throws ProcessingException {
try { try {
DocumentBuilderFactory factory = getDocumentBuilderFactory(); DocumentBuilder builder = getDocumentBuilder();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.getDOMImplementation().createDocument(baseNamespace, localPart, null); return builder.getDOMImplementation().createDocument(baseNamespace, localPart, null);
} catch (DOMException e) { } catch (DOMException e) {
throw logger.processingError(e); throw logger.processingError(e);
@ -157,8 +155,7 @@ public class DocumentUtil {
*/ */
public static Document getDocument(Reader reader) throws ConfigurationException, ProcessingException, ParsingException { public static Document getDocument(Reader reader) throws ConfigurationException, ProcessingException, ParsingException {
try { try {
DocumentBuilderFactory factory = getDocumentBuilderFactory(); DocumentBuilder builder = getDocumentBuilder();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(reader)); return builder.parse(new InputSource(reader));
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
throw logger.configurationError(e); throw logger.configurationError(e);
@ -181,9 +178,8 @@ public class DocumentUtil {
* @throws SAXException * @throws SAXException
*/ */
public static Document getDocument(File file) throws ConfigurationException, ProcessingException, ParsingException { public static Document getDocument(File file) throws ConfigurationException, ProcessingException, ParsingException {
DocumentBuilderFactory factory = getDocumentBuilderFactory();
try { try {
DocumentBuilder builder = factory.newDocumentBuilder(); DocumentBuilder builder = getDocumentBuilder();
return builder.parse(file); return builder.parse(file);
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
throw logger.configurationError(e); throw logger.configurationError(e);
@ -206,9 +202,8 @@ public class DocumentUtil {
* @throws SAXException * @throws SAXException
*/ */
public static Document getDocument(InputStream is) throws ConfigurationException, ProcessingException, ParsingException { public static Document getDocument(InputStream is) throws ConfigurationException, ProcessingException, ParsingException {
DocumentBuilderFactory factory = getDocumentBuilderFactory();
try { try {
DocumentBuilder builder = factory.newDocumentBuilder(); DocumentBuilder builder = getDocumentBuilder();
return builder.parse(is); return builder.parse(is);
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
throw logger.configurationError(e); throw logger.configurationError(e);
@ -502,6 +497,25 @@ public class DocumentUtil {
} }
} }
private static final ThreadLocal<DocumentBuilder> XML_DOCUMENT_BUILDER = new ThreadLocal<DocumentBuilder>() {
@Override
protected DocumentBuilder initialValue() {
DocumentBuilderFactory factory = getDocumentBuilderFactory();
try {
return factory.newDocumentBuilder();
} catch (ParserConfigurationException ex) {
throw new RuntimeException(ex);
}
}
};
private static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
DocumentBuilder res = XML_DOCUMENT_BUILDER.get();
res.reset();
return res;
}
/** /**
* <p> Creates a namespace aware {@link DocumentBuilderFactory}. The returned instance is cached and shared between * <p> Creates a namespace aware {@link DocumentBuilderFactory}. The returned instance is cached and shared between
* different threads. </p> * different threads. </p>

View file

@ -26,7 +26,7 @@ import java.security.PrivilegedAction;
* @author Anil.Saldhana@redhat.com * @author Anil.Saldhana@redhat.com
* @since Dec 9, 2008 * @since Dec 9, 2008
*/ */
class SecurityActions { public class SecurityActions {
/** /**
* <p> Loads a {@link Class} using the <code>fullQualifiedName</code> supplied. This method tries first to load from * <p> Loads a {@link Class} using the <code>fullQualifiedName</code> supplied. This method tries first to load from
@ -186,7 +186,7 @@ class SecurityActions {
* *
* @return * @return
*/ */
static ClassLoader getTCCL() { public static ClassLoader getTCCL() {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() { public ClassLoader run() {
@ -203,7 +203,7 @@ class SecurityActions {
* *
* @param paramCl * @param paramCl
*/ */
static void setTCCL(final ClassLoader paramCl) { public static void setTCCL(final ClassLoader paramCl) {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() { AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() { public Void run() {

View file

@ -24,6 +24,8 @@ import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -47,6 +49,7 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import javax.xml.validation.Validator;
import java.io.InputStream; import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Utility for the stax based parser * Utility for the stax based parser
@ -122,7 +125,6 @@ public class StaxParserUtil {
*/ */
public static String getAttributeValue(Attribute attribute) { public static String getAttributeValue(Attribute attribute) {
String str = trim(attribute.getValue()); String str = trim(attribute.getValue());
str = StringUtil.getSystemPropertyAsString(str);
return str; return str;
} }
@ -224,7 +226,6 @@ public class StaxParserUtil {
String str = null; String str = null;
try { try {
str = xmlEventReader.getElementText().trim(); str = xmlEventReader.getElementText().trim();
str = StringUtil.getSystemPropertyAsString(str);
} catch (XMLStreamException e) { } catch (XMLStreamException e) {
throw logger.parserException(e); throw logger.parserException(e);
} }
@ -250,6 +251,34 @@ public class StaxParserUtil {
return xmlEventReader; return xmlEventReader;
} }
private static AtomicBoolean XML_EVENT_READER_ON_SOURCE_SUPPORTED = new AtomicBoolean(true);
/**
* Get the XML event reader
*
* @param source
*
* @return
*/
public static XMLEventReader getXMLEventReader(Source source) {
XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get();
try {
if (XML_EVENT_READER_ON_SOURCE_SUPPORTED.get()) {
try {
// The following method is optional per specification
return xmlInputFactory.createXMLEventReader(source);
} catch (UnsupportedOperationException ex) {
XML_EVENT_READER_ON_SOURCE_SUPPORTED.set(false);
return getXMLEventReader(source);
}
} else {
return getXMLEventReader(DocumentUtil.getSourceAsStream(source));
}
} catch (ConfigurationException | ProcessingException | XMLStreamException ex) {
throw new RuntimeException(ex);
}
}
/** /**
* Given a {@code Location}, return a formatted string [lineNum,colNum] * Given a {@code Location}, return a formatted string [lineNum,colNum]
* *

View file

@ -59,6 +59,9 @@ public class StringUtil {
return str == null || str.isEmpty(); return str == null || str.isEmpty();
} }
private static final Pattern PROPERTY_REPLACEMENT = Pattern.compile("(.*?)" + "\\$\\{(.*?)" + "(?:::(.*?))?\\}");
// 1: PREFIX | START 2: NAME | 3: OPTIONAL DEFAULT VALUE
/** /**
* <p> * <p>
* Get the system property value if the string is of the format ${sysproperty} * Get the system property value if the string is of the format ${sysproperty}
@ -84,37 +87,25 @@ public class StringUtil {
public static String getSystemPropertyAsString(String str) { public static String getSystemPropertyAsString(String str) {
if (str == null) if (str == null)
throw logger.nullArgumentError("str"); throw logger.nullArgumentError("str");
if (str.contains("${")) {
Pattern pattern = Pattern.compile("\\$\\{([^}]+)}");
Matcher matcher = pattern.matcher(str);
StringBuffer buffer = new StringBuffer(); Matcher m = PROPERTY_REPLACEMENT.matcher(str);
String sysPropertyValue = null; StringBuilder sb = new StringBuilder();
int lastPosition = 0;
while (m.find()) {
String propertyName = m.group(2);
String defaultValue = m.group(3);
while (matcher.find()) { String sysPropertyValue = SecurityActions.getSystemProperty(propertyName, defaultValue);
String subString = matcher.group(1); if (sysPropertyValue.isEmpty()) {
String defaultValue = ""; throw logger.systemPropertyMissingError(propertyName);
// Look for default value
if (subString.contains("::")) {
int index = subString.indexOf("::");
defaultValue = subString.substring(index + 2);
subString = subString.substring(0, index);
}
sysPropertyValue = SecurityActions.getSystemProperty(subString, defaultValue);
if (sysPropertyValue.isEmpty()) {
throw logger.systemPropertyMissingError(matcher.group(1));
}else{
// sanitize the value before we use append-and-replace
sysPropertyValue = Matcher.quoteReplacement(sysPropertyValue);
}
matcher.appendReplacement(buffer, sysPropertyValue);
} }
matcher.appendTail(buffer); sb.append(m.group(1)).append(sysPropertyValue);
str = buffer.toString();
lastPosition = m.end();
} }
return str;
return sb.append(str.substring(lastPosition)).toString();
} }
/** /**

View file

@ -165,7 +165,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
SAML2Object requestType = (SAML2Object) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); SAML2Object requestType = (SAML2Object) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
@ -192,7 +192,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
RequestAbstractType requestType = (RequestAbstractType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); RequestAbstractType requestType = (RequestAbstractType) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
@ -220,7 +220,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
AuthnRequestType requestType = (AuthnRequestType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); AuthnRequestType requestType = (AuthnRequestType) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
} }

View file

@ -376,7 +376,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
return (EncryptedAssertionType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); return (EncryptedAssertionType) samlParser.parse(samlDocument);
} }
@ -398,7 +398,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
return (AssertionType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); return (AssertionType) samlParser.parse(samlDocument);
} }
/** /**
@ -429,7 +429,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument);
ResponseType responseType = (ResponseType) samlParser.parse(DocumentUtil.getNodeAsStream(samlResponseDocument)); ResponseType responseType = (ResponseType) samlParser.parse(samlResponseDocument);
samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument); samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument);
return responseType; return responseType;
@ -460,8 +460,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument);
InputStream responseStream = DocumentUtil.getNodeAsStream(samlResponseDocument); SAML2Object responseType = (SAML2Object) samlParser.parse(samlResponseDocument);
SAML2Object responseType = (SAML2Object) samlParser.parse(responseStream);
samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument); samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument);
return responseType; return responseType;

View file

@ -51,15 +51,11 @@ public class XMLTimeUtil {
* *
* @throws org.keycloak.saml.common.exceptions.ConfigurationException * @throws org.keycloak.saml.common.exceptions.ConfigurationException
*/ */
public static XMLGregorianCalendar add(XMLGregorianCalendar value, long milis) throws ConfigurationException { public static XMLGregorianCalendar add(XMLGregorianCalendar value, long milis) {
XMLGregorianCalendar newVal = (XMLGregorianCalendar) value.clone(); XMLGregorianCalendar newVal = (XMLGregorianCalendar) value.clone();
Duration duration; Duration duration;
try { duration = DATATYPE_FACTORY.get().newDuration(milis);
duration = newDatatypeFactory().newDuration(milis);
} catch (DatatypeConfigurationException e) {
throw logger.configurationError(e);
}
newVal.add(duration); newVal.add(duration);
return newVal; return newVal;
} }
@ -74,7 +70,7 @@ public class XMLTimeUtil {
* *
* @throws ConfigurationException * @throws ConfigurationException
*/ */
public static XMLGregorianCalendar subtract(XMLGregorianCalendar value, long milis) throws ConfigurationException { public static XMLGregorianCalendar subtract(XMLGregorianCalendar value, long milis) {
if (milis < 0) if (milis < 0)
throw logger.invalidArgumentError("milis should be a positive value"); throw logger.invalidArgumentError("milis should be a positive value");
return add(value, -1 * milis); return add(value, -1 * milis);
@ -91,14 +87,10 @@ public class XMLTimeUtil {
* *
* @throws ConfigurationException * @throws ConfigurationException
*/ */
public static XMLGregorianCalendar getIssueInstant(String timezone) throws ConfigurationException { public static XMLGregorianCalendar getIssueInstant(String timezone) {
TimeZone tz = TimeZone.getTimeZone(timezone); TimeZone tz = TimeZone.getTimeZone(timezone);
DatatypeFactory dtf; DatatypeFactory dtf;
try { dtf = DATATYPE_FACTORY.get();
dtf = newDatatypeFactory();
} catch (DatatypeConfigurationException e) {
throw logger.configurationError(e);
}
GregorianCalendar gc = new GregorianCalendar(tz); GregorianCalendar gc = new GregorianCalendar(tz);
XMLGregorianCalendar xgc = dtf.newXMLGregorianCalendar(gc); XMLGregorianCalendar xgc = dtf.newXMLGregorianCalendar(gc);
@ -188,13 +180,7 @@ public class XMLTimeUtil {
PicketLinkLoggerFactory.getLogger().nullArgumentError("duration time"); PicketLinkLoggerFactory.getLogger().nullArgumentError("duration time");
} }
DatatypeFactory factory = null; DatatypeFactory factory = DATATYPE_FACTORY.get();
try {
factory = newDatatypeFactory();
} catch (DatatypeConfigurationException e) {
throw logger.parserError(e);
}
try { try {
// checks if it is a ISO 8601 period. If not it must be a numeric value. // checks if it is a ISO 8601 period. If not it must be a numeric value.
@ -218,15 +204,20 @@ public class XMLTimeUtil {
* @throws ParsingException * @throws ParsingException
*/ */
public static XMLGregorianCalendar parse(String timeString) throws ParsingException { public static XMLGregorianCalendar parse(String timeString) throws ParsingException {
DatatypeFactory factory = null; DatatypeFactory factory = DATATYPE_FACTORY.get();
try {
factory = newDatatypeFactory();
} catch (DatatypeConfigurationException e) {
throw logger.parserError(e);
}
return factory.newXMLGregorianCalendar(timeString); return factory.newXMLGregorianCalendar(timeString);
} }
private static final ThreadLocal<DatatypeFactory> DATATYPE_FACTORY = new ThreadLocal<DatatypeFactory>() {
@Override
protected DatatypeFactory initialValue() {
try {
return newDatatypeFactory();
} catch (DatatypeConfigurationException e) {
throw new RuntimeException(e);
}
}
};
/** /**
* Create a new {@link DatatypeFactory} * Create a new {@link DatatypeFactory}
@ -235,7 +226,7 @@ public class XMLTimeUtil {
* *
* @throws DatatypeConfigurationException * @throws DatatypeConfigurationException
*/ */
public static DatatypeFactory newDatatypeFactory() throws DatatypeConfigurationException { private static DatatypeFactory newDatatypeFactory() throws DatatypeConfigurationException {
boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false") boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false")
.equalsIgnoreCase("true"); .equalsIgnoreCase("true");
ClassLoader prevTCCL = SecurityActions.getTCCL(); ClassLoader prevTCCL = SecurityActions.getTCCL();

View file

@ -0,0 +1,47 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.keycloak.saml.common.util;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
/**
*
* @author hmlnarik
*/
public class StringUtilTest {
public StringUtilTest() {
}
@Test
public void testGetSystemPropertyAsString() {
System.setProperty("StringUtilTest.prop1", "value1");
System.setProperty("StringUtilTest.prop2", "value2");
assertThat(StringUtil.getSystemPropertyAsString("a"), is("a"));
assertThat(StringUtil.getSystemPropertyAsString("a ${StringUtilTest.prop1}"), is("a value1"));
assertThat(
StringUtil.getSystemPropertyAsString("a" + "${StringUtilTest.prop1}" + "StringUtilTest.prop1"),
is("a" + "value1" + "StringUtilTest.prop1")
);
assertThat(
StringUtil.getSystemPropertyAsString("a" + "${StringUtilTest.prop1}" + "StringUtilTest.prop1" + "${StringUtilTest.prop2}"),
is("a" + "value1" + "StringUtilTest.prop1" + "value2")
);
assertThat(
StringUtil.getSystemPropertyAsString("a" + "${StringUtilTest.prop1}" + "StringUtilTest.prop1" + "${StringUtilTest.prop2}" + "${StringUtilTest.prop3::abc}"),
is("a" + "value1" + "StringUtilTest.prop1" + "value2" + "abc")
);
assertThat(
StringUtil.getSystemPropertyAsString("a" + "${StringUtilTest.prop1}" + "StringUtilTest.prop1" + "${StringUtilTest.prop2}" + "${StringUtilTest.prop3::abc}" + "end"),
is("a" + "value1" + "StringUtilTest.prop1" + "value2" + "abc" + "end")
);
}
}

View file

@ -2218,7 +2218,13 @@ public class RepresentationToModel {
} }
} }
if (!hasResource && !"".equals(resourceId)) { if (!hasResource && !"".equals(resourceId)) {
policy.addResource(storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId())); Resource resource = storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId());
if (resource == null) {
throw new RuntimeException("Resource [" + resourceId + "] not found.");
}
policy.addResource(resource);
} }
} }

View file

@ -74,8 +74,15 @@ public class ResourceServerService {
public void create() { public void create() {
this.auth.requireManage(); this.auth.requireManage();
UserModel serviceAccount = this.session.users().getServiceAccount(client);
if (serviceAccount == null) {
throw new RuntimeException("Client does not have a service account.");
}
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId()); this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId());
createDefaultRoles(); createDefaultRoles(serviceAccount);
createDefaultPermission(createDefaultResource(), createDefaultPolicy()); createDefaultPermission(createDefaultResource(), createDefaultPolicy());
} }
@ -215,15 +222,13 @@ public class ResourceServerService {
return defaultResource; return defaultResource;
} }
private void createDefaultRoles() { private void createDefaultRoles(UserModel serviceAccount) {
RoleModel umaProtectionRole = client.getRole(Constants.AUTHZ_UMA_PROTECTION); RoleModel umaProtectionRole = client.getRole(Constants.AUTHZ_UMA_PROTECTION);
if (umaProtectionRole == null) { if (umaProtectionRole == null) {
umaProtectionRole = client.addRole(Constants.AUTHZ_UMA_PROTECTION); umaProtectionRole = client.addRole(Constants.AUTHZ_UMA_PROTECTION);
} }
UserModel serviceAccount = this.session.users().getServiceAccount(client);
if (!serviceAccount.hasRole(umaProtectionRole)) { if (!serviceAccount.hasRole(umaProtectionRole)) {
serviceAccount.grantRole(umaProtectionRole); serviceAccount.grantRole(umaProtectionRole);
} }

View file

@ -154,8 +154,12 @@ public class ClientResource {
} }
public void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException { public void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
if (TRUE.equals(rep.isServiceAccountsEnabled()) && !client.isServiceAccountsEnabled()) { if (TRUE.equals(rep.isServiceAccountsEnabled())) {
new ClientManager(new RealmManager(session)).enableServiceAccount(client); UserModel serviceAccount = this.session.users().getServiceAccount(client);
if (serviceAccount == null) {
new ClientManager(new RealmManager(session)).enableServiceAccount(client);
}
} }
if (!rep.getClientId().equals(client.getClientId())) { if (!rep.getClientId().equals(client.getClientId())) {

View file

@ -16,20 +16,26 @@
*/ */
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import static java.lang.Boolean.TRUE;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.admin.AuthorizationService;
import org.keycloak.common.Profile;
import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType; import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.ClientValidator;
import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.PairwiseClientValidator;
import org.keycloak.services.validation.ValidationMessages; import org.keycloak.services.validation.ValidationMessages;
@ -93,7 +99,17 @@ public class ClientsResource {
boolean view = auth.hasView(); boolean view = auth.hasView();
for (ClientModel clientModel : clientModels) { for (ClientModel clientModel : clientModels) {
if (view) { if (view) {
rep.add(ModelToRepresentation.toRepresentation(clientModel)); ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel);
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
AuthorizationService authorizationService = getAuthorizationService(clientModel);
if (authorizationService.isEnabled()) {
representation.setAuthorizationServicesEnabled(true);
}
}
rep.add(representation);
} else { } else {
ClientRepresentation client = new ClientRepresentation(); ClientRepresentation client = new ClientRepresentation();
client.setId(clientModel.getId()); client.setId(clientModel.getId());
@ -111,6 +127,10 @@ public class ClientsResource {
return rep; return rep;
} }
private AuthorizationService getAuthorizationService(ClientModel clientModel) {
return new AuthorizationService(session, clientModel, auth);
}
/** /**
* Create a new client * Create a new client
* *
@ -138,6 +158,20 @@ public class ClientsResource {
try { try {
ClientModel clientModel = ClientManager.createClient(session, realm, rep, true); ClientModel clientModel = ClientManager.createClient(session, realm, rep, true);
if (TRUE.equals(rep.isServiceAccountsEnabled())) {
UserModel serviceAccount = session.users().getServiceAccount(clientModel);
if (serviceAccount == null) {
new ClientManager(new RealmManager(session)).enableServiceAccount(clientModel);
}
}
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
if (TRUE.equals(rep.getAuthorizationServicesEnabled())) {
getAuthorizationService(clientModel).enable();
}
}
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientModel.getId()).representation(rep).success(); adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientModel.getId()).representation(rep).success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(clientModel.getId()).build()).build(); return Response.created(uriInfo.getAbsolutePathBuilder().path(clientModel.getId()).build()).build();

View file

@ -82,6 +82,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@ -656,6 +658,13 @@ public class TestingResourceProvider implements RealmResourceProvider {
return session.getContext().getRealm().getIdentityProviderByAlias(alias).getConfig(); return session.getContext().getRealm().getIdentityProviderByAlias(alias).getConfig();
} }
@PUT
@Path("/set-krb5-conf-file")
@Consumes(MediaType.APPLICATION_JSON)
public void setKrb5ConfFile(@QueryParam("krb5-conf-file") String krb5ConfFile) {
System.setProperty("java.security.krb5.conf", krb5ConfFile);
}
private RealmModel getRealmByName(String realmName) { private RealmModel getRealmByName(String realmName) {
RealmProvider realmProvider = session.getProvider(RealmProvider.class); RealmProvider realmProvider = session.getProvider(RealmProvider.class);
return realmProvider.getRealmByName(realmName); return realmProvider.getRealmByName(realmName);

View file

@ -88,6 +88,16 @@ module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location,
$location.path('/'); $location.path('/');
}); });
}; };
$scope.createWithInvalidUser = function () {
var newAlbum = new Album($scope.album);
newAlbum.$save({user: 'invalidUser'}, function (data) {
document.getElementById("output").innerHTML = 'Request was successful'
},
function (response) {
document.getElementById("output").innerHTML = response.data;
});
};
}); });
module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $location, Profile) { module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $location, Profile) {

View file

@ -4,4 +4,5 @@
Name: <input type="text" id="album.name" ng-model="album.name"/> Name: <input type="text" id="album.name" ng-model="album.name"/>
<button ng-click="create()" id="save-album">Save</button> <button ng-click="create()" id="save-album">Save</button>
<button ng-click="createWithInvalidUser()" id="save-album-invalid">Save with invalid user</button>
</form> </form>

View file

@ -22,6 +22,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
@ -54,17 +55,18 @@ public class AlbumService {
@POST @POST
@Consumes("application/json") @Consumes("application/json")
public Response create(Album newAlbum) { public Response create(Album newAlbum, @QueryParam("user") String username) {
newAlbum.setId(++nextId); newAlbum.setId(++nextId);
Principal userPrincipal = request.getUserPrincipal(); if (username == null) {
username = request.getUserPrincipal().getName();
newAlbum.setUserId(userPrincipal.getName()); }
newAlbum.setUserId(username);
Query queryDuplicatedAlbum = this.entityManager.createQuery("from Album where name = :name and userId = :userId"); Query queryDuplicatedAlbum = this.entityManager.createQuery("from Album where name = :name and userId = :userId");
queryDuplicatedAlbum.setParameter("name", newAlbum.getName()); queryDuplicatedAlbum.setParameter("name", newAlbum.getName());
queryDuplicatedAlbum.setParameter("userId", userPrincipal.getName()); queryDuplicatedAlbum.setParameter("userId", username);
if (!queryDuplicatedAlbum.getResultList().isEmpty()) { if (!queryDuplicatedAlbum.getResultList().isEmpty()) {
throw new ErrorResponse("Name [" + newAlbum.getName() + "] already taken. Choose another one.", Status.CONFLICT); throw new ErrorResponse("Name [" + newAlbum.getName() + "] already taken. Choose another one.", Status.CONFLICT);

View file

@ -53,23 +53,34 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
protected ConsentPage consentPage; protected ConsentPage consentPage;
@FindBy(xpath = "//a[@ng-click = 'Identity.logout()']") @FindBy(xpath = "//a[@ng-click = 'Identity.logout()']")
WebElement signOutButton; private WebElement signOutButton;
@FindBy(id = "entitlement") @FindBy(id = "entitlement")
WebElement entitlement; private WebElement entitlement;
@FindBy(id = "entitlements") @FindBy(id = "entitlements")
WebElement entitlements; private WebElement entitlements;
@FindBy(id = "output")
private WebElement output;
public void createAlbum(String name) { public void createAlbum(String name) {
createAlbum(name, "save-album");
}
public void createAlbum(String name, String buttonId) {
navigateTo(); navigateTo();
this.driver.findElement(By.id("create-album")).click(); this.driver.findElement(By.id("create-album")).click();
Form.setInputValue(this.driver.findElement(By.id("album.name")), name); Form.setInputValue(this.driver.findElement(By.id("album.name")), name);
pause(200); // We need to wait a bit for the form to "accept" the input (otherwise it registers the input as empty) pause(200); // We need to wait a bit for the form to "accept" the input (otherwise it registers the input as empty)
this.driver.findElement(By.id("save-album")).click(); this.driver.findElement(By.id(buttonId)).click();
pause(WAIT_AFTER_OPERATION); pause(WAIT_AFTER_OPERATION);
} }
public void createAlbumWithInvalidUser(String name) {
createAlbum(name, "save-album-invalid");
}
@Override @Override
public URL getInjectedUrl() { public URL getInjectedUrl() {
return this.url; return this.url;
@ -137,6 +148,10 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
pause(WAIT_AFTER_OPERATION); pause(WAIT_AFTER_OPERATION);
} }
public WebElement getOutput() {
return output;
}
@Override @Override
public void navigateTo(boolean waitForMatch) { public void navigateTo(boolean waitForMatch) {
super.navigateTo(waitForMatch); super.navigateTo(waitForMatch);

View file

@ -256,4 +256,9 @@ public interface TestingResource {
@Path("/component") @Path("/component")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
MultivaluedHashMap<String, String> getComponentConfig(@QueryParam("componentId") String componentId); MultivaluedHashMap<String, String> getComponentConfig(@QueryParam("componentId") String componentId);
@PUT
@Path("/set-krb5-conf-file")
@Consumes(MediaType.APPLICATION_JSON)
void setKrb5ConfFile(@QueryParam("krb5-conf-file") String krb5ConfFile);
} }

View file

@ -15,17 +15,17 @@
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.testsuite.rule; package org.keycloak.testsuite.util;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.federation.ldap.LDAPTestConfiguration;
import org.keycloak.util.ldap.KerberosEmbeddedServer;
import org.keycloak.util.ldap.LDAPEmbeddedServer;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.util.Properties; import java.util.Properties;
import org.jboss.logging.Logger;
import org.keycloak.testsuite.client.resources.TestingResource;
import org.keycloak.util.ldap.KerberosEmbeddedServer;
import org.keycloak.util.ldap.LDAPEmbeddedServer;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@ -36,18 +36,24 @@ public class KerberosRule extends LDAPRule {
private final String configLocation; private final String configLocation;
public KerberosRule(String configLocation) { public KerberosRule(String configLocation) {
this(configLocation, null);
}
public KerberosRule(String configLocation, LDAPRuleCondition condition) {
super(condition);
this.configLocation = configLocation; this.configLocation = configLocation;
// Global kerberos configuration // Global kerberos configuration
String krb5ConfPath = getKrb5ConfPath();
System.setProperty("java.security.krb5.conf", krb5ConfPath);
}
private String getKrb5ConfPath() {
URL krb5ConfURL = LDAPTestConfiguration.class.getResource("/kerberos/test-krb5.conf"); URL krb5ConfURL = LDAPTestConfiguration.class.getResource("/kerberos/test-krb5.conf");
String krb5ConfPath = new File(krb5ConfURL.getFile()).getAbsolutePath(); String krb5ConfPath = new File(krb5ConfURL.getFile()).getAbsolutePath();
log.info("Krb5.conf file location is: " + krb5ConfPath); log.info("Krb5.conf file location is: " + krb5ConfPath);
return krb5ConfPath;
}
public void setKrb5ConfPath(TestingResource testingResource) {
String krb5ConfPath = getKrb5ConfPath();
System.setProperty("java.security.krb5.conf", krb5ConfPath); System.setProperty("java.security.krb5.conf", krb5ConfPath);
testingResource.setKrb5ConfFile(krb5ConfPath); // Needs to set it on wildfly server too
} }
@Override @Override
@ -63,4 +69,8 @@ public class KerberosRule extends LDAPRule {
return new KerberosEmbeddedServer(defaultProperties); return new KerberosEmbeddedServer(defaultProperties);
} }
public boolean isCaseSensitiveLogin() {
return ldapTestConfiguration.isCaseSensitiveLogin();
}
} }

View file

@ -28,6 +28,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -88,7 +89,7 @@ public class LDAPTestConfiguration {
DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false"); DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG"); DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG"); DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
String keyTabPath = getResource("http.keytab"); String keyTabPath = getResource("/kerberos/http.keytab");
DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath); DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true"); DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true"); DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true");
@ -102,8 +103,10 @@ public class LDAPTestConfiguration {
return ldapTestConfiguration; return ldapTestConfiguration;
} }
public static String getResource(String resourceName) { public static String getResource(String resourcePath) {
return new File(PROJECT_BUILD_DIRECTORY, "dependency/kerberos/" + resourceName).getAbsolutePath(); URL urlPath = LDAPTestConfiguration.class.getResource(resourcePath);
String absolutePath = new File(urlPath.getFile()).getAbsolutePath();
return absolutePath;
} }
protected void loadConnectionProperties(String connectionPropertiesLocation) { protected void loadConnectionProperties(String connectionPropertiesLocation) {

View file

@ -59,6 +59,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.util.IOUtil.loadJson; import static org.keycloak.testsuite.util.IOUtil.loadJson;
import static org.keycloak.testsuite.util.IOUtil.loadRealm; import static org.keycloak.testsuite.util.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -141,6 +142,22 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
} }
} }
@Test
public void createAlbumWithInvalidUser() {
try {
this.deployer.deploy(RESOURCE_SERVER_ID);
loginToClientPage("alice", "alice");
clientPage.createAlbumWithInvalidUser("Alice Family Album");
waitUntilElement(clientPage.getOutput()).text().not().contains("Request was successful");
waitUntilElement(clientPage.getOutput()).text().contains("Could not register protected resource");
} finally {
this.deployer.undeploy(RESOURCE_SERVER_ID);
}
}
@Test @Test
public void testOnlyOwnerCanDeleteAlbum() throws Exception { public void testOnlyOwnerCanDeleteAlbum() throws Exception {
try { try {

View file

@ -1,130 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.adapter.federation;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.impl.auth.SPNegoScheme;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.params.HttpParams;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
import javax.security.auth.Subject;
import java.security.PrivilegedExceptionAction;
/**
* Usable for testing only. Username and password are shared for the whole factory
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KeycloakSPNegoSchemeFactory extends SPNegoSchemeFactory {
private final CommonKerberosConfig kerberosConfig;
private String username;
private String password;
public KeycloakSPNegoSchemeFactory(CommonKerberosConfig kerberosConfig) {
super(true, false);
this.kerberosConfig = kerberosConfig;
}
public void setCredentials(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public AuthScheme newInstance(HttpParams params) {
return new KeycloakSPNegoScheme(isStripPort(), isUseCanonicalHostname());
}
public class KeycloakSPNegoScheme extends SPNegoScheme {
public KeycloakSPNegoScheme(boolean stripPort, boolean useCanonicalHostname) {
super(stripPort, useCanonicalHostname);
}
@Override
protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer, Credentials credentials) throws GSSException {
KerberosUsernamePasswordAuthenticator authenticator = new KerberosUsernamePasswordAuthenticator(kerberosConfig);
try {
Subject clientSubject = authenticator.authenticateSubject(username, password);
ByteArrayHolder holder = Subject.doAs(clientSubject, new ClientAcceptSecContext(input, oid, authServer));
return holder.bytes;
} catch (Exception le) {
throw new RuntimeException(le);
} finally {
authenticator.logoutSubject();
}
}
private class ClientAcceptSecContext implements PrivilegedExceptionAction<ByteArrayHolder> {
private final byte[] input;
private final Oid oid;
private final String authServer;
public ClientAcceptSecContext(byte[] input, Oid oid, String authServer) {
this.input = input;
this.oid = oid;
this.authServer = authServer;
}
@Override
public ByteArrayHolder run() throws Exception {
byte[] token = input;
if (token == null) {
token = new byte[0];
}
GSSManager manager = getManager();
GSSName serverName = manager.createName("HTTP/" + authServer + "@" + kerberosConfig.getKerberosRealm(), null);
GSSContext gssContext = manager.createContext(
serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
gssContext.requestMutualAuth(true);
gssContext.requestCredDeleg(true);
byte[] outputToken = gssContext.initSecContext(token, 0, token.length);
ByteArrayHolder result = new ByteArrayHolder();
result.bytes = outputToken;
return result;
}
}
private class ByteArrayHolder {
private byte[] bytes;
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.admin;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.Response;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Assume;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
*/
public class ImpersonationDisabledTest extends AbstractAdminTest {
@BeforeClass
public static void enabled() {
Assume.assumeTrue("impersonation".equals(System.getProperty("feature.name"))
&& "disabled".equals(System.getProperty("feature.value")));
}
@Test
public void testImpersonationDisabled() {
String impersonatedUserId = adminClient.realm(TEST).users().search("test-user@localhost", 0, 1).get(0).getId();
try {
adminClient.realms().realm("test").users().get(impersonatedUserId).impersonate();
} catch (ServerErrorException e) {
assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), e.getResponse().getStatus());
return;
}
fail("Feature impersonation should be disabled.");
}
}

View file

@ -47,6 +47,8 @@ import javax.ws.rs.core.Response;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Assume;
import org.junit.BeforeClass;
/** /**
* Tests Undertow Adapter * Tests Undertow Adapter
@ -81,6 +83,12 @@ public class ImpersonationTest extends AbstractKeycloakTest {
testRealms.add(realm.build()); testRealms.add(realm.build());
} }
@BeforeClass
public static void enabled() {
Assume.assumeFalse("impersonation".equals(System.getProperty("feature.name"))
&& "disabled".equals(System.getProperty("feature.value")));
}
@Test @Test
public void testImpersonateByMasterAdmin() { public void testImpersonateByMasterAdmin() {
// test that composite is set up right for impersonation role // test that composite is set up right for impersonation role

View file

@ -26,6 +26,7 @@ import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -44,7 +45,9 @@ public class AuthorizationDisabledInPreviewTest extends AbstractClientTest {
testRealmResource().clients().get(id).authorization().getSettings(); testRealmResource().clients().get(id).authorization().getSettings();
} catch (ServerErrorException e) { } catch (ServerErrorException e) {
assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), e.getResponse().getStatus()); assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), e.getResponse().getStatus());
return;
} }
fail("Feature Authorization should be disabled.");
} }
} }

View file

@ -0,0 +1,443 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.kerberos;
import java.net.URI;
import java.security.Principal;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.sasl.Sasl;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.ietf.jgss.GSSCredential;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.HttpClientBuilder;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.KerberosSerializationUtils;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.events.Details;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.pages.AccountPasswordPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.OAuthClient;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractKerberosTest extends AbstractAuthTest {
protected KeycloakSPNegoSchemeFactory spnegoSchemeFactory;
protected ResteasyClient client;
@Page
protected LoginPage loginPage;
@Rule
public AssertEvents events = new AssertEvents(this);
@Page
protected AccountPasswordPage changePasswordPage;
protected abstract CommonKerberosConfig getKerberosConfig();
protected abstract ComponentRepresentation getUserStorageConfiguration();
protected abstract void setKrb5ConfPath();
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation realmRep = loadJson(getClass().getResourceAsStream("/kerberos/kerberosrealm.json"), RealmRepresentation.class);
testRealms.add(realmRep);
}
@Before
public void beforeAbstractKeycloakTest() throws Exception {
super.beforeAbstractKeycloakTest();
testRealmPage.setAuthRealm(AuthRealm.TEST);
changePasswordPage.realm(AuthRealm.TEST);
setKrb5ConfPath();
spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig());
initHttpClient(true);
removeAllUsers();
oauth.clientId("kerberos-app");
ComponentRepresentation rep = getUserStorageConfiguration();
testRealmResource().components().add(rep);
}
@After
public void afterAbstractKeycloakTest() {
cleanupApacheHttpClient();
super.afterAbstractKeycloakTest();
}
private void cleanupApacheHttpClient() {
client.close();
client = null;
}
// @Test
// public void sleepTest() throws Exception {
// String kcLoginPageLocation = oauth.getLoginFormUrl();
// Thread.sleep(10000000);
// }
@Test
public void spnegoNotAvailableTest() throws Exception {
initHttpClient(false);
String kcLoginPageLocation = oauth.getLoginFormUrl();
Response response = client.target(kcLoginPageLocation).request().get();
Assert.assertEquals(401, response.getStatus());
Assert.assertEquals(KerberosConstants.NEGOTIATE, response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
String responseText = response.readEntity(String.class);
response.close();
}
protected OAuthClient.AccessTokenResponse spnegoLoginTestImpl() throws Exception {
Response spnegoResponse = spnegoLogin("hnelson", "secret");
Assert.assertEquals(302, spnegoResponse.getStatus());
List<UserRepresentation> users = testRealmResource().users().search("hnelson", 0, 1);
String userId = users.get(0).getId();
events.expectLogin()
.client("kerberos-app")
.user(userId)
.detail(Details.USERNAME, "hnelson")
.assertEvent();
String codeUrl = spnegoResponse.getLocation().toString();
return assertAuthenticationSuccess(codeUrl);
}
protected abstract boolean isCaseSensitiveLogin();
// KEYCLOAK-2102
@Test
public void spnegoCaseInsensitiveTest() throws Exception {
Response spnegoResponse = spnegoLogin(isCaseSensitiveLogin() ? "MyDuke" : "myduke", "theduke");
Assert.assertEquals(302, spnegoResponse.getStatus());
List<UserRepresentation> users = testRealmResource().users().search("myduke", 0, 1);
String userId = users.get(0).getId();
events.expectLogin()
.client("kerberos-app")
.user(userId)
.detail(Details.USERNAME, "myduke")
.assertEvent();
String codeUrl = spnegoResponse.getLocation().toString();
assertAuthenticationSuccess(codeUrl);
}
@Test
public void usernamePasswordLoginTest() throws Exception {
// Change editMode to READ_ONLY
updateProviderEditMode(UserStorageProvider.EditMode.READ_ONLY);
// Login with username/password from kerberos
changePasswordPage.open();
loginPage.assertCurrent();
loginPage.login("jduke", "theduke");
changePasswordPage.assertCurrent();
// Bad existing password
changePasswordPage.changePassword("theduke-invalid", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
// Change password is not possible as editMode is READ_ONLY
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(
driver.getPageSource().contains("You can't update your password as your account is read only"));
// Change editMode to UNSYNCED
updateProviderEditMode(UserStorageProvider.EditMode.UNSYNCED);
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Login with old password doesn't work, but with new password works
loginPage.login("jduke", "theduke");
loginPage.assertCurrent();
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.logout();
// Assert SPNEGO login still with the old password as mode is unsynced
events.clear();
Response spnegoResponse = spnegoLogin("jduke", "theduke");
Assert.assertEquals(302, spnegoResponse.getStatus());
List<UserRepresentation> users = testRealmResource().users().search("jduke", 0, 1);
String userId = users.get(0).getId();
events.expectLogin()
.client("kerberos-app")
.user(userId)
.detail(Details.USERNAME, "jduke")
.assertEvent();
String codeUrl = spnegoResponse.getLocation().toString();
assertAuthenticationSuccess(codeUrl);
}
@Test
public void credentialDelegationTest() throws Exception {
// Add kerberos delegation credential mapper
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
true, false);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
String protocolMapperId = ApiUtil.getCreatedId(response);
response.close();
// SPNEGO login
OAuthClient.AccessTokenResponse tokenResponse = spnegoLoginTestImpl();
// Assert kerberos ticket in the accessToken can be re-used to authenticate against other 3rd party kerberos service (ApacheDS Server in this case)
String accessToken = tokenResponse.getAccessToken();
AccessToken token = oauth.verifyToken(accessToken);
String serializedGssCredential = (String) token.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
Assert.assertNotNull(serializedGssCredential);
GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
String ldapResponse = invokeLdap(gssCredential, token.getPreferredUsername());
Assert.assertEquals("Horatio Nelson", ldapResponse);
// Logout
oauth.openLogout();
// Remove protocolMapper
clientResource.getProtocolMappers().delete(protocolMapperId);
// Login and assert delegated credential not anymore
tokenResponse = spnegoLoginTestImpl();
accessToken = tokenResponse.getAccessToken();
token = oauth.verifyToken(accessToken);
Assert.assertFalse(token.getOtherClaims().containsKey(KerberosConstants.GSS_DELEGATION_CREDENTIAL));
events.clear();
}
private String invokeLdap(GSSCredential gssCredential, String username) throws NamingException {
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
if (gssCredential != null) {
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
env.put(Sasl.CREDENTIALS, gssCredential);
}
DirContext ctx = new InitialDirContext(env);
try {
Attributes attrs = ctx.getAttributes("uid=" + username + ",ou=People,dc=keycloak,dc=org");
String cn = (String) attrs.get("cn").get();
String sn = (String) attrs.get("sn").get();
return cn + " " + sn;
} finally {
ctx.close();
}
}
protected Response spnegoLogin(String username, String password) {
String kcLoginPageLocation = oauth.getLoginFormUrl();
// Request for SPNEGO login sent with Resteasy client
spnegoSchemeFactory.setCredentials(username, password);
Response response = client.target(kcLoginPageLocation).request().get();
if (response.getStatus() == 302) {
if (response.getLocation() == null)
return response;
String uri = response.getLocation().toString();
if (uri.contains("login-actions/required-action")) {
response = client.target(uri).request().get();
}
}
return response;
}
protected void initHttpClient(boolean useSpnego) {
if (client != null) {
cleanupApacheHttpClient();
}
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO, spnegoSchemeFactory);
if (useSpnego) {
Credentials fake = new Credentials() {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
};
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1, null),
fake);
}
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
client = new ResteasyClientBuilder().httpEngine(engine).build();
}
protected void removeAllUsers() {
RealmResource realm = testRealmResource();
List<UserRepresentation> users = realm.users().search("", 0, Integer.MAX_VALUE);
for (UserRepresentation user : users) {
if (!user.getUsername().equals(AssertEvents.DEFAULT_USERNAME)) {
realm.users().get(user.getId()).remove();
}
}
Assert.assertEquals(1, realm.users().search("", 0, Integer.MAX_VALUE).size());
}
protected void assertUser(String expectedUsername, String expectedEmail, String expectedFirstname,
String expectedLastname, boolean updateProfileActionExpected) {
try {
UserRepresentation user = ApiUtil.findUserByUsername(testRealmResource(), expectedUsername);
Assert.assertNotNull(user);
Assert.assertEquals(expectedEmail, user.getEmail());
Assert.assertEquals(expectedFirstname, user.getFirstName());
Assert.assertEquals(expectedLastname, user.getLastName());
if (updateProfileActionExpected) {
Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(),
user.getRequiredActions().iterator().next());
} else {
Assert.assertTrue(user.getRequiredActions().isEmpty());
}
} finally {
}
}
protected OAuthClient.AccessTokenResponse assertAuthenticationSuccess(String codeUrl) throws Exception {
List<NameValuePair> pairs = URLEncodedUtils.parse(new URI(codeUrl), "UTF-8");
String code = null;
String state = null;
for (NameValuePair pair : pairs) {
if (pair.getName().equals(OAuth2Constants.CODE)) {
code = pair.getValue();
} else if (pair.getName().equals(OAuth2Constants.STATE)) {
state = pair.getValue();
}
}
Assert.assertNotNull(code);
Assert.assertNotNull(state);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertNotNull(response.getAccessToken());
events.clear();
return response;
}
protected void updateProviderEditMode(UserStorageProvider.EditMode editMode) {
List<ComponentRepresentation> reps = testRealmResource().components().query("test", UserStorageProvider.class.getName());
Assert.assertEquals(1, reps.size());
ComponentRepresentation kerberosProvider = reps.get(0);
kerberosProvider.getConfig().putSingle(LDAPConstants.EDIT_MODE, editMode.toString());
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
}
public RealmResource testRealmResource() {
return adminClient.realm("test");
}
// TODO: Use LDAPTestUtils.toComponentConfig once it's migrated to new testsuite
public static MultivaluedHashMap<String, String> toComponentConfig(Map<String, String> ldapConfig) {
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
for (Map.Entry<String, String> entry : ldapConfig.entrySet()) {
config.add(entry.getKey(), entry.getValue());
}
return config;
}
}

View file

@ -0,0 +1,150 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.kerberos;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.Response;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.events.Details;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
import org.keycloak.testsuite.util.KerberosRule;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KerberosLdapTest extends AbstractKerberosTest {
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-ldap-connection.properties";
@ClassRule
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
@Override
protected CommonKerberosConfig getKerberosConfig() {
return new LDAPProviderKerberosConfig(getUserStorageConfiguration());
}
@Override
protected ComponentRepresentation getUserStorageConfiguration() {
Map<String,String> kerberosConfig = kerberosRule.getConfig();
MultivaluedHashMap<String, String> config = toComponentConfig(kerberosConfig);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setLastSync(0);
model.setChangedSyncPeriod(-1);
model.setFullSyncPeriod(-1);
model.setName("kerberos-ldap");
model.setPriority(0);
model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME);
model.setConfig(config);
ComponentRepresentation rep = ModelToRepresentation.toRepresentationWithoutConfig(model);
return rep;
}
@Override
protected boolean isCaseSensitiveLogin() {
return kerberosRule.isCaseSensitiveLogin();
}
@Override
protected void setKrb5ConfPath() {
kerberosRule.setKrb5ConfPath(testingClient.testing());
}
@Test
public void spnegoLoginTest() throws Exception {
spnegoLoginTestImpl();
// Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP
assertUser("hnelson", "hnelson@keycloak.org", "Horatio", "Nelson", false);
}
@Test
public void writableEditModeTest() throws Exception {
// Change editMode to WRITABLE
updateProviderEditMode(UserStorageProvider.EditMode.WRITABLE);
// Login with username/password from kerberos
changePasswordPage.open();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
loginPage.assertCurrent();
loginPage.login("jduke", "theduke");
Assert.assertTrue(changePasswordPage.isCurrent());
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
// Login with old password doesn't work, but with new password works
loginPage.login("jduke", "theduke");
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.logout();
// Assert SPNEGO login with the new password as mode is writable
events.clear();
Response spnegoResponse = spnegoLogin("jduke", "newPass");
org.keycloak.testsuite.Assert.assertEquals(302, spnegoResponse.getStatus());
org.keycloak.testsuite.Assert.assertEquals(302, spnegoResponse.getStatus());
List<UserRepresentation> users = testRealmResource().users().search("jduke", 0, 1);
String userId = users.get(0).getId();
events.expectLogin()
.client("kerberos-app")
.user(userId)
.detail(Details.USERNAME, "jduke")
.assertEvent();
String codeUrl = spnegoResponse.getLocation().toString();
assertAuthenticationSuccess(codeUrl);
// Change password back
changePasswordPage.open();
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.changePassword("newPass", "theduke", "theduke");
}
}

View file

@ -0,0 +1,185 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.kerberos;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ws.rs.core.Response;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.federation.kerberos.KerberosConfig;
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.util.KerberosRule;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KerberosStandaloneTest extends AbstractKerberosTest {
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
@ClassRule
public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
@Override
protected CommonKerberosConfig getKerberosConfig() {
return new KerberosConfig(getUserStorageConfiguration());
}
@Override
protected ComponentRepresentation getUserStorageConfiguration() {
Map<String,String> kerberosConfig = kerberosRule.getConfig();
MultivaluedHashMap<String, String> config = toComponentConfig(kerberosConfig);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setLastSync(0);
model.setChangedSyncPeriod(-1);
model.setFullSyncPeriod(-1);
model.setName("kerberos-standalone");
model.setPriority(0);
model.setProviderId(KerberosFederationProviderFactory.PROVIDER_NAME);
model.setConfig(config);
ComponentRepresentation rep = ModelToRepresentation.toRepresentationWithoutConfig(model);
return rep;
}
@Override
protected boolean isCaseSensitiveLogin() {
return kerberosRule.isCaseSensitiveLogin();
}
@Override
protected void setKrb5ConfPath() {
kerberosRule.setKrb5ConfPath(testingClient.testing());
}
@Test
public void spnegoLoginTest() throws Exception {
spnegoLoginTestImpl();
// Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP
assertUser("hnelson", "hnelson@keycloak.org", null, null, false);
}
@Test
public void updateProfileEnabledTest() throws Exception {
// Switch updateProfileOnFirstLogin to on
List<ComponentRepresentation> reps = testRealmResource().components().query("test", UserStorageProvider.class.getName());
org.keycloak.testsuite.Assert.assertEquals(1, reps.size());
ComponentRepresentation kerberosProvider = reps.get(0);
kerberosProvider.getConfig().putSingle(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
// Assert update profile page is displayed
Response spnegoResponse = spnegoLogin("hnelson", "secret");
Assert.assertEquals(200, spnegoResponse.getStatus());
String responseText = spnegoResponse.readEntity(String.class);
Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account."));
Assert.assertTrue(responseText.contains("hnelson@keycloak.org"));
spnegoResponse.close();
// Assert user was imported and has required action on him
assertUser("hnelson", "hnelson@keycloak.org", null, null, true);
// Switch updateProfileOnFirstLogin to off
kerberosProvider.getConfig().putSingle(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "false");
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
}
/**
* KEYCLOAK-3451
*
* Test that if there is no User Storage Provider that can handle kerberos we can still login
*
* @throws Exception
*/
@Test
public void noProvider() throws Exception {
List<ComponentRepresentation> reps = testRealmResource().components().query("test", UserStorageProvider.class.getName());
org.keycloak.testsuite.Assert.assertEquals(1, reps.size());
ComponentRepresentation kerberosProvider = reps.get(0);
testRealmResource().components().component(kerberosProvider.getId()).remove();
/*
To do this we do a valid kerberos login. The authenticator will obtain a valid token, but there will
be no user storage provider that can process it. This means we should be on the login page.
We do this through a JAX-RS client request. We extract the action URL from the login page, and stuff it
into selenium then just perform a regular login.
*/
Response spnegoResponse = spnegoLogin("hnelson", "secret");
String context = spnegoResponse.readEntity(String.class);
spnegoResponse.close();
Pattern pattern = Pattern.compile("action=\"([^\"]+)\"");
Matcher m = pattern.matcher(context);
Assert.assertTrue(m.find());
String url = m.group(1);
driver.navigate().to(url);
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("test-user@localhost", "password");
String pageSource = driver.getPageSource();
assertAuthenticationSuccess(driver.getCurrentUrl());
events.clear();
testRealmResource().components().add(kerberosProvider);
}
/**
* KEYCLOAK-4178
*
* Assert it's handled when kerberos realm is unreachable
*
* @throws Exception
*/
@Test
public void handleUnknownKerberosRealm() throws Exception {
// Switch kerberos realm to "unavailable"
List<ComponentRepresentation> reps = testRealmResource().components().query("test", UserStorageProvider.class.getName());
org.keycloak.testsuite.Assert.assertEquals(1, reps.size());
ComponentRepresentation kerberosProvider = reps.get(0);
kerberosProvider.getConfig().putSingle(KerberosConstants.KERBEROS_REALM, "unavailable");
testRealmResource().components().component(kerberosProvider.getId()).update(kerberosProvider);
// Try register new user and assert it failed
UserRepresentation john = new UserRepresentation();
john.setUsername("john");
Response response = testRealmResource().users().create(john);
Assert.assertEquals(500, response.getStatus());
response.close();
}
}

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.testsuite.federation; package org.keycloak.testsuite.federation.kerberos;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;

View file

@ -19,6 +19,8 @@ package org.keycloak.testsuite.migration;
import java.util.HashSet; import java.util.HashSet;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.keys.KeyProvider;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.arquillian.migration.Migration; import org.keycloak.testsuite.arquillian.migration.Migration;
@ -135,6 +137,32 @@ public class MigrationTest extends AbstractKeycloakTest {
*/ */
private void testMigrationTo2_3_0() { private void testMigrationTo2_3_0() {
testUpdateProtocolMappers(masterRealm, migrationRealm); testUpdateProtocolMappers(masterRealm, migrationRealm);
testExtractRealmKeys(masterRealm, migrationRealm);
}
private void testExtractRealmKeys(RealmResource masterRealm, RealmResource migrationRealm) {
String expectedMasterRealmKey = "MIIEowIBAAKCAQEAiU54OXoCbHy0L0gHn1yasctcnKHRU1pHFIJnWvaI7rClJydet9dDJaiYXOxMKseiBm3eYznfN3cPyU8udYmRnMuKjiocZ77LT2IEttAjXb6Ggazx7loriFHRy0IOJeX4KxXhAPWmxqa3mkFNfLBEvFqVaBgUDHQ60cmnPvNSHYudBTW9K80s8nvmP2pso7HTwWJ1+Xatj1Ey/gTmB3CXlyqBegGWC9TeuErEYpYhdh+11TVWasgMBZyUCtL3NRPaBuhaPg1LpW8lWGk05nS+YM6dvTk3Mppv+z2RygEpxyO09oT3b4G+Zfwit1STqn0AvDTGzINdoKcNtFScV0j8TwIDAQABAoIBAHcbPKsPLZ8SJfOF1iblW8OzFulAbaaSf2pJHIMJrQrw7LKkMkPjVXoLX+/rgr7xYZmWIP2OLBWfEHCeYTzQUyHiZpSf7vgHx7Fa45/5uVQOe/ttHIiYa37bCtP4vvEdJkOpvP7qGPvljwsebqsk9Ns28LfVez66bHOjK5Mt2yOIulbTeEs7ch//h39YwKJv96vc+CHbV2O6qoOxZessO6y+287cOBvbFXmS2GaGle5Nx/EwncBNS4b7czoetmm70+9ht3yX+kxaP311YUT31KQjuaJt275kOiKsrXr27PvgO++bsIyGuSzqyS7G7fmxF2zUyphEqEpalyDGMKMnrAECgYEA1fCgFox03rPDjm0MhW/ThoS2Ld27sbWQ6reS+PBMdUTJZVZIU1D2//h6VXDnlddhk6avKjA4smdy1aDKzmjz3pt9AKn+kgkXqtTC2fD3wp+fC9hND0z+rQPGe/Gk7ZUnTdsqnfyowxr+woIgzdnRukOUrG+xQiP3RUUT7tt6NQECgYEApEz2xvgqMm+9/f/YxjLdsFUfLqc4WlafB863stYEVqlCYy5ujyo0VQ0ahKSKJkLDnf52+aMUqPOpwaGePpu3O6VkvpcKfPY2MUlZW7/6Sa9et9hxNkdTS7Gui2d1ELpaCBe1Bc62sk8EA01iHXE1PpvyUqDWrhNh+NrDICA9oU8CgYBgGDYACtTP11TmW2r9YK5VRLUDww30k4ZlN1GnyV++aMhBYVEZQ0u+y+A/EnijIFwu0vbo70H4OGknNZMCxbeMbLDoJHM5KyZbUDe5ZvgSjloFGwH59m6KTiDQOUkIgi9mVCQ/VGaFRFHcElEjxUvj60kTbxPijn8ZuR5r8l9hAQKBgQCQ9jL5pHWeoIayN20smi6M6N2lTPbkhe60dcgQatHTIG2pkosLl8IqlHAkPgSB84AiwyR351JQKwRJCm7TcJI/dxMnMZ6YWKfB3qSP1hdfsfJRJQ/mQxIUBAYrizF3e+P5peka4aLCOgMhYsJBlePThMZN7wja99EGPwXQL4IQ8wKBgB8Nis1lQK6Z30GCp9u4dYleGfEP71Lwqvk/eJb89/uz0fjF9CTpJMULFc+nA5u4yHP3LFnRg3zCU6aEwfwUyk4GH9lWGV/qIAisQtgrCEraVe4qxz0DVE59C7qjO26IhU2U66TEzPAqvQ3zqey+woDn/cz/JMWK1vpcSk+TKn3K";
String expectedMigrationRealmKey = "MIIEpAIBAAKCAQEApt6gCllWkVTZ7fy/oRIx6Bxjt9x3eKKyKGFXvN4iaafrNqpYU9lcqPngWJ9DyXGqUf8RpjPaQWiLWLxjw3xGBqLk2E1/Frb9e/dy8rj//fHGq6bujN1iguzyFwxPGT5Asd7jflRI3qU04M8JE52PArqPhGL2Fn+FiSK5SWRIGm+hVL7Ck/E/tVxM25sFG1/UTQqvrROm4q76TmP8FsyZaTLVf7cCwW2QPIX0N5HTVb3QbBb5KIsk4kKmk/g7uUxS9r42tu533LISzRr5CTyWZAL2XFRuF2RrKdE8gwqkEubw6sDmB2mE0EoPdY1DUhBQgVP/5rwJrCtTsUBR2xdEYQIDAQABAoIBAFbbsNBSOlZBpYJUOmcb8nBQPrOYhXN8tGGCccn0klMOvcdhmcJjdPDbyCQ5Gm7DxJUTwNsTSHsdcNMKlJ9Pk5+msJnKlOl87KrXXbTsCQvlCrWUmb0nCzz9GvJWTOHl3oT3cND0DE4gDksqWR4luCgCdevCGzgQvrBoK6wBD+r578uEW3iw10hnJ0+wnGiw8IvPzE1a9xbY4HD8/QrYdaLxuLb/aC1PDuzrz0cOjnvPkrws5JrbUSnbFygJiOv1z4l2Q00uGIxlHtXdwQBnTZZjVi4vOec2BYSHffgwDYEZIglw1mnrV7y0N1nnPbtJK/cegIkXoBQHXm8Q99TrWMUCgYEA9au86qcwrXZZg5H4BpR5cpy0MSkcKDbA1aRL1cAyTCqJxsczlAtLhFADF+NhnlXj4y7gwDEYWrz064nF73I+ZGicvCiyOy+tCTugTyTGS+XR948ElDMS6PCUUXsotS3dKa0b3c9wd2mxeddTjq/ArfgEVZJ6fE1KtjLt9dtfA+8CgYEAreK3JsvjR5b/Xct28TghYUU7Qnasombb/shqqy8FOMjYUr5OUm/OjNIgoCqhOlE8oQDJ4dOZofNSa7tL+oM8Gmbal+E3fRzxnx/9/EC4QV6sVaPLTIyk7EPfKTcZuzH7+BNZtAziTxJw9d6YJQRbkpg92EZIEoR8iDj2Xs5xrK8CgYEAwMVWwwYX8zT3vn7ukTM2LRH7bsvkVUXJgJqgCwT6Mrv6SmkK9vL5+cPS+Y6pjdW1sRGauBSOGL1Grf/4ug/6F03jFt4UJM8fRyxreU7Q7sNSQ6AMpsGA6BnHODycz7ZCYa59PErG5FyiL4of/cm5Nolz1TXQOPNpWZiTEqVlZC8CgYA4YPbjVF4nuxSnU64H/hwMjsbtAM9uhI016cN0J3W4+J3zDhMU9X1x+Tts0wWdg/N1fGz4lIQOl3cUyRCUc/KL2OdtMS+tmDHbVyMho9ZaE5kq10W2Vy+uDz+O/HeSU12QDK4cC8Vgv+jyPy7zaZtLR6NduUPrBRvfiyCOkr8WrwKBgQCY0h4RCdNFhr0KKLLmJipAtV8wBCGcg1jY1KoWKQswbcykfBKwHbF6EooVqkRW0ITjWB7ZZCf8TnSUxe0NXCUAkVBrhzS4DScgtoSZYOOUaSHgOxpfwgnQ3oYotKi98Yg3IsaLs1j4RuPG5Sp1z6o+ELP1uvr8azyn9YlLa+523Q==";
List<ComponentRepresentation> components = masterRealm.components().query(MASTER, KeyProvider.class.getName());
assertEquals(2, components.size());
components = masterRealm.components().query(MASTER, KeyProvider.class.getName(), "rsa");
assertEquals(1, components.size());
assertEquals(expectedMasterRealmKey, testingClient.testing(MASTER).getComponentConfig(components.get(0).getId()).getFirst("privateKey"));
components = masterRealm.components().query(MASTER, KeyProvider.class.getName(), "hmac-generated");
assertEquals(1, components.size());
components = migrationRealm.components().query(MIGRATION, KeyProvider.class.getName());
assertEquals(2, components.size());
components = migrationRealm.components().query(MIGRATION, KeyProvider.class.getName(), "rsa");
assertEquals(1, components.size());
assertEquals(expectedMigrationRealmKey, testingClient.testing(MIGRATION).getComponentConfig(components.get(0).getId()).getFirst("privateKey"));
components = migrationRealm.components().query(MIGRATION, KeyProvider.class.getName(), "hmac-generated");
assertEquals(1, components.size());
} }
private void testMigrationTo2_5_0() { private void testMigrationTo2_5_0() {

View file

@ -1,20 +0,0 @@
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<Context path="/kerberos-portal">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

View file

@ -1,27 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Get name="securityHandler">
<Set name="authenticator">
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
</New>
</Set>
</Get>
</Configure>

View file

@ -1,9 +0,0 @@
{
"realm" : "test",
"resource" : "kerberos-app",
"auth-server-url" : "/auth",
"ssl-required" : "external",
"credentials" : {
"secret": "password"
}
}

View file

@ -1,10 +0,0 @@
{
"realm": "test",
"resource": "kerberos-app",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8081/auth",
"ssl-required" : "external",
"credentials": {
"secret": "password"
}
}

View file

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>kerberos-portal</module-name>
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>org.keycloak.testsuite.adapter.servlet.KerberosCredDelegServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Users</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>test</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>

View file

@ -68,6 +68,7 @@
${adapter.test.props} ${adapter.test.props}
${migration.import.properties} ${migration.import.properties}
${auth.server.profile} ${auth.server.profile}
${auth.server.feature}
</property> </property>
<property name="javaVmArguments"> <property name="javaVmArguments">
${auth.server.memory.settings} ${auth.server.memory.settings}

View file

@ -37,7 +37,8 @@
"adminUrl": "/kerberos-portal/logout", "adminUrl": "/kerberos-portal/logout",
"baseUrl": "/kerberos-portal", "baseUrl": "/kerberos-portal",
"redirectUris": [ "redirectUris": [
"/kerberos-portal/*" "/kerberos-portal/*",
"/auth/realms/master/app/*"
], ],
"secret": "password" "secret": "password"
} }

View file

@ -28,10 +28,10 @@
"quickLoginCheckMilliSeconds" : 1000, "quickLoginCheckMilliSeconds" : 1000,
"maxDeltaTimeSeconds" : 43200, "maxDeltaTimeSeconds" : 43200,
"failureFactor" : 30, "failureFactor" : 30,
"privateKey" : "MIIEogIBAAKCAQEA0jK5KDa59d5WGINVY/pRZTaz57PjqgPsCGtLvLHan9FWLqz/DP+zZMJdu5HsPR7ZTMCZPeF5xzmM5I05XdSPkt9yNEDIXt04D+IZFMWaNLRp6fG2F8VRrZit6JXM/LvnsLC+JS2JY3xl2c8SiYyCRqz+2R2Bp62nAJqf7OJPYb2rHO4iAX9ZcHuNBqONl6BIgznlZccKCB+Fxbd1Qax1OnB95Q4yVjA2++sWfsRoUYVmUoUgGT1YPY/GTGFbNucGNbHzWsFIuy20cAv0IWbrV1KiN9VEd+MC7ebLPgsW0JtFSdsUKZt5PZnQ8tOOEvv/uJ7QvVBba6c8F0E8ffhGiQIDAQABAoIBACuVkujwuAxwJp/8k3cgj0ISXqlblohJOebhAJpGspNS37Db9CKo3nS1DKR49GgT/nC4us2m9A5IM5usYMnA6mhE5rRI/oMVEavRcOvqsTfedNuqX9x78JyzPNQuQ4vgFoF/lF23HHbBc4j/M9PLi7PF6S3xvVGcYzVv2ltO7bDRN+4lj+utg4fVGEPQvt3FDSoNdG1Ol+cshJ/tTB5kYq/nUXFYrk71fWKkpo38Q87UQJeKIlKGZ3L4TwlwKBeQzeCe2OIMoYi4sh0JRUOBFCPvjjWSJ1tNvNMkLw1tDC5FA4S4TrOMhiaSuyIckZ4a2rk8DKNJiTt9HruSYlStqRECgYEA7o1WL4xgTlgUpGPs63DCaWdUBkL5JXCGFyBgCwwoI2hJ+u5IVT4I5W6gmcAkXAKPLraXW4TnTb/UjrItA+miWAdWb//uIDhbwpRCj4/qJ0+gCOj1zN4c9J6XaRY5+zsy9hGi4rwN1Y3mWHDEsD0PDCCZ1c+p7JZt+bekRU2vwZUCgYEA4ZJ8RY0AKaUw7w+yCvEyU7M8px0rzNwW06t3LRoY0vlak02xUSqx2jSxCrObn/cFcymQVIhqk2KqIVvJuPLJotUHe5OJ5vIKM085Mf2PFW6em7K/zPNU1R4aYuXI+hBAWz5m4u30PbEAzzT+idb4v3JKKJZPcc8YLJoHq5GRHCUCgYB33wXD9uId/BzDToMq/Ap1NLsAOn8/1P9Md7RLWNyCjlE+iqZGT1nhEGobWHXs1kxkyNPd5Tbl1tNUsSv1n104ptdEiHJ8NlXFvQKFaWfcQSLF12pMfABI27hx0f1oxSKii9Ix5RV+jkOFhYUQml4JVgh3bFaS0DSLG0CsopEC0QKBgFPfU7XIp7bUZU1+WIUZCc11Z6f7gL27WAF8T+PFQFWxwavjx3qWlWq9DNcd0U+5WZ1oZvIHhxkmJnwg1PCf1bll3FJmmW/1rzVEz892qC0f5HmgDRD3qSNU/hqntgLE6CkFGtxl/dqyZcqlub8m33bP2CMfQIrgpexvmUqB5aLBAoGAb37J1Nowx+lLxukkCvXvfqeT6n/xikK7JhUBRXH9K6+mUpxeSzuDqaqsoUTsyd5aw10iJevNXmi0bu+gZOOzM2n+9YKUDRMJ7neA30Aa7Q+KTQYukwmXZ7GHVQzQgFFHt9CLMGRHxG4M1UHbiOtGlAf6SRRW9UUo0FygMeKFTV4=", "privateKey" : "MIIEowIBAAKCAQEAiU54OXoCbHy0L0gHn1yasctcnKHRU1pHFIJnWvaI7rClJydet9dDJaiYXOxMKseiBm3eYznfN3cPyU8udYmRnMuKjiocZ77LT2IEttAjXb6Ggazx7loriFHRy0IOJeX4KxXhAPWmxqa3mkFNfLBEvFqVaBgUDHQ60cmnPvNSHYudBTW9K80s8nvmP2pso7HTwWJ1+Xatj1Ey/gTmB3CXlyqBegGWC9TeuErEYpYhdh+11TVWasgMBZyUCtL3NRPaBuhaPg1LpW8lWGk05nS+YM6dvTk3Mppv+z2RygEpxyO09oT3b4G+Zfwit1STqn0AvDTGzINdoKcNtFScV0j8TwIDAQABAoIBAHcbPKsPLZ8SJfOF1iblW8OzFulAbaaSf2pJHIMJrQrw7LKkMkPjVXoLX+/rgr7xYZmWIP2OLBWfEHCeYTzQUyHiZpSf7vgHx7Fa45/5uVQOe/ttHIiYa37bCtP4vvEdJkOpvP7qGPvljwsebqsk9Ns28LfVez66bHOjK5Mt2yOIulbTeEs7ch//h39YwKJv96vc+CHbV2O6qoOxZessO6y+287cOBvbFXmS2GaGle5Nx/EwncBNS4b7czoetmm70+9ht3yX+kxaP311YUT31KQjuaJt275kOiKsrXr27PvgO++bsIyGuSzqyS7G7fmxF2zUyphEqEpalyDGMKMnrAECgYEA1fCgFox03rPDjm0MhW/ThoS2Ld27sbWQ6reS+PBMdUTJZVZIU1D2//h6VXDnlddhk6avKjA4smdy1aDKzmjz3pt9AKn+kgkXqtTC2fD3wp+fC9hND0z+rQPGe/Gk7ZUnTdsqnfyowxr+woIgzdnRukOUrG+xQiP3RUUT7tt6NQECgYEApEz2xvgqMm+9/f/YxjLdsFUfLqc4WlafB863stYEVqlCYy5ujyo0VQ0ahKSKJkLDnf52+aMUqPOpwaGePpu3O6VkvpcKfPY2MUlZW7/6Sa9et9hxNkdTS7Gui2d1ELpaCBe1Bc62sk8EA01iHXE1PpvyUqDWrhNh+NrDICA9oU8CgYBgGDYACtTP11TmW2r9YK5VRLUDww30k4ZlN1GnyV++aMhBYVEZQ0u+y+A/EnijIFwu0vbo70H4OGknNZMCxbeMbLDoJHM5KyZbUDe5ZvgSjloFGwH59m6KTiDQOUkIgi9mVCQ/VGaFRFHcElEjxUvj60kTbxPijn8ZuR5r8l9hAQKBgQCQ9jL5pHWeoIayN20smi6M6N2lTPbkhe60dcgQatHTIG2pkosLl8IqlHAkPgSB84AiwyR351JQKwRJCm7TcJI/dxMnMZ6YWKfB3qSP1hdfsfJRJQ/mQxIUBAYrizF3e+P5peka4aLCOgMhYsJBlePThMZN7wja99EGPwXQL4IQ8wKBgB8Nis1lQK6Z30GCp9u4dYleGfEP71Lwqvk/eJb89/uz0fjF9CTpJMULFc+nA5u4yHP3LFnRg3zCU6aEwfwUyk4GH9lWGV/qIAisQtgrCEraVe4qxz0DVE59C7qjO26IhU2U66TEzPAqvQ3zqey+woDn/cz/JMWK1vpcSk+TKn3K",
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0jK5KDa59d5WGINVY/pRZTaz57PjqgPsCGtLvLHan9FWLqz/DP+zZMJdu5HsPR7ZTMCZPeF5xzmM5I05XdSPkt9yNEDIXt04D+IZFMWaNLRp6fG2F8VRrZit6JXM/LvnsLC+JS2JY3xl2c8SiYyCRqz+2R2Bp62nAJqf7OJPYb2rHO4iAX9ZcHuNBqONl6BIgznlZccKCB+Fxbd1Qax1OnB95Q4yVjA2++sWfsRoUYVmUoUgGT1YPY/GTGFbNucGNbHzWsFIuy20cAv0IWbrV1KiN9VEd+MC7ebLPgsW0JtFSdsUKZt5PZnQ8tOOEvv/uJ7QvVBba6c8F0E8ffhGiQIDAQAB", "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiU54OXoCbHy0L0gHn1yasctcnKHRU1pHFIJnWvaI7rClJydet9dDJaiYXOxMKseiBm3eYznfN3cPyU8udYmRnMuKjiocZ77LT2IEttAjXb6Ggazx7loriFHRy0IOJeX4KxXhAPWmxqa3mkFNfLBEvFqVaBgUDHQ60cmnPvNSHYudBTW9K80s8nvmP2pso7HTwWJ1+Xatj1Ey/gTmB3CXlyqBegGWC9TeuErEYpYhdh+11TVWasgMBZyUCtL3NRPaBuhaPg1LpW8lWGk05nS+YM6dvTk3Mppv+z2RygEpxyO09oT3b4G+Zfwit1STqn0AvDTGzINdoKcNtFScV0j8TwIDAQAB",
"certificate" : "MIICmzCCAYMCBgFXuEgtnjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDEyMDk0MzQwWhcNMjYxMDEyMDk0NTIwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSMrkoNrn13lYYg1Vj+lFlNrPns+OqA+wIa0u8sdqf0VYurP8M/7Nkwl27kew9HtlMwJk94XnHOYzkjTld1I+S33I0QMhe3TgP4hkUxZo0tGnp8bYXxVGtmK3olcz8u+ewsL4lLYljfGXZzxKJjIJGrP7ZHYGnracAmp/s4k9hvasc7iIBf1lwe40Go42XoEiDOeVlxwoIH4XFt3VBrHU6cH3lDjJWMDb76xZ+xGhRhWZShSAZPVg9j8ZMYVs25wY1sfNawUi7LbRwC/QhZutXUqI31UR34wLt5ss+CxbQm0VJ2xQpm3k9mdDy044S+/+4ntC9UFtrpzwXQTx9+EaJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABweR6V1xLtSuQp1VZcz7f5Y4Sop/REW61YfOfJ2jO6JUQKZIS1fypfS8H0Y6E4DWiq6K/Eiv/5Ph94DABlMxtpyVoU7YRNmRfaZYZPKzOiuUv4sdCXORTaF6SSr+Lxyh6XvIpB66g1i/KqFsBQ2w9pbpWj4QAyOW0bACkH3S1dTVWDTXzv07nxssVNq6TmT6mzKuPaaO2EX7fBaVNUesOzvDQcyOF+7oapjtMj+J87S/YiyLTgCywC8mFExO4YBXM/c6KRv4al40SLCkEii9NLlNxq/cLVdUPdkbelzIuqs9EOllvHDYB/QkWo3tvNvbPsca2MUNbvXqPMubTjzXwo=", "certificate" : "MIICmzCCAYMCBgFXt/Tg9TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDEyMDgxMjQxWhcNMjYxMDEyMDgxNDIxWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJTng5egJsfLQvSAefXJqxy1ycodFTWkcUgmda9ojusKUnJ16310MlqJhc7Ewqx6IGbd5jOd83dw/JTy51iZGcy4qOKhxnvstPYgS20CNdvoaBrPHuWiuIUdHLQg4l5fgrFeEA9abGpreaQU18sES8WpVoGBQMdDrRyac+81Idi50FNb0rzSzye+Y/amyjsdPBYnX5dq2PUTL+BOYHcJeXKoF6AZYL1N64SsRiliF2H7XVNVZqyAwFnJQK0vc1E9oG6Fo+DUulbyVYaTTmdL5gzp29OTcymm/7PZHKASnHI7T2hPdvgb5l/CK3VJOqfQC8NMbMg12gpw20VJxXSPxPAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAC54wFHL8tmrksq4OzatzNUM+R+3Hu/VXX3T44dwg0EvXzGW45sME+gKCuleU1PabIrr6oFm0bBMTdxgE2hbLWpYbU3OcsjArpCeCsOlxrAkqhVQN161J+tp77JkDMgArFdwe3wh5bhvLaOZSt6Fsq+oo16CXG1obe1feyaK3+sU3YuDUIHE01UYtvwtfDsYBC+VDyTdNDbB15WcdRoGljJY/JiT0JHdmAfq8qdGDuxGocIV0lSB8bO5JwF/WCmKqMrnh5j1NfGcE1g26Hbz2RmDs17X0K10Okzs/qz1YZqDjPVYiU//VFQQro71/D35dPOJv8mQMjhjNaXScL44h7w=",
"codeSecret" : "766580d5-5599-42b3-8e4d-003043f39415", "codeSecret" : "4c59c2db-d9c3-4023-8cd5-8808fe854e98",
"roles" : { "roles" : {
"realm" : [ { "realm" : [ {
"id" : "40dd3051-9581-479d-9ae0-80abd28b3f94", "id" : "40dd3051-9581-479d-9ae0-80abd28b3f94",
@ -1533,10 +1533,10 @@
"quickLoginCheckMilliSeconds" : 1000, "quickLoginCheckMilliSeconds" : 1000,
"maxDeltaTimeSeconds" : 43200, "maxDeltaTimeSeconds" : 43200,
"failureFactor" : 30, "failureFactor" : 30,
"privateKey" : "MIIEowIBAAKCAQEAvXWOP4hFWeEmCiB6gEeoZfUt+2lRE19tvZ3eyTEdnJoSUAA3VjUNcZUk/sjT1hrgfzaiX7D7yMkDrmxS2aHyJvzpUhZ6CVFQ3c9S2oneU8tN4zi52tzC1i3AaljhO3eUyYa97FY27bNmwAF0MSSsYtRVA7woDN1PLj5NIWxQhs2kbhU6A6JTrsQonFQ7rStmldI8Odx/FQHPA8CnH7v44HWAaO7sdV7BdNWGkqtGzynEXSbqgdbWw2Nb0R5mNYvgP8visSFg+Uni5Nx6FbO305+qBf1xNxr27siTFDuUttfastFtCVf3HuuCDGGqQfNdpjdf/2n4T0aBlxQ/GPzctwIDAQABAoIBABPXDqlqjNCnukMPOaTbXDApJ8KPOdVMhgJ4WM863H38yHQmQaU+cwT0+GXSBBUExYOs5f4dW5CgNEl8wIRX6DLbA1f8UJVPNae0wVlvXJ/rx2FHHHdl7OVe7ypz2kpO8H7WJGduc9wakGUFAfTvgTnsQGbeulb2igcusQcR/7DOBAtNIwo0RBds0mQSJlLsRKYz/kna/oRDicSWq3VFWBkcT0jjvx3W7Qd42RSBZ2PrxdaiESEXz1oNlzHemiBAxi4jGqaLh2rg8A2O6mU75gHeaGtdin/uhxcPyPrau0iX5D3V6bsnLYnMQFSQ+AzE89GqA2o6hOtUrYlmlsOJqSECgYEA3/dSjdurt90IlFZc7FydGyJSMMcpHkRlhmpY9jbGmQAx3iPFi1E/uVg561vKNRt1N6+fj5oydkLnYBjTrpGzWJeJ+VZ+Au7rHMvq8lkB2l/uj/E9jCcH95cydHX/tV13WAMg7MJvpym6J+xPQn0+I1HTz9O6YKK+PwlzG+jWCwcCgYEA2I69EIWKkpR8qJG+Ext9ticx163jONr6bmsPbxzHEdMLVMmeNRp+8Exu0u35WzYH0LzmhR/Id9uTn4fh02XRM+GnYSRlJl8bHIsy9W9lKgpfz6xOk/OmQ0iO/lQW3T+FQjPEqPhttsL0ntdlwEtnSHswTtjEZrPlS3chyLowRNECgYBLm0Z3NS0nCKsYyH7eG/W9bzyn0tQCr7xGQCiAFcW9aN9syk6p1WfZvfrvhTEh9H8BqXwIElnuqm9JdZggWndc9OtTDOvFObqNIhnSblG90pzKYvoQEi0SXCxgPGVyaHcdcxDuXEUs/V0mlRO8smMDsV/7zNIiRU5MIXiGd3eqiQKBgBFLhQn2wFRoi3Qi/50jCu0rQC3Lc2QtCOccocTr9XIU+siwuXPgQ9cJI6iw3MNOE122WGq1WRtJbAbHoV9yuDx+fxGhCHsCCXZEH7O+S8QJxBEf376tLNAe0Vy6doEA+7GH1OLxxEFeXI77AONUolEFlZwWb8kMAPn/77DKPeShAoGBAL//+BGQnXuvywrmwUF3u+FDqwpkVi72ttSFFoF1pCR7KLD16CW/bw2tck0OvKte+VFBDzfpfiXH56wPOpGf2nVvUnkHgyy9CS4zc+Jj266QGKZ/SMv2aCofwdLdla4qLM+luxsMtFuAj3Z9G8thAfj5zHRA5eK28Wq4qacDg+IW", "privateKey" : "MIIEpAIBAAKCAQEApt6gCllWkVTZ7fy/oRIx6Bxjt9x3eKKyKGFXvN4iaafrNqpYU9lcqPngWJ9DyXGqUf8RpjPaQWiLWLxjw3xGBqLk2E1/Frb9e/dy8rj//fHGq6bujN1iguzyFwxPGT5Asd7jflRI3qU04M8JE52PArqPhGL2Fn+FiSK5SWRIGm+hVL7Ck/E/tVxM25sFG1/UTQqvrROm4q76TmP8FsyZaTLVf7cCwW2QPIX0N5HTVb3QbBb5KIsk4kKmk/g7uUxS9r42tu533LISzRr5CTyWZAL2XFRuF2RrKdE8gwqkEubw6sDmB2mE0EoPdY1DUhBQgVP/5rwJrCtTsUBR2xdEYQIDAQABAoIBAFbbsNBSOlZBpYJUOmcb8nBQPrOYhXN8tGGCccn0klMOvcdhmcJjdPDbyCQ5Gm7DxJUTwNsTSHsdcNMKlJ9Pk5+msJnKlOl87KrXXbTsCQvlCrWUmb0nCzz9GvJWTOHl3oT3cND0DE4gDksqWR4luCgCdevCGzgQvrBoK6wBD+r578uEW3iw10hnJ0+wnGiw8IvPzE1a9xbY4HD8/QrYdaLxuLb/aC1PDuzrz0cOjnvPkrws5JrbUSnbFygJiOv1z4l2Q00uGIxlHtXdwQBnTZZjVi4vOec2BYSHffgwDYEZIglw1mnrV7y0N1nnPbtJK/cegIkXoBQHXm8Q99TrWMUCgYEA9au86qcwrXZZg5H4BpR5cpy0MSkcKDbA1aRL1cAyTCqJxsczlAtLhFADF+NhnlXj4y7gwDEYWrz064nF73I+ZGicvCiyOy+tCTugTyTGS+XR948ElDMS6PCUUXsotS3dKa0b3c9wd2mxeddTjq/ArfgEVZJ6fE1KtjLt9dtfA+8CgYEAreK3JsvjR5b/Xct28TghYUU7Qnasombb/shqqy8FOMjYUr5OUm/OjNIgoCqhOlE8oQDJ4dOZofNSa7tL+oM8Gmbal+E3fRzxnx/9/EC4QV6sVaPLTIyk7EPfKTcZuzH7+BNZtAziTxJw9d6YJQRbkpg92EZIEoR8iDj2Xs5xrK8CgYEAwMVWwwYX8zT3vn7ukTM2LRH7bsvkVUXJgJqgCwT6Mrv6SmkK9vL5+cPS+Y6pjdW1sRGauBSOGL1Grf/4ug/6F03jFt4UJM8fRyxreU7Q7sNSQ6AMpsGA6BnHODycz7ZCYa59PErG5FyiL4of/cm5Nolz1TXQOPNpWZiTEqVlZC8CgYA4YPbjVF4nuxSnU64H/hwMjsbtAM9uhI016cN0J3W4+J3zDhMU9X1x+Tts0wWdg/N1fGz4lIQOl3cUyRCUc/KL2OdtMS+tmDHbVyMho9ZaE5kq10W2Vy+uDz+O/HeSU12QDK4cC8Vgv+jyPy7zaZtLR6NduUPrBRvfiyCOkr8WrwKBgQCY0h4RCdNFhr0KKLLmJipAtV8wBCGcg1jY1KoWKQswbcykfBKwHbF6EooVqkRW0ITjWB7ZZCf8TnSUxe0NXCUAkVBrhzS4DScgtoSZYOOUaSHgOxpfwgnQ3oYotKi98Yg3IsaLs1j4RuPG5Sp1z6o+ELP1uvr8azyn9YlLa+523Q==",
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXWOP4hFWeEmCiB6gEeoZfUt+2lRE19tvZ3eyTEdnJoSUAA3VjUNcZUk/sjT1hrgfzaiX7D7yMkDrmxS2aHyJvzpUhZ6CVFQ3c9S2oneU8tN4zi52tzC1i3AaljhO3eUyYa97FY27bNmwAF0MSSsYtRVA7woDN1PLj5NIWxQhs2kbhU6A6JTrsQonFQ7rStmldI8Odx/FQHPA8CnH7v44HWAaO7sdV7BdNWGkqtGzynEXSbqgdbWw2Nb0R5mNYvgP8visSFg+Uni5Nx6FbO305+qBf1xNxr27siTFDuUttfastFtCVf3HuuCDGGqQfNdpjdf/2n4T0aBlxQ/GPzctwIDAQAB", "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt6gCllWkVTZ7fy/oRIx6Bxjt9x3eKKyKGFXvN4iaafrNqpYU9lcqPngWJ9DyXGqUf8RpjPaQWiLWLxjw3xGBqLk2E1/Frb9e/dy8rj//fHGq6bujN1iguzyFwxPGT5Asd7jflRI3qU04M8JE52PArqPhGL2Fn+FiSK5SWRIGm+hVL7Ck/E/tVxM25sFG1/UTQqvrROm4q76TmP8FsyZaTLVf7cCwW2QPIX0N5HTVb3QbBb5KIsk4kKmk/g7uUxS9r42tu533LISzRr5CTyWZAL2XFRuF2RrKdE8gwqkEubw6sDmB2mE0EoPdY1DUhBQgVP/5rwJrCtTsUBR2xdEYQIDAQAB",
"certificate" : "MIICoTCCAYkCBgFXuEp3fDANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlNaWdyYXRpb24wHhcNMTYxMDEyMDk0NjEwWhcNMjYxMDEyMDk0NzUwWjAUMRIwEAYDVQQDDAlNaWdyYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dY4/iEVZ4SYKIHqAR6hl9S37aVETX229nd7JMR2cmhJQADdWNQ1xlST+yNPWGuB/NqJfsPvIyQOubFLZofIm/OlSFnoJUVDdz1Laid5Ty03jOLna3MLWLcBqWOE7d5TJhr3sVjbts2bAAXQxJKxi1FUDvCgM3U8uPk0hbFCGzaRuFToDolOuxCicVDutK2aV0jw53H8VAc8DwKcfu/jgdYBo7ux1XsF01YaSq0bPKcRdJuqB1tbDY1vRHmY1i+A/y+KxIWD5SeLk3HoVs7fTn6oF/XE3GvbuyJMUO5S219qy0W0JV/ce64IMYapB812mN1//afhPRoGXFD8Y/Ny3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA6Sbqa4oxGXli57CF6vcI0NmqHkDQkrjAUNwfr98UR1/WGQc1+qabdcj8dyL2g3PTrI98CXHdxlvOOQfbniaB59usF1IKKqo7W6SzcmVVlQXP+g7WN0ldS4QYtqkA+LsWj6rBgz561WnflK0sRj0V+5QmrwifQ62syhnJ2FpYRwDHUyE9ezR3nSpkr1NO1S1Sh7VzLftm8YoZYC1Zy35FyK+JF3Utf3IRV1oe+fBtKWwfmw+Xh94sXIYpxAZrspSJLXEIP/dony4kt88KrFWnOotUT4ctECW/l57qczeKEP5BzrqlZCw65NVz55fiIh7sELp6ZW/aFPj1gNqhpK2j8=", "certificate" : "MIICoTCCAYkCBgFXt/t9TjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlNaWdyYXRpb24wHhcNMTYxMDEyMDgxOTU0WhcNMjYxMDEyMDgyMTM0WjAUMRIwEAYDVQQDDAlNaWdyYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCm3qAKWVaRVNnt/L+hEjHoHGO33Hd4orIoYVe83iJpp+s2qlhT2Vyo+eBYn0PJcapR/xGmM9pBaItYvGPDfEYGouTYTX8Wtv1793LyuP/98carpu6M3WKC7PIXDE8ZPkCx3uN+VEjepTTgzwkTnY8Cuo+EYvYWf4WJIrlJZEgab6FUvsKT8T+1XEzbmwUbX9RNCq+tE6birvpOY/wWzJlpMtV/twLBbZA8hfQ3kdNVvdBsFvkoiyTiQqaT+Du5TFL2vja27nfcshLNGvkJPJZkAvZcVG4XZGsp0TyDCqQS5vDqwOYHaYTQSg91jUNSEFCBU//mvAmsK1OxQFHbF0RhAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFtPdVUB65dAP6v2wu8idPkkqRaP5gmcOuOt2+8/slx7RvO/FFwzFvAqroqmpaKJ53daewZwIG4Wzu4lziqYnD3F3YoqxqUY8ID58SLm9a6XF6aYka7TxXJnZgmy7v1ZWcbbTinvUC7S1m23imT7779cWj5NkkXSM/R+RWB8ZAQCpy9pg7iElAMTlqAp31pCntNG3l1O13A6t5eN3Af474T0FjVaXIEG/PLcRmF/5kTwmkYy5Av1v2vmyLBYXKNUrWwjeTGEEX0+j9AkcF79D1GpdKZpvuC0wxOrOgHLiR9DpGucMJajx+RA8zbAAj5C1A5JfkKBZPh2jMQ06c2eAAM=",
"codeSecret" : "1102419e-9b67-4ad7-8e93-5fa12153b022", "codeSecret" : "be7e5acb-ad90-4c01-8dfe-c78cc492b752",
"roles" : { "roles" : {
"realm" : [ { "realm" : [ {
"id" : "a3e9f038-0c6d-4024-8a2a-ce3958c7afbb", "id" : "a3e9f038-0c6d-4024-8a2a-ce3958c7afbb",

View file

@ -64,6 +64,7 @@
<auth.server.remote>false</auth.server.remote> <auth.server.remote>false</auth.server.remote>
<auth.server.profile/> <auth.server.profile/>
<auth.server.feature/>
<adapter.test.props/> <adapter.test.props/>
<migration.import.properties/> <migration.import.properties/>
@ -165,6 +166,7 @@
<auth.server.adapter.impl.class>${auth.server.adapter.impl.class}</auth.server.adapter.impl.class> <auth.server.adapter.impl.class>${auth.server.adapter.impl.class}</auth.server.adapter.impl.class>
<auth.server.profile>${auth.server.profile}</auth.server.profile> <auth.server.profile>${auth.server.profile}</auth.server.profile>
<auth.server.feature>${auth.server.feature}</auth.server.feature>
<frontend.console.output>${frontend.console.output}</frontend.console.output> <frontend.console.output>${frontend.console.output}</frontend.console.output>
<backends.console.output>${backend.console.output}</backends.console.output> <backends.console.output>${backend.console.output}</backends.console.output>
@ -263,6 +265,41 @@
</properties> </properties>
</profile> </profile>
<!--
profile that enables/disables specified feature, for more details see
https://keycloak.gitbooks.io/server-installation-and-configuration/content/topics/profiles.html
-->
<profile>
<id>auth-server-enable-disable-feature</id>
<properties>
<auth.server.feature>-Dkeycloak.profile.feature.${feature.name}=${feature.value}</auth.server.feature>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireProperty>
<property>feature.name</property>
</requireProperty>
<requireProperty>
<property>feature.value</property>
</requireProperty>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile> <profile>
<id>auth-server-cluster</id> <id>auth-server-cluster</id>
<properties> <properties>

View file

@ -1,398 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.HttpClientBuilder;
import org.keycloak.authentication.authenticators.browser.SpnegoAuthenticator;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.component.ComponentModel;
import org.keycloak.events.Details;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountPasswordPage;
import org.keycloak.testsuite.pages.BypassKerberosPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.security.Principal;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractKerberosTest {
protected String KERBEROS_APP_URL = "http://localhost:8081/kerberos-portal";
protected KeycloakSPNegoSchemeFactory spnegoSchemeFactory;
protected ResteasyClient client;
@WebResource
protected OAuthClient oauth;
@WebResource
protected WebDriver driver;
@WebResource
protected LoginPage loginPage;
@WebResource
protected BypassKerberosPage bypassPage;
@WebResource
protected AccountPasswordPage changePasswordPage;
protected abstract CommonKerberosConfig getKerberosConfig();
protected abstract KeycloakRule getKeycloakRule();
protected abstract AssertEvents getAssertEvents();
@Before
public void before() {
CommonKerberosConfig kerberosConfig = getKerberosConfig();
spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(kerberosConfig);
initHttpClient(true);
removeAllUsers();
}
@After
public void after() {
client.close();
client = null;
}
@Test
public void spnegoNotAvailableTest() throws Exception {
initHttpClient(false);
SpnegoAuthenticator.bypassChallengeJavascript = true;
driver.navigate().to(KERBEROS_APP_URL);
String kcLoginPageLocation = driver.getCurrentUrl();
Response response = client.target(kcLoginPageLocation).request().get();
Assert.assertEquals(401, response.getStatus());
Assert.assertEquals(KerberosConstants.NEGOTIATE, response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE));
String responseText = response.readEntity(String.class);
responseText.contains("Log in to test");
response.close();
SpnegoAuthenticator.bypassChallengeJavascript = false;
}
protected void spnegoLoginTestImpl() throws Exception {
KeycloakRule keycloakRule = getKeycloakRule();
AssertEvents events = getAssertEvents();
Response spnegoResponse = spnegoLogin("hnelson", "secret");
Assert.assertEquals(302, spnegoResponse.getStatus());
events.expectLogin()
.client("kerberos-app")
.user(keycloakRule.getUser("test", "hnelson").getId())
.detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
//.detail(Details.AUTH_METHOD, "spnego")
.detail(Details.USERNAME, "hnelson")
.assertEvent();
String location = spnegoResponse.getLocation().toString();
driver.navigate().to(location);
String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
spnegoResponse.close();
events.clear();
}
// KEYCLOAK-2102
@Test
public void spnegoCaseInsensitiveTest() throws Exception {
KeycloakRule keycloakRule = getKeycloakRule();
AssertEvents events = getAssertEvents();
Response spnegoResponse = spnegoLogin("MyDuke", "theduke");
Assert.assertEquals(302, spnegoResponse.getStatus());
events.expectLogin()
.client("kerberos-app")
.user(keycloakRule.getUser("test", "myduke").getId())
.detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
//.detail(Details.AUTH_METHOD, "spnego")
.detail(Details.USERNAME, "myduke")
.assertEvent();
String location = spnegoResponse.getLocation().toString();
driver.navigate().to(location);
String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
spnegoResponse.close();
events.clear();
}
@Test
public void usernamePasswordLoginTest() throws Exception {
KeycloakRule keycloakRule = getKeycloakRule();
AssertEvents events = getAssertEvents();
// Change editMode to READ_ONLY
updateProviderEditMode(UserStorageProvider.EditMode.READ_ONLY);
// Login with username/password from kerberos
changePasswordPage.open();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
loginPage.assertCurrent();
loginPage.login("jduke", "theduke");
changePasswordPage.assertCurrent();
// Bad existing password
changePasswordPage.changePassword("theduke-invalid", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
// Change password is not possible as editMode is READ_ONLY
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("You can't update your password as your account is read only"));
// Change editMode to UNSYNCED
updateProviderEditMode(UserStorageProvider.EditMode.UNSYNCED);
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
// Login with old password doesn't work, but with new password works
loginPage.login("jduke", "theduke");
loginPage.assertCurrent();
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.logout();
// Assert SPNEGO login still with the old password as mode is unsynced
events.clear();
Response spnegoResponse = spnegoLogin("jduke", "theduke");
Assert.assertEquals(302, spnegoResponse.getStatus());
String redirect = spnegoResponse.getLocation().toString();
events.expectLogin()
.client("kerberos-app")
.user(keycloakRule.getUser("test", "jduke").getId())
.detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
//.detail(Details.AUTH_METHOD, "spnego")
.detail(Details.USERNAME, "jduke")
.assertEvent();
spnegoResponse.close();
}
@Test
public void credentialDelegationTest() throws Exception {
// Add kerberos delegation credential mapper
getKeycloakRule().update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
true, false);
ClientModel kerberosApp = appRealm.getClientByClientId("kerberos-app");
kerberosApp.addProtocolMapper(protocolMapper);
}
});
// SPNEGO login
spnegoLoginTestImpl();
// Assert servlet authenticated to LDAP with delegated credential
driver.navigate().to(KERBEROS_APP_URL + KerberosCredDelegServlet.CRED_DELEG_TEST_PATH);
String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("LDAP Data: Horatio Nelson"));
// Remove kerberos delegation credential mapper
getKeycloakRule().update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ClientModel kerberosApp = appRealm.getClientByClientId("kerberos-app");
ProtocolMapperModel toRemove = kerberosApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
kerberosApp.removeProtocolMapper(toRemove);
}
});
// Clear driver and login again. I can't invoke LDAP now as GSS Credential is not in accessToken
driver.manage().deleteAllCookies();
spnegoLoginTestImpl();
driver.navigate().to(KERBEROS_APP_URL + KerberosCredDelegServlet.CRED_DELEG_TEST_PATH);
pageSource = driver.getPageSource();
Assert.assertFalse(pageSource.contains("LDAP Data: Horatio Nelson"));
Assert.assertTrue(pageSource.contains("LDAP Data: ERROR"));
}
protected Response spnegoLogin(String username, String password) {
SpnegoAuthenticator.bypassChallengeJavascript = true;
driver.navigate().to(KERBEROS_APP_URL);
String kcLoginPageLocation = driver.getCurrentUrl();
// Request for SPNEGO login sent with Resteasy client
spnegoSchemeFactory.setCredentials(username, password);
Response response = client.target(kcLoginPageLocation).request().get();
SpnegoAuthenticator.bypassChallengeJavascript = false;
if (response.getStatus() == 302) {
if (response.getLocation() == null) return response;
String uri = response.getLocation().toString();
if (uri.contains("login-actions/required-action")) {
response = client.target(uri).request().get();
}
}
return response;
}
protected void initHttpClient(boolean useSpnego) {
if (client != null) {
after();
}
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO, spnegoSchemeFactory);
if (useSpnego) {
Credentials fake = new Credentials() {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
};
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1, null),
fake);
}
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
client = new ResteasyClientBuilder().httpEngine(engine).build();
}
protected void removeAllUsers() {
KeycloakRule keycloakRule = getKeycloakRule();
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
List<UserModel> users = session.users().getUsers(appRealm, true);
for (UserModel user : users) {
if (!user.getUsername().equals(AssertEvents.DEFAULT_USERNAME)) {
session.users().removeUser(appRealm, user);
}
}
Assert.assertEquals(1, session.users().getUsers(appRealm, true).size());
} finally {
keycloakRule.stopSession(session, true);
}
}
protected void assertUser(String expectedUsername, String expectedEmail, String expectedFirstname, String expectedLastname, boolean updateProfileActionExpected) {
KeycloakRule keycloakRule = getKeycloakRule();
KeycloakSession session = keycloakRule.startSession();
try {
RealmManager manager = new RealmManager(session);
RealmModel appRealm = manager.getRealm("test");
UserModel user = session.users().getUserByUsername(expectedUsername, appRealm);
Assert.assertNotNull(user);
Assert.assertEquals(user.getEmail(), expectedEmail);
Assert.assertEquals(user.getFirstName(), expectedFirstname);
Assert.assertEquals(user.getLastName(), expectedLastname);
if (updateProfileActionExpected) {
Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(), user.getRequiredActions().iterator().next());
} else {
Assert.assertTrue(user.getRequiredActions().isEmpty());
}
} finally {
keycloakRule.stopSession(session, true);
}
}
protected void updateProviderEditMode(UserStorageProvider.EditMode editMode) {
KeycloakRule keycloakRule = getKeycloakRule();
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel realm = session.realms().getRealm("test");
ComponentModel kerberosProviderModel = realm.getComponents(realm.getId(), UserStorageProvider.class.getName()).get(0);
kerberosProviderModel.getConfig().putSingle(LDAPConstants.EDIT_MODE, editMode.toString());
realm.updateComponent(kerberosProviderModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
}

View file

@ -1,112 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation;
import org.ietf.jgss.GSSCredential;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.KerberosSerializationUtils;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.sasl.Sasl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KerberosCredDelegServlet extends HttpServlet {
public static final String CRED_DELEG_TEST_PATH = "/cred-deleg-test";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String ldapData = null;
if (req.getRequestURI().endsWith(CRED_DELEG_TEST_PATH)) {
try {
// Retrieve kerberos credential from accessToken and deserialize it
KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) req.getUserPrincipal();
String serializedGssCredential = (String) keycloakPrincipal.getKeycloakSecurityContext().getToken().getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential);
// First try to invoke without gssCredential. It should fail
try {
invokeLdap(null);
throw new RuntimeException("Not expected to authenticate to LDAP without credential");
} catch (NamingException nse) {
System.out.println("Expected exception: " + nse.getMessage());
}
ldapData = invokeLdap(gssCredential);
} catch (KerberosSerializationUtils.KerberosSerializationException kse) {
System.err.println("KerberosSerializationUtils.KerberosSerializationException: " + kse.getMessage());
ldapData = "ERROR";
} catch (Exception e) {
e.printStackTrace();
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
pw.printf("<html><head><title>%s</title></head><body>", "Kerberos Test");
pw.printf("Kerberos servlet secured content<br>");
if (ldapData != null) {
pw.printf("LDAP Data: " + ldapData + "<br>");
}
pw.print("</body></html>");
pw.flush();
}
private String invokeLdap(GSSCredential gssCredential) throws NamingException {
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
if (gssCredential != null) {
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
env.put(Sasl.CREDENTIALS, gssCredential);
}
DirContext ctx = new InitialDirContext(env);
try {
Attributes attrs = ctx.getAttributes("uid=hnelson,ou=People,dc=keycloak,dc=org");
String cn = (String) attrs.get("cn").get();
String sn = (String) attrs.get("sn").get();
return cn + " " + sn;
} finally {
ctx.close();
}
}
}

View file

@ -1,249 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runners.MethodSorters;
import org.keycloak.authentication.authenticators.browser.SpnegoAuthenticator;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.federation.kerberos.KerberosConfig;
import org.keycloak.federation.kerberos.KerberosFederationProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.federation.storage.ldap.LDAPTestUtils;
import org.keycloak.testsuite.rule.KerberosRule;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.utils.CredentialHelper;
import javax.ws.rs.core.Response;
import java.net.URL;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Test of KerberosFederationProvider (Kerberos not backed by LDAP)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class KerberosStandaloneTest extends AbstractKerberosTest {
private static final String PROVIDER_CONFIG_LOCATION = "kerberos/kerberos-standalone-connection.properties";
private static UserStorageProviderModel kerberosModel;
private static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
CredentialHelper.setAlternativeCredential(manager.getSession(), CredentialRepresentation.KERBEROS, appRealm);
URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json");
keycloakRule.createApplicationDeployment()
.name("kerberos-portal").contextPath("/kerberos-portal")
.servletClass(KerberosCredDelegServlet.class).adapterConfigPath(url.getPath())
.role("user").deployApplication();
Map<String,String> kerberosConfig = kerberosRule.getConfig();
MultivaluedHashMap<String, String> config = LDAPTestUtils.toComponentConfig(kerberosConfig);
UserStorageProviderModel model = new UserStorageProviderModel();
model.setLastSync(0);
model.setChangedSyncPeriod(-1);
model.setFullSyncPeriod(-1);
model.setName("kerberos-standalone");
model.setPriority(0);
model.setProviderId(KerberosFederationProviderFactory.PROVIDER_NAME);
model.setConfig(config);
kerberosModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
}
}) {
@Override
protected void importRealm() {
server.importRealm(getClass().getResourceAsStream("/kerberos-test/kerberosrealm.json"));
}
};
@ClassRule
public static TestRule chain = RuleChain
.outerRule(kerberosRule)
.around(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Override
protected CommonKerberosConfig getKerberosConfig() {
return new KerberosConfig(kerberosModel);
}
@Override
protected KeycloakRule getKeycloakRule() {
return keycloakRule;
}
@Override
protected AssertEvents getAssertEvents() {
return events;
}
@Test
public void spnegoLoginTest() throws Exception {
spnegoLoginTestImpl();
// Assert user was imported and hasn't any required action on him
assertUser("hnelson", "hnelson@keycloak.org", null, null, false);
}
@Test
@Override
public void spnegoCaseInsensitiveTest() throws Exception {
super.spnegoCaseInsensitiveTest();
}
@Test
@Override
public void credentialDelegationTest() throws Exception {
super.credentialDelegationTest();
}
@Test
@Override
public void usernamePasswordLoginTest() throws Exception {
super.usernamePasswordLoginTest();
}
@Test
public void updateProfileEnabledTest() throws Exception {
// Switch updateProfileOnFirstLogin to on
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel realm = session.realms().getRealm("test");
UserStorageProviderModel kerberosProviderModel = realm.getUserStorageProviders().get(0);
kerberosProviderModel.getConfig().putSingle(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
realm.updateComponent(kerberosProviderModel);
} finally {
keycloakRule.stopSession(session, true);
}
// Assert update profile page is displayed
Response spnegoResponse = spnegoLogin("hnelson", "secret");
Assert.assertEquals(200, spnegoResponse.getStatus());
String responseText = spnegoResponse.readEntity(String.class);
Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account."));
Assert.assertTrue(responseText.contains("hnelson@keycloak.org"));
spnegoResponse.close();
// Assert user was imported and has required action on him
assertUser("hnelson", "hnelson@keycloak.org", null, null, true);
// Switch updateProfileOnFirstLogin to off
session = keycloakRule.startSession();
try {
RealmModel realm = session.realms().getRealm("test");
UserStorageProviderModel kerberosProviderModel = realm.getUserStorageProviders().get(0);
kerberosProviderModel.getConfig().putSingle(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "false");
realm.updateComponent(kerberosProviderModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
/**
* KEYCLOAK-3451
*
* Test that if there is no User Storage Provider that can handle kerberos we can still login
*
* @throws Exception
*/
@Test
public void noProvider() throws Exception {
KeycloakSession session = keycloakRule.startSession();
try {
RealmModel realm = session.realms().getRealm("test");
realm.removeComponent(kerberosModel);
} finally {
keycloakRule.stopSession(session, true);
}
/*
To do this we do a valid kerberos login. The authenticator will obtain a valid token, but there will
be no user storage provider that can process it. This means we should be on the login page.
We do this through a JAX-RS client request. We extract the action URL from the login page, and stuff it
into selenium then just perform a regular login.
*/
Response spnegoResponse = spnegoLogin("hnelson", "secret");
String context = spnegoResponse.readEntity(String.class);
spnegoResponse.close();
Pattern pattern = Pattern.compile("action=\"([^\"]+)\"");
Matcher m = pattern.matcher(context);
Assert.assertTrue(m.find());
String url = m.group(1);
driver.navigate().to(url);
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("test-user@localhost", "password");
String pageSource = driver.getPageSource();
Assert.assertTrue(pageSource.contains("Kerberos Test") && pageSource.contains("Kerberos servlet secured content"));
events.clear();
session = keycloakRule.startSession();
try {
RealmModel realm = session.realms().getRealm("test");
realm.addComponentModel(kerberosModel);
} finally {
keycloakRule.stopSession(session, true);
}
}
}

View file

@ -84,9 +84,9 @@ public class LDAPTestConfiguration {
DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false"); DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG"); DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG"); DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab"); // URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab");
String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath(); // String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath();
DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath); // DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true"); DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true"); DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true");
DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true"); DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");

View file

@ -1,197 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.storage.ldap;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.events.Details;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.federation.AbstractKerberosTest;
import org.keycloak.testsuite.federation.KerberosCredDelegServlet;
import org.keycloak.testsuite.rule.KerberosRule;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.utils.CredentialHelper;
import javax.ws.rs.core.Response;
import java.net.URL;
/**
* Test of LDAPFederationProvider (Kerberos backed by LDAP)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class KerberosLdapTest extends AbstractKerberosTest {
private static final String PROVIDER_CONFIG_LOCATION = "kerberos/kerberos-ldap-connection.properties";
private static UserStorageProviderModel ldapModel = null;
private static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION);
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
CredentialHelper.setAlternativeCredential(manager.getSession(), CredentialRepresentation.KERBEROS, appRealm);
URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json");
keycloakRule.createApplicationDeployment()
.name("kerberos-portal").contextPath("/kerberos-portal")
.servletClass(KerberosCredDelegServlet.class).adapterConfigPath(url.getPath())
.role("user").deployApplication();
MultivaluedHashMap<String, String> ldapConfig = LDAPTestUtils.toComponentConfig(kerberosRule.getConfig());
UserStorageProviderModel model = new UserStorageProviderModel();
model.setLastSync(0);
model.setChangedSyncPeriod(-1);
model.setFullSyncPeriod(-1);
model.setName("test-ldap");
model.setPriority(0);
model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME);
model.setConfig(ldapConfig);
ldapModel = new UserStorageProviderModel(appRealm.addComponentModel(model));
}
}) {
@Override
protected void importRealm() {
server.importRealm(getClass().getResourceAsStream("/kerberos-test/kerberosrealm.json"));
}
};
@ClassRule
public static TestRule chain = RuleChain
.outerRule(kerberosRule)
.around(keycloakRule);
@Rule
public WebRule webRule = new WebRule(this);
@Rule
public AssertEvents events = new AssertEvents(keycloakRule);
@Override
protected CommonKerberosConfig getKerberosConfig() {
return new LDAPProviderKerberosConfig(ldapModel);
}
@Override
protected KeycloakRule getKeycloakRule() {
return keycloakRule;
}
@Override
protected AssertEvents getAssertEvents() {
return events;
}
@Test
public void spnegoLoginTest() throws Exception {
spnegoLoginTestImpl();
// Assert user was imported and hasn't any required action on him. Profile info is synced from LDAP
assertUser("hnelson", "hnelson@keycloak.org", "Horatio", "Nelson", false);
}
@Test
@Override
public void usernamePasswordLoginTest() throws Exception {
super.usernamePasswordLoginTest();
}
@Test
public void writableEditModeTest() throws Exception {
KeycloakRule keycloakRule = getKeycloakRule();
AssertEvents events = getAssertEvents();
// Change editMode to WRITABLE
updateProviderEditMode(UserStorageProvider.EditMode.WRITABLE);
// Login with username/password from kerberos
changePasswordPage.open();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
loginPage.assertCurrent();
loginPage.login("jduke", "theduke");
changePasswordPage.assertCurrent();
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
// Login with old password doesn't work, but with new password works
loginPage.login("jduke", "theduke");
loginPage.assertCurrent();
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.logout();
// Assert SPNEGO login with the new password as mode is writable
events.clear();
Response spnegoResponse = spnegoLogin("jduke", "newPass");
Assert.assertEquals(302, spnegoResponse.getStatus());
events.expectLogin()
.client("kerberos-app")
.user(keycloakRule.getUser("test", "jduke").getId())
.detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
//.detail(Details.AUTH_METHOD, "spnego")
.detail(Details.USERNAME, "jduke")
.assertEvent();
// Change password back
changePasswordPage.open();
// Only needed if you are providing a click thru to bypass kerberos. Currently there is a javascript
// to forward the user if kerberos isn't enabled.
//bypassPage.isCurrent();
//bypassPage.clickContinue();
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.changePassword("newPass", "theduke", "theduke");
Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
spnegoResponse.close();
events.clear();
}
}

View file

@ -1,149 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.storage.ldap;
import org.jboss.logging.Logger;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.models.LDAPConstants;
import org.keycloak.storage.UserStorageProvider;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPTestConfiguration {
private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class);
private String connectionPropertiesLocation;
private int sleepTime;
private boolean startEmbeddedLdapLerver = true;
private Map<String, String> config;
protected static final Map<String, String> PROP_MAPPINGS = new HashMap<String, String>();
protected static final Map<String, String> DEFAULT_VALUES = new HashMap<String, String>();
static {
PROP_MAPPINGS.put(LDAPConstants.CONNECTION_URL, "idm.test.ldap.connection.url");
PROP_MAPPINGS.put(LDAPConstants.BASE_DN, "idm.test.ldap.base.dn");
PROP_MAPPINGS.put(LDAPConstants.USERS_DN, "idm.test.ldap.user.dn.suffix");
PROP_MAPPINGS.put(LDAPConstants.BIND_DN, "idm.test.ldap.bind.dn");
PROP_MAPPINGS.put(LDAPConstants.BIND_CREDENTIAL, "idm.test.ldap.bind.credential");
PROP_MAPPINGS.put(LDAPConstants.VENDOR, "idm.test.ldap.vendor");
PROP_MAPPINGS.put(LDAPConstants.CONNECTION_POOLING, "idm.test.ldap.connection.pooling");
PROP_MAPPINGS.put(LDAPConstants.PAGINATION, "idm.test.ldap.pagination");
PROP_MAPPINGS.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "idm.test.ldap.batch.size.for.sync");
PROP_MAPPINGS.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "idm.test.ldap.username.ldap.attribute");
PROP_MAPPINGS.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, "idm.test.ldap.rdn.ldap.attribute");
PROP_MAPPINGS.put(LDAPConstants.USER_OBJECT_CLASSES, "idm.test.ldap.user.object.classes");
PROP_MAPPINGS.put(LDAPConstants.EDIT_MODE, "idm.test.ldap.edit.mode");
PROP_MAPPINGS.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "idm.test.kerberos.allow.kerberos.authentication");
PROP_MAPPINGS.put(KerberosConstants.KERBEROS_REALM, "idm.test.kerberos.realm");
PROP_MAPPINGS.put(KerberosConstants.SERVER_PRINCIPAL, "idm.test.kerberos.server.principal");
PROP_MAPPINGS.put(KerberosConstants.KEYTAB, "idm.test.kerberos.keytab");
PROP_MAPPINGS.put(KerberosConstants.DEBUG, "idm.test.kerberos.debug");
PROP_MAPPINGS.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "idm.test.kerberos.allow.password.authentication");
PROP_MAPPINGS.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "idm.test.kerberos.update.profile.first.login");
PROP_MAPPINGS.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "idm.test.kerberos.use.kerberos.for.password.authentication");
DEFAULT_VALUES.put(LDAPConstants.CONNECTION_URL, "ldap://localhost:10389");
DEFAULT_VALUES.put(LDAPConstants.BASE_DN, "dc=keycloak,dc=org");
DEFAULT_VALUES.put(LDAPConstants.USERS_DN, "ou=People,dc=keycloak,dc=org");
DEFAULT_VALUES.put(LDAPConstants.BIND_DN, "uid=admin,ou=system");
DEFAULT_VALUES.put(LDAPConstants.BIND_CREDENTIAL, "secret");
DEFAULT_VALUES.put(LDAPConstants.VENDOR, LDAPConstants.VENDOR_OTHER);
DEFAULT_VALUES.put(LDAPConstants.CONNECTION_POOLING, "true");
DEFAULT_VALUES.put(LDAPConstants.PAGINATION, "true");
DEFAULT_VALUES.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC));
DEFAULT_VALUES.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, null);
DEFAULT_VALUES.put(LDAPConstants.USER_OBJECT_CLASSES, null);
DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.toString());
DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab");
String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath();
DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true");
DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
DEFAULT_VALUES.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "false");
}
public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) {
LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration();
ldapTestConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation);
ldapTestConfiguration.loadConnectionProperties();
return ldapTestConfiguration;
}
protected void loadConnectionProperties() {
Properties p = new Properties();
try {
log.info("Reading LDAP configuration from: " + connectionPropertiesLocation);
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(connectionPropertiesLocation);
p.load(is);
}
catch (Exception e) {
throw new RuntimeException(e);
}
config = new HashMap<String, String>();
for (Map.Entry<String, String> property : PROP_MAPPINGS.entrySet()) {
String propertyName = property.getKey();
String configName = property.getValue();
String value = (String) p.get(configName);
if (value == null) {
value = DEFAULT_VALUES.get(propertyName);
}
config.put(propertyName, value);
}
startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true"));
sleepTime = Integer.parseInt(p.getProperty("idm.test.ldap.sleepTime", "1000"));
log.info("Start embedded server: " + startEmbeddedLdapLerver);
log.info("Read config: " + config);
}
public Map<String,String> getLDAPConfig() {
return config;
}
public void setConnectionPropertiesLocation(String connectionPropertiesLocation) {
this.connectionPropertiesLocation = connectionPropertiesLocation;
}
public boolean isStartEmbeddedLdapLerver() {
return startEmbeddedLdapLerver;
}
public int getSleepTime() {
return sleepTime;
}
}

View file

@ -146,7 +146,7 @@ public class SamlEcpProfileTest {
assertNotNull(samlResponse); assertNotNull(samlResponse);
ResponseType responseType = (ResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse)); ResponseType responseType = (ResponseType) new SAMLParser().parse(samlResponse);
StatusCodeType statusCode = responseType.getStatus().getStatusCode(); StatusCodeType statusCode = responseType.getStatus().getStatusCode();
assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
@ -229,7 +229,7 @@ public class SamlEcpProfileTest {
assertNotNull(samlResponse); assertNotNull(samlResponse);
StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse)); StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(samlResponse);
StatusCodeType statusCode = responseType.getStatus().getStatusCode(); StatusCodeType statusCode = responseType.getStatus().getStatusCode();
assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());

View file

@ -1,10 +0,0 @@
{
"realm": "test",
"resource": "kerberos-app",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8081/auth",
"ssl-required" : "external",
"credentials": {
"secret": "password"
}
}

View file

@ -1,53 +0,0 @@
{
"id": "test",
"realm": "test",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"resetPasswordAllowed": true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password", "kerberos" ],
"defaultRoles": [ "user" ],
"users" : [
{
"username" : "test-user@localhost",
"enabled": true,
"email" : "test-user@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"realmRoles": ["user"],
"applicationRoles": {
"account": [ "view-profile", "manage-account" ]
}
}
],
"scopeMappings": [
{
"client": "kerberos-app",
"roles": ["user"]
}
],
"applications": [
{
"name": "kerberos-app",
"enabled": true,
"baseUrl": "http://localhost:8081/kerberos-portal",
"redirectUris": [
"http://localhost:8081/kerberos-portal/*"
],
"adminUrl": "http://localhost:8081/kerberos-portal/logout",
"secret": "password"
}
],
"roles" : {
"realm" : [
{
"name": "user",
"description": "Have User privileges"
}
]
}
}

View file

@ -1,34 +0,0 @@
#
# Copyright 2016 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
idm.test.ldap.connection.url=ldap\://localhost\:10389
idm.test.ldap.base.dn=dc\=keycloak,dc\=org
idm.test.ldap.roles.dn.suffix=ou\=Roles,dc\=keycloak,dc\=org
idm.test.ldap.group.dn.suffix=ou\=Groups,dc\=keycloak,dc\=org
idm.test.ldap.user.dn.suffix=ou\=People,dc\=keycloak,dc\=org
idm.test.ldap.start.embedded.ldap.server=true
idm.test.ldap.bind.dn=uid\=admin,ou\=system
idm.test.ldap.bind.credential=secret
idm.test.ldap.connection.pooling=true
idm.test.ldap.pagination=true
idm.test.ldap.batch.size.for.sync=3
idm.test.kerberos.allow.kerberos.authentication=true
idm.test.kerberos.realm=KEYCLOAK.ORG
idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG
idm.test.kerberos.debug=false
idm.test.kerberos.use.kerberos.for.password.authentication=true

View file

@ -1,23 +0,0 @@
#
# Copyright 2016 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
idm.test.kerberos.allow.kerberos.authentication=true
idm.test.kerberos.realm=KEYCLOAK.ORG
idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG
idm.test.kerberos.debug=false
idm.test.kerberos.allow.password.authentication=true
idm.test.kerberos.update.profile.first.login=false

View file

@ -1,19 +0,0 @@
[libdefaults]
default_realm = KEYCLOAK.ORG
default_tgs_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
default_tkt_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
permitted_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
kdc_timeout = 30000
dns_lookup_realm = false
dns_lookup_kdc = false
dns_canonicalize_hostname = false
ignore_acceptor_hostname = true
forwardable = true
[realms]
KEYCLOAK.ORG = {
kdc = localhost:6088
}
[domain_realm]
localhost = KEYCLOAK.ORG

View file

@ -1,104 +0,0 @@
dn: dc=keycloak,dc=org
objectclass: dcObject
objectclass: organization
o: Keycloak
dc: Keycloak
dn: ou=People,dc=keycloak,dc=org
objectClass: organizationalUnit
objectClass: top
ou: People
dn: uid=krbtgt,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: KDC Service
sn: Service
uid: krbtgt
userPassword: secret
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=ldap,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: LDAP
sn: Service
uid: ldap
userPassword: randall
krb5PrincipalName: ${ldapSaslPrincipal}
krb5KeyVersionNumber: 0
dn: uid=HTTP,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: HTTP
sn: Service
uid: HTTP
userPassword: httppwd
krb5PrincipalName: HTTP/${hostname}@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=hnelson,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: Horatio
sn: Nelson
mail: hnelson@keycloak.org
uid: hnelson
userPassword: secret
krb5PrincipalName: hnelson@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=jduke,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: Java
sn: Duke
mail: jduke@keycloak.org
uid: jduke
userPassword: theduke
krb5PrincipalName: jduke@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=MyDuke,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: My
sn: Duke
mail: MyDuke@keycloak.org
uid: MyDuke
userPassword: theduke
krb5PrincipalName: MyDuke@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=gsstestserver,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: gsstestserver
sn: Service
uid: gsstestserver
userPassword: gsstestpwd
krb5PrincipalName: gsstestserver/xxx@KEYCLOAK.ORG
krb5KeyVersionNumber: 0

View file

@ -890,7 +890,12 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
}, },
onUpdate : function() { onUpdate : function() {
$scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); if ($scope.policy.config.resources && $scope.policy.config.resources._id) {
$scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
} else {
delete $scope.policy.config.resources
}
var policies = []; var policies = [];
for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) { for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
@ -920,7 +925,11 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
}, },
onCreate : function() { onCreate : function() {
$scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); if ($scope.policy.config.resources && $scope.policy.config.resources._id) {
$scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
} else {
delete $scope.policy.config.resources
}
var policies = []; var policies = [];

View file

@ -1717,7 +1717,7 @@ module.controller('RealmAdminEventsModalCtrl', function($scope, $filter, event)
$scope.event = event; $scope.event = event;
}); });
module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, TimeUnit) { module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, TimeUnit, $route) {
console.log('RealmBruteForceCtrl'); console.log('RealmBruteForceCtrl');
$scope.realm = realm; $scope.realm = realm;
@ -1780,8 +1780,7 @@ module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $
}; };
$scope.reset = function() { $scope.reset = function() {
$scope.realm = angular.copy(oldCopy); $route.reload();
$scope.changed = false;
}; };
}); });

View file

@ -67,7 +67,7 @@ country=Pa\u00EDs
emailVerified=Email verificat emailVerified=Email verificat
gssDelegationCredential=GSS Delegation Credential gssDelegationCredential=GSS Delegation Credential
loginTotpStep1=Instal\u00B7la <a href=\"https://fedorahosted.org/freeotp/\" target=\"_blank\">FreeOTP</a> o Google Authenticator al teu tel\u00E8fon m\u00F2bil. Les dues aplicacions estan disponibles a <a href=\"https://play.google.com\">Google Play</a> i en l''App Store d''Apple. loginTotpStep1=Instal\u00B7la <a href=\"https://freeotp.github.io/\" target=\"_blank\">FreeOTP</a> o Google Authenticator al teu tel\u00E8fon m\u00F2bil. Les dues aplicacions estan disponibles a <a href=\"https://play.google.com\">Google Play</a> i en l''App Store d''Apple.
loginTotpStep2=Obre l''aplicaci\u00F3 i escaneja el codi o introdueix la clau. loginTotpStep2=Obre l''aplicaci\u00F3 i escaneja el codi o introdueix la clau.
loginTotpStep3=Introdueix el codi \u00FAnic que et mostra l''aplicaci\u00F3 d''autenticaci\u00F3 i fes clic a Envia per finalitzar la configuraci\u00F3 loginTotpStep3=Introdueix el codi \u00FAnic que et mostra l''aplicaci\u00F3 d''autenticaci\u00F3 i fes clic a Envia per finalitzar la configuraci\u00F3
loginTotpOneTime=Codi d''un sol \u00FAs loginTotpOneTime=Codi d''un sol \u00FAs

View file

@ -65,7 +65,7 @@ country=Land
emailVerified=E-Mail verifiziert emailVerified=E-Mail verifiziert
gssDelegationCredential=GSS delegierte Berechtigung gssDelegationCredential=GSS delegierte Berechtigung
loginTotpStep1=Installieren Sie <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> oder <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> auf Ihrem Smartphone. loginTotpStep1=Installieren Sie <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> oder <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> auf Ihrem Smartphone.
loginTotpStep2=\u00D6ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein. loginTotpStep2=\u00D6ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein.
loginTotpStep3=Geben Sie den One-time Code welcher die Applikation generiert hat ein und klicken Sie auf Absenden. loginTotpStep3=Geben Sie den One-time Code welcher die Applikation generiert hat ein und klicken Sie auf Absenden.
loginTotpOneTime=One-time Code loginTotpOneTime=One-time Code

View file

@ -67,7 +67,7 @@ country=Country
emailVerified=Email verified emailVerified=Email verified
gssDelegationCredential=GSS Delegation Credential gssDelegationCredential=GSS Delegation Credential
loginTotpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or Google Authenticator on your mobile. Both applications are available in <a href="https://play.google.com">Google Play</a> and Apple App Store. loginTotpStep1=Install <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> or Google Authenticator on your mobile. Both applications are available in <a href="https://play.google.com">Google Play</a> and Apple App Store.
loginTotpStep2=Open the application and scan the barcode or enter the key loginTotpStep2=Open the application and scan the barcode or enter the key
loginTotpStep3=Enter the one-time code provided by the application and click Submit to finish the setup loginTotpStep3=Enter the one-time code provided by the application and click Submit to finish the setup
loginTotpOneTime=One-time code loginTotpOneTime=One-time code

View file

@ -67,7 +67,7 @@ country=Pa\u00EDs
emailVerified=Email verificado emailVerified=Email verificado
gssDelegationCredential=GSS Delegation Credential gssDelegationCredential=GSS Delegation Credential
loginTotpStep1=Instala <a href=\"https://fedorahosted.org/freeotp/\" target=\"_blank\">FreeOTP</a> o Google Authenticator en tu tel\u00E9fono m\u00F3vil. Ambas aplicaciones est\u00E1n disponibles en <a href=\"https://play.google.com\">Google Play</a> y en la App Store de Apple. loginTotpStep1=Instala <a href=\"https://freeotp.github.io/\" target=\"_blank\">FreeOTP</a> o Google Authenticator en tu tel\u00E9fono m\u00F3vil. Ambas aplicaciones est\u00E1n disponibles en <a href=\"https://play.google.com\">Google Play</a> y en la App Store de Apple.
loginTotpStep2=Abre la aplicacvi\u00F3n y escanea el c\u00F3digo o introduce la clave. loginTotpStep2=Abre la aplicacvi\u00F3n y escanea el c\u00F3digo o introduce la clave.
loginTotpStep3=Introduce el c\u00F3digo \u00FAnico que te muestra la aplicaci\u00F3n de autenticaci\u00F3n y haz clic en Enviar para finalizar la configuraci\u00F3n loginTotpStep3=Introduce el c\u00F3digo \u00FAnico que te muestra la aplicaci\u00F3n de autenticaci\u00F3n y haz clic en Enviar para finalizar la configuraci\u00F3n
loginTotpOneTime=C\u00F3digo de un solo uso loginTotpOneTime=C\u00F3digo de un solo uso

View file

@ -67,7 +67,7 @@ country=Pays
emailVerified=Courriel v\u00e9rifi\u00e9 emailVerified=Courriel v\u00e9rifi\u00e9
gssDelegationCredential=Accr\u00e9ditation de d\u00e9l\u00e9gation GSS gssDelegationCredential=Accr\u00e9ditation de d\u00e9l\u00e9gation GSS
loginTotpStep1=Installez <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store. loginTotpStep1=Installez <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
loginTotpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9. loginTotpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9.
loginTotpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer. loginTotpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
loginTotpOneTime=Code \u00e0 usage unique loginTotpOneTime=Code \u00e0 usage unique

View file

@ -63,7 +63,7 @@ country=Paese
emailVerified=Email verificata emailVerified=Email verificata
gssDelegationCredential=credenziali gss delegation gssDelegationCredential=credenziali gss delegation
loginTotpStep1=Installa <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile loginTotpStep1=Installa <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile
loginTotpStep2=Apri l''applicazione e scansione il barcode o scrivi la chiave loginTotpStep2=Apri l''applicazione e scansione il barcode o scrivi la chiave
loginTotpStep3=Scrivi il codice one-time fornito dall''applicazione e premi Invia per finire il setup loginTotpStep3=Scrivi il codice one-time fornito dall''applicazione e premi Invia per finire il setup
loginTotpOneTime=Codice one-time loginTotpOneTime=Codice one-time

View file

@ -68,7 +68,7 @@ country=国
emailVerified=確認済みEメール emailVerified=確認済みEメール
gssDelegationCredential=GSS 代行クレデンシャル gssDelegationCredential=GSS 代行クレデンシャル
loginTotpStep1=<a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> または Google Authenticator (Google認証システム) をご自身のデバイスにインストールしてください。これらのアプリケーションは <a href="https://play.google.com">Google Play</a> と Apple App Store で入手できます。 loginTotpStep1=<a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> または Google Authenticator (Google認証システム) をご自身のデバイスにインストールしてください。これらのアプリケーションは <a href="https://play.google.com">Google Play</a> と Apple App Store で入手できます。
loginTotpStep2=アプリケーションを開きバーコードをスキャンするかキーを入力してください。 loginTotpStep2=アプリケーションを開きバーコードをスキャンするかキーを入力してください。
loginTotpStep3=アプリケーションで提供されたワンタイムコードを入力して送信をクリックし、セットアップを完了してください。 loginTotpStep3=アプリケーションで提供されたワンタイムコードを入力して送信をクリックし、セットアップを完了してください。
loginTotpOneTime=ワンタイムコード loginTotpOneTime=ワンタイムコード

View file

@ -67,7 +67,7 @@ country=\u0160alis
emailVerified=El. pa\u0161to adresas patvirtintas emailVerified=El. pa\u0161to adresas patvirtintas
gssDelegationCredential=GSS prisijungimo duomen\u0173 delegavimas gssDelegationCredential=GSS prisijungimo duomen\u0173 delegavimas
loginTotpStep1=\u012Ediekite <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> arba Google Authenticator savo \u012Frenginyje. Program\u0117l\u0117s prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store. loginTotpStep1=\u012Ediekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo \u012Frenginyje. Program\u0117l\u0117s prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store.
loginTotpStep2=Atidarykite program\u0117l\u0119 ir nuskenuokite barkod\u0105 arba \u012Fveskite kod\u0105. loginTotpStep2=Atidarykite program\u0117l\u0119 ir nuskenuokite barkod\u0105 arba \u012Fveskite kod\u0105.
loginTotpStep3=\u012Eveskite program\u0117l\u0117je sugeneruot\u0105 vien\u0105 kart\u0105 galiojant\u012F kod\u0105 ir paspauskite Saugoti nor\u0117dami prisijungti. loginTotpStep3=\u012Eveskite program\u0117l\u0117je sugeneruot\u0105 vien\u0105 kart\u0105 galiojant\u012F kod\u0105 ir paspauskite Saugoti nor\u0117dami prisijungti.
loginTotpOneTime=Vienkartinis kodas loginTotpOneTime=Vienkartinis kodas

View file

@ -67,7 +67,7 @@ country=Land
emailVerified=E-postadresse bekreftet emailVerified=E-postadresse bekreftet
gssDelegationCredential=GSS legitimasjons-delegering gssDelegationCredential=GSS legitimasjons-delegering
loginTotpStep1=Installer <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> eller Google Authenticator p\u00E5 din mobiltelefon. Begge applikasjoner er tilgjengelige p\u00E5 <a href="https://play.google.com">Google Play</a> og Apple App Store. loginTotpStep1=Installer <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> eller Google Authenticator p\u00E5 din mobiltelefon. Begge applikasjoner er tilgjengelige p\u00E5 <a href="https://play.google.com">Google Play</a> og Apple App Store.
loginTotpStep2=\u00C5pne applikasjonen og skann strekkoden eller skriv inn koden loginTotpStep2=\u00C5pne applikasjonen og skann strekkoden eller skriv inn koden
loginTotpStep3=Skriv inn engangskoden fra applikasjonen og klikk send inn for \u00E5 fullf\u00F8re loginTotpStep3=Skriv inn engangskoden fra applikasjonen og klikk send inn for \u00E5 fullf\u00F8re
loginTotpOneTime=Engangskode loginTotpOneTime=Engangskode

View file

@ -67,7 +67,7 @@ country=Pa\u00EDs
emailVerified=E-mail verificado emailVerified=E-mail verificado
gssDelegationCredential=gss delega\u00E7\u00E3o credencial gssDelegationCredential=gss delega\u00E7\u00E3o credencial
loginTotpStep1=Instale <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu celular loginTotpStep1=Instale <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu celular
loginTotpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo loginTotpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo
loginTotpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o loginTotpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o
loginTotpOneTime=C\u00F3digo autenticador loginTotpOneTime=C\u00F3digo autenticador

View file

@ -67,7 +67,7 @@ country=\u0421\u0442\u0440\u0430\u043D\u0430
emailVerified=Email \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D emailVerified=Email \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D
gssDelegationCredential=\u0414\u0435\u043B\u0435\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 GSS gssDelegationCredential=\u0414\u0435\u043B\u0435\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 GSS
loginTotpStep1=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> \u0438\u043B\u0438 Google Authenticator. \u041E\u0431\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0432 <a href="https://play.google.com">Google Play</a> \u0438 Apple App Store. loginTotpStep1=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> \u0438\u043B\u0438 Google Authenticator. \u041E\u0431\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0432 <a href="https://play.google.com">Google Play</a> \u0438 Apple App Store.
loginTotpStep2=\u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u0440\u043E\u0441\u043A\u0430\u043D\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0440\u043A\u043E\u0434, \u043B\u0438\u0431\u043E \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043B\u044E\u0447 loginTotpStep2=\u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u0440\u043E\u0441\u043A\u0430\u043D\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0440\u043A\u043E\u0434, \u043B\u0438\u0431\u043E \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043B\u044E\u0447
loginTotpStep3=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C, \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438 loginTotpStep3=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C, \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438
loginTotpOneTime=\u041E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C loginTotpOneTime=\u041E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C