Merge upstream-master into tkyjovsk-master

This commit is contained in:
Tomas Kyjovsky 2015-06-10 10:40:32 +02:00
commit 9dd7a947e9
294 changed files with 4165 additions and 2958 deletions

View file

@ -10,8 +10,8 @@ import org.keycloak.provider.Spi;
public class IdentityProviderMapperSpi implements Spi {
@Override
public boolean isPrivate() {
return false;
public boolean isInternal() {
return true;
}
@Override

View file

@ -29,7 +29,7 @@ public class IdentityProviderSpi implements Spi {
public static final String IDENTITY_PROVIDER_SPI_NAME = "identity_provider";
@Override
public boolean isPrivate() {
public boolean isInternal() {
return false;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class FileConnectionSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class HttpClientSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class InfinispanConnectionSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -19,7 +19,7 @@
<column name="AUTH_USER_ID" type="VARCHAR(255)"/>
<column name="IP_ADDRESS" type="VARCHAR(255)"/>
<column name="RESOURCE_PATH" type="VARCHAR(2550)"/>
<column name="REPRESENTATION" type="BLOB(25500)"/>
<column name="REPRESENTATION" type="TEXT(25500)"/>
<column name="ERROR" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="AUTHENTICATOR">
@ -28,7 +28,7 @@
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="PROVIDER_ID" type="VARCHAR(36)"/>
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="AUTHENTICATION_FLOW">
<column name="ID" type="VARCHAR(36)">
@ -94,6 +94,9 @@
<column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="EDIT_USERNAME_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<createTable tableName="CLIENT_SESSION_AUTH_STATUS">
<column name="AUTHENTICATOR" type="VARCHAR(36)">
@ -107,6 +110,19 @@
<addColumn tableName="CLIENT_SESSION">
<column name="AUTH_USER_ID" type="VARCHAR(36)"/>
</addColumn>
<addColumn tableName="IDENTITY_PROVIDER">
<column name="TRUST_EMAIL" type="BOOLEAN" defaultValueBoolean="false"/>
<column name="UPDATE_PROFILE_FIRST_LGN_MD" type="VARCHAR(10)" defaultValue="on">
<constraints nullable="false"/>
</column>
</addColumn>
<!-- migrate value from UPDATE_PROFILE_FIRST_LOGIN to UPDATE_PROFILE_FIRST_LGN_MD then drop it -->
<update tableName="IDENTITY_PROVIDER">
<column name="UPDATE_PROFILE_FIRST_LGN_MD" value="off"/>
<where>UPDATE_PROFILE_FIRST_LOGIN = false</where>
</update>
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LOGIN"/>
<addColumn tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" type="VARCHAR(36)">
<constraints nullable="false"/>
@ -147,5 +163,7 @@
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_FEDPRV" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="USER_FEDERATION_MAPPER_ID" baseTableName="USER_FEDERATION_MAPPER_CONFIG" constraintName="FK_FEDMAPPER_CFG" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_MAPPER"/>
<dropColumn tableName="REALM" columnName="PASSWORD_CRED_GRANT_ALLOWED"/>
</changeSet>
</databaseChangeLog>

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class JpaConnectionSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class JpaUpdaterSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -6,11 +6,7 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.jboss.logging.Logger;
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
import org.keycloak.connections.mongo.updater.impl.updates.Update;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_CR1;
import org.keycloak.connections.mongo.updater.impl.updates.*;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@ -30,7 +26,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
Update1_0_0_Final.class,
Update1_1_0_Beta1.class,
Update1_2_0_Beta1.class,
Update1_2_0_CR1.class
Update1_2_0_CR1.class,
Update1_3_0_Beta1.class
};
@Override

View file

@ -53,6 +53,10 @@ public abstract class Update {
log.debugv("Deleted entries from {0}", collection);
}
protected void removeField(String collection, String field) {
db.getCollection(collection).update(new BasicDBObject(), new BasicDBObject("$unset" , new BasicDBObject(field, 1)), false, true);
}
protected void renameCollection(String collection, String newName) {
db.getCollection(collection).rename(newName);
}

View file

@ -0,0 +1,59 @@
package org.keycloak.connections.mongo.updater.impl.updates;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Update1_3_0_Beta1 extends Update {
@Override
public String getId() {
return "1.3.0.Beta1";
}
@Override
public void update(KeycloakSession session) {
deleteEntries("clientSessions");
deleteEntries("sessions");
removeField("realms", "passwordCredentialGrantAllowed");
updateIdentityProviders();
}
private void updateIdentityProviders() {
DBCollection realms = db.getCollection("realms");
DBCursor realmsCursor = realms.find();
try {
while (realmsCursor.hasNext()) {
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders");
if (identityProviders != null) {
for (Object ipObj : identityProviders) {
BasicDBObject identityProvider = (BasicDBObject) ipObj;
boolean updateProfileFirstLogin = identityProvider.getBoolean("updateProfileFirstLogin");
String upflMode = updateProfileFirstLogin ? IdentityProviderRepresentation.UPFLM_ON : IdentityProviderRepresentation.UPFLM_OFF;
identityProvider.put("updateProfileFirstLoginMode", upflMode);
identityProvider.removeField("updateProfileFirstLogin");
identityProvider.put("trustEmail", false);
}
}
realms.save(realm);
}
} finally {
realmsCursor.close();
}
}
}

View file

@ -43,7 +43,10 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.entities.ProtocolMapperEntity",
"org.keycloak.models.entities.IdentityProviderMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity"
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
"org.keycloak.models.entities.AuthenticationExecutionEntity",
"org.keycloak.models.entities.AuthenticationFlowEntity",
"org.keycloak.models.entities.AuthenticatorEntity",
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class MongoConnectionSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class MongoUpdaterSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -29,7 +29,26 @@ public class IdentityProviderRepresentation {
protected String internalId;
protected String providerId;
protected boolean enabled = true;
protected boolean updateProfileFirstLogin = true;
public static final String UPFLM_ON = "on";
public static final String UPFLM_MISSING = "missing";
public static final String UPFLM_OFF = "off";
/**
* Mode of profile update after first login when user is created over this identity provider. Possible values:
* <ul>
* <li><code>on</code> - update profile page is presented for all users
* <li><code>missing</code> - update profile page is presented for users with missing some of mandatory user profile fields
* <li><code>off</code> - update profile page is newer shown after first login
* </ul>
*
* @see #UPFLM_ON
* @see #UPFLM_MISSING
* @see #UPFLM_OFF
*/
protected String updateProfileFirstLoginMode = UPFLM_ON;
protected boolean trustEmail;
protected boolean storeToken;
protected boolean addReadTokenRoleOnCreate;
protected boolean authenticateByDefault;
@ -75,12 +94,29 @@ public class IdentityProviderRepresentation {
this.enabled = enabled;
}
public boolean isUpdateProfileFirstLogin() {
return this.updateProfileFirstLogin;
/**
*
* Deprecated because replaced by {@link #updateProfileFirstLoginMode}. Kept here to allow import of old realms.
*
* @deprecated {@link #setUpdateProfileFirstLoginMode(String)}
*/
@Deprecated
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
this.updateProfileFirstLoginMode = updateProfileFirstLogin ? UPFLM_ON : UPFLM_OFF;
}
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
this.updateProfileFirstLogin = updateProfileFirstLogin;
/**
* @return see {@link #updateProfileFirstLoginMode}
*/
public String getUpdateProfileFirstLoginMode() {
return updateProfileFirstLoginMode;
}
/**
* @param updateProfileFirstLoginMode see {@link #updateProfileFirstLoginMode}
*/
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
}
public boolean isAuthenticateByDefault() {
@ -106,4 +142,13 @@ public class IdentityProviderRepresentation {
public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) {
this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate;
}
public boolean isTrustEmail() {
return trustEmail;
}
public void setTrustEmail(boolean trustEmail) {
this.trustEmail = trustEmail;
}
}

View file

@ -18,12 +18,14 @@ public class RealmRepresentation {
protected Integer accessCodeLifespanLogin;
protected Boolean enabled;
protected String sslRequired;
@Deprecated
protected Boolean passwordCredentialGrantAllowed;
protected Boolean registrationAllowed;
protected Boolean registrationEmailAsUsername;
protected Boolean rememberMe;
protected Boolean verifyEmail;
protected Boolean resetPasswordAllowed;
protected Boolean editUsernameAllowed;
protected Boolean userCacheEnabled;
protected Boolean realmCacheEnabled;
@ -268,10 +270,6 @@ public class RealmRepresentation {
return passwordCredentialGrantAllowed;
}
public void setPasswordCredentialGrantAllowed(Boolean passwordCredentialGrantAllowed) {
this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
}
public Boolean isRegistrationAllowed() {
return registrationAllowed;
}
@ -328,6 +326,14 @@ public class RealmRepresentation {
this.resetPasswordAllowed = resetPassword;
}
public Boolean isEditUsernameAllowed() {
return editUsernameAllowed;
}
public void setEditUsernameAllowed(Boolean editUsernameAllowed) {
this.editUsernameAllowed = editUsernameAllowed;
}
@Deprecated
public Boolean isSocial() {
return social;

12
core/src/main/java/org/keycloak/util/HtmlUtils.java Normal file → Executable file
View file

@ -34,7 +34,17 @@ public class HtmlUtils {
for (int i = 0; i < value.length(); i++) {
char chr = value.charAt(i);
if (chr != '\'' && chr != '"' && chr != '<' && chr != '>' && chr != '/') {
if (chr == '<') {
escaped.append("&lt;");
} else if (chr == '>') {
escaped.append("&gt;");
} else if (chr == '"') {
escaped.append("&quot;");
} else if (chr == '\'') {
escaped.append("&apos;");
} else if (chr == '&') {
escaped.append("&amp;");
} else {
escaped.append(chr);
}
}

View file

@ -14,7 +14,6 @@
<outputDirectory>keycloak</outputDirectory>
<excludes>
<exclude>**/*.sh</exclude>
<exclude>standalone/configuration/standalone-keycloak.xml</exclude>
</excludes>
</fileSet>
<fileSet>
@ -25,6 +24,20 @@
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/keycloak-server-overlay-${project.version}</directory>
<outputDirectory>keycloak</outputDirectory>
<excludes>
<exclude>standalone/configuration/standalone-keycloak.xml</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/keycloak-wf9-adapter-${project.version}</directory>
<outputDirectory>keycloak</outputDirectory>
<excludes>
<exclude>standalone/configuration/standalone-keycloak.xml</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/keycloak-docs-${project.version}</directory>
<outputDirectory>docs</outputDirectory>
@ -34,5 +47,11 @@
<outputDirectory>examples</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>${project.build.directory}/unpacked/standalone.xml</source>
<outputDirectory>keycloak/standalone/configuration</outputDirectory>
</file>
</files>
</assembly>

View file

@ -16,7 +16,12 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-dist</artifactId>
<artifactId>keycloak-server-overlay</artifactId>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wf9-adapter-dist</artifactId>
<type>zip</type>
</dependency>
<dependency>
@ -63,7 +68,7 @@
</configuration>
</execution>
<execution>
<id>unpack-server-overlay</id>
<id>unpack-server</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
@ -74,7 +79,24 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-overlay</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked/wildfly-${wildfly.version}</outputDirectory>
<outputDirectory>${project.build.directory}/unpacked/keycloak-server-overlay-${project.version}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-adapter</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wf9-adapter-dist</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked/keycloak-wf9-adapter-${project.version}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
@ -134,7 +156,7 @@
<includes>
<include>standalone.xml</include>
</includes>
<outputDir>${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration</outputDir>
<outputDir>${project.build.directory}/unpacked/</outputDir>
</transformationSet>
</transformationSets>
</configuration>

View file

@ -1,72 +0,0 @@
{
"admin": {
"realm": "master"
},
"eventsStore": {
"provider": "jpa",
"jpa": {
"exclude-events": [ "REFRESH_TOKEN" ]
}
},
"realm": {
"provider": "jpa"
},
"user": {
"provider": "jpa"
},
"userSessions": {
"provider" : "mem"
},
"realmCache": {
"provider": "mem"
},
"userCache": {
"provider": "mem",
"mem": {
"maxSize": 20000
}
},
"timer": {
"provider": "basic"
},
"theme": {
"default": "keycloak",
"staticMaxAge": 2592000,
"cacheTemplates": true,
"cacheThemes": true,
"folder": {
"dir": "${jboss.server.config.dir}/themes"
}
},
"login": {
"provider": "freemarker"
},
"account": {
"provider": "freemarker"
},
"email": {
"provider": "freemarker"
},
"scheduled": {
"interval": 900
},
"connectionsJpa": {
"default": {
"dataSource": "java:jboss/datasources/KeycloakDS",
"databaseSchema": "update"
}
}
}

View file

@ -1,2 +0,0 @@
Any provider implementation jars and libraries in this folder will be loaded by Keycloak. See the providers
section in the documentation for more details.

View file

@ -1,3 +0,0 @@
Themes to configure the look and feel of login pages and account management console. It's not recommended to
modify existing the built-in themes, instead you should create a new theme that extends a built-in theme. See the theme
section in the documentation for more details.

View file

@ -39,11 +39,9 @@
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<auth-server name="main-auth-server">
<enabled>true</enabled>
<web-context>auth</web-context>
</auth-server>
<web-context>auth</web-context>
</subsystem>
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
</xsl:copy>
</xsl:template>

View file

@ -126,7 +126,7 @@
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/auth-server</outputDirectory>
<outputDirectory>${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>

View file

@ -4,7 +4,7 @@
<module xmlns="urn:jboss:module:1.1" name="de.idyl.winzipaes">
<resources>
<!-- Insert resources here -->
<artifact name="${de.idyl:winzipaes}"/>
</resources>
<dependencies>
<module name="javax.api"/>

View file

@ -25,7 +25,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-server-subsystem">
<properties>
<property name="keycloak-version" value="${project.version}"/>
<property name="auth-server-exploded" value="false"/>
<property name="server-war-exploded" value="false"/>
</properties>
<resources>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="sun.jdk.jgss">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<system export="true">
<paths>
<path name="sun/security/jgss" />
<path name="sun/security/jgss/spi" />
<path name="sun/security/jgss/krb5" />
</paths>
</system>
</dependencies>
</module>

View file

@ -46,11 +46,6 @@
<maven-resource group="net.iharder" artifact="base64"/>
</module-def>
<module-def name="org.bouncycastle">
<maven-resource group="org.bouncycastle" artifact="bcprov-jdk15on"/>
<maven-resource group="org.bouncycastle" artifact="bcpkix-jdk15on"/>
</module-def>
<module-def name="org.keycloak.keycloak-broker-core">
<maven-resource group="org.keycloak" artifact="keycloak-broker-core"/>
</module-def>
@ -71,10 +66,6 @@
<maven-resource group="org.keycloak" artifact="keycloak-services"/>
</module-def>
<module-def name="org.keycloak.keycloak-wildfly-extensions">
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-extensions"/>
</module-def>
<module-def name="com.google.zxing.core">
<maven-resource group="com.google.zxing" artifact="core"/>
</module-def>
@ -315,40 +306,9 @@
<module-def name="org.keycloak.keycloak-server"></module-def>
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>
<module-def name="org.keycloak.keycloak-jboss-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
</module-def>
<module-def name="org.keycloak.keycloak-as7-adapter">
<maven-resource group="org.keycloak" artifact="keycloak-as7-adapter"/>
<maven-resource group="org.keycloak" artifact="keycloak-tomcat-core-adapter"/>
</module-def>
<module-def name="org.keycloak.keycloak-undertow-adapter">
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter"/>
</module-def>
<module-def name="org.keycloak.keycloak-wildfly-adapter">
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-adapter"/>
</module-def>
<module-def name="org.keycloak.keycloak-server-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-server-subsystem"/>
</module-def>
<module-def name="org.keycloak.keycloak-as7-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-as7-subsystem"/>
</module-def>
<module-def name="org.apache.httpcomponents" slot="4.3">
<maven-resource group="org.apache.httpcomponents" artifact="httpclient"/>
<maven-resource group="org.apache.httpcomponents" artifact="httpcore"/>
<maven-resource group="org.apache.httpcomponents" artifact="httpmime"/>
</module-def>
</target>
<target name="clean-target">

View file

@ -34,26 +34,6 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jboss-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
@ -64,18 +44,6 @@
<artifactId>keycloak-server</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-subsystem</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</dependency>
</dependencies>
<build>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.apache.httpcomponents" slot="4.3">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.apache.commons.codec"/>
<module name="org.apache.commons.logging"/>
<module name="org.apache.james.mime4j"/>
</dependencies>
</module>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.bouncycastle">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
</dependencies>
</module>

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-core">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.codehaus.jackson.jackson-core-asl"/>
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
</module>

View file

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2014, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-subsystem">
<properties>
<property name="keycloak-version" value="${project.version}"/>
</properties>
<resources>
<resource-root path="."/>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.staxmapper"/>
<module name="org.jboss.as.controller"/>
<module name="org.jboss.as.ee"/>
<module name="org.jboss.as.server"/>
<module name="org.jboss.modules"/>
<module name="org.jboss.msc"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.vfs"/>
<module name="org.jboss.as.web-common" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.version" optional="true"/>
<module name="org.keycloak.keycloak-as7-adapter" optional="true"/>
<module name="org.jboss.metadata"/>
</dependencies>
</module>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-as7-adapter">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.bouncycastle" />
<module name="org.codehaus.jackson.jackson-core-asl"/>
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="javax.servlet.api"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.as.security"/>
<module name="org.jboss.as.web"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
</module>

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-as7-subsystem">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.keycloak.keycloak-as7-adapter"/>
<module name="org.jboss.staxmapper"/>
<module name="org.jboss.as.controller"/>
<module name="org.jboss.as.server"/>
<module name="org.jboss.as.web"/>
<module name="org.jboss.modules"/>
<module name="org.jboss.msc"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.vfs"/>
<module name="org.jboss.metadata"/>
</dependencies>
</module>

View file

@ -14,7 +14,7 @@
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.jboss.logging"/>
<module name="javax.api"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.apache.httpcomponents"/>
</dependencies>
</module>

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-jboss-adapter-core">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
</module>

View file

@ -16,7 +16,7 @@
<module name="org.keycloak.keycloak-login-api"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.keycloak.keycloak-forms-common-freemarker"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/>
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>

View file

@ -46,7 +46,6 @@
<module name="org.jboss.as.web-common" optional="true"/>
<module name="org.jboss.as.web" optional="true"/>
<module name="org.jboss.as.version" optional="true"/>
<module name="org.keycloak.keycloak-as7-adapter" optional="true"/>
<module name="org.jboss.metadata"/>
</dependencies>
</module>

View file

@ -9,10 +9,6 @@
<dependencies>
<module name="org.keycloak.keycloak-account-api" services="import"/>
<module name="org.keycloak.keycloak-account-freemarker" services="import"/>
<module name="org.keycloak.keycloak-adapter-core" services="import"/>
<module name="org.keycloak.keycloak-adapter-subsystem" services="import"/>
<module name="org.keycloak.keycloak-as7-adapter" services="import"/>
<module name="org.keycloak.keycloak-as7-subsystem" services="import"/>
<module name="org.keycloak.keycloak-connections-infinispan" services="import"/>
<module name="org.keycloak.keycloak-connections-jpa" services="import"/>
<module name="org.keycloak.keycloak-connections-jpa-liquibase" services="import"/>
@ -36,7 +32,6 @@
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
<module name="org.keycloak.keycloak-invalidation-cache-model" services="import"/>
<module name="org.keycloak.keycloak-jboss-adapter-core" services="import"/>
<module name="org.keycloak.keycloak-js-adapter" services="import"/>
<module name="org.keycloak.keycloak-kerberos-federation" services="import"/>
<module name="org.keycloak.keycloak-ldap-federation" services="import"/>
@ -61,8 +56,6 @@
<module name="org.keycloak.keycloak-server-subsystem" services="import"/>
<module name="org.keycloak.keycloak-timer-api" services="import"/>
<module name="org.keycloak.keycloak-timer-basic" services="import"/>
<module name="org.keycloak.keycloak-undertow-adapter" services="import"/>
<module name="org.keycloak.keycloak-wildfly-adapter" services="import"/>
</dependencies>
</module>

View file

@ -36,7 +36,6 @@
<module name="org.keycloak.keycloak-forms-common-themes" services="import"/>
<module name="org.keycloak.keycloak-invalidation-cache-infinispan" services="import"/>
<module name="org.keycloak.keycloak-invalidation-cache-model" services="import"/>
<module name="org.keycloak.keycloak-jboss-adapter-core" services="import"/>
<module name="org.keycloak.keycloak-js-adapter" services="import"/>
<module name="org.keycloak.keycloak-kerberos-federation" services="import"/>
<module name="org.keycloak.keycloak-ldap-federation" services="import"/>
@ -50,7 +49,6 @@
<module name="org.keycloak.keycloak-model-sessions-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mem" services="import"/>
<module name="org.keycloak.keycloak-model-sessions-mongo" services="import"/>
<module name="org.keycloak.keycloak-wildfly-extensions" services="import"/>
<module name="org.keycloak.keycloak-saml-protocol" services="import"/>
<module name="org.keycloak.keycloak-services" export="true" services="import"/>
@ -78,7 +76,7 @@
<module name="net.iharder.base64"/>
<module name="javax.api"/>
<module name="javax.activation.api"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.apache.httpcomponents"/>
</dependencies>
</module>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-undertow-adapter">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.bouncycastle" />
<module name="org.codehaus.jackson.jackson-core-asl"/>
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="javax.servlet.api"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
</module>

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-adapter">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.bouncycastle" />
<module name="org.codehaus.jackson.jackson-core-asl"/>
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="javax.servlet.api"/>
<module name="org.jboss.logging"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
</module>

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-extensions">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/>
<module name="org.keycloak.keycloak-services"/>
<module name="org.keycloak.keycloak-forms-common-freemarker"/>
<module name="org.keycloak.keycloak-forms-common-themes"/>
<module name="org.jboss.modules"/>
</dependencies>
</module>

View file

@ -28,13 +28,13 @@
<modules>
<module>adapters</module>
<!--<module>demo-dist</module>-->
<module>demo-dist</module>
<module>docs-dist</module>
<module>examples-dist</module>
<module>modules</module>
<module>proxy-dist</module>
<module>server-dist</module>
<!--<module>server-overlay</module>-->
<module>server-overlay</module>
<module>src-dist</module>
<module>subsystem-war</module>
<module>feature-packs</module>

View file

@ -10,20 +10,38 @@
<fileSets>
<fileSet>
<directory>${project.build.directory}/unpacked/modules</directory>
<outputDirectory>modules</outputDirectory>
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/modules/system/layers/base</directory>
<outputDirectory>modules/system/layers/base</outputDirectory>
<includes>
<include>com/google/zxing/**</include>
<include>de/idyl/winzipaes/**</include>
<include>net/iharder/**</include>
<include>org/freemarker/**</include>
<include>org/keycloak/**</include>
<include>org/liquibase/**</include>
<include>org/mongodb/**</include>
<include>org/twitter4j/**</include>
<include>sun/jdk/jgss/**</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/content</directory>
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/content</directory>
<outputDirectory></outputDirectory>
</fileSet>
<fileSet>
<directory>../../forms/common-themes/src/main/resources/theme</directory>
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/themes</directory>
<outputDirectory>standalone/configuration/themes</outputDirectory>
<includes>
<include>**/**</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/providers</directory>
<outputDirectory>standalone/configuration/providers</outputDirectory>
<includes>
<include>**/**</include>
</includes>
</fileSet>
<fileSet>
<directory>../../</directory>
<includes>
@ -31,25 +49,19 @@
</includes>
<outputDirectory></outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration/standalone.xml</source>
<source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/standalone.xml</source>
<outputDirectory>standalone/configuration</outputDirectory>
<destName>standalone-keycloak.xml</destName>
</file>
<file>
<source>src/main/keycloak-server.json</source>
<source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
<outputDirectory>standalone/configuration</outputDirectory>
</file>
<file>
<source>src/main/themes/README.txt</source>
<outputDirectory>standalone/configuration/themes</outputDirectory>
</file>
<file>
<source>src/main/providers/README.txt</source>
<outputDirectory>standalone/configuration/providers</outputDirectory>
</file>
</files>
</assembly>

View file

@ -10,49 +10,27 @@
<artifactId>keycloak-server-overlay</artifactId>
<packaging>pom</packaging>
<name>Keycloak Server Overlay</name>
<name>Keycloak Server Overlay Distribution</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jboss-modules</artifactId>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-dist</artifactId>
<artifactId>keycloak-server-dist</artifactId>
<type>zip</type>
</dependency>
</dependencies>
<build>
<finalName>keycloak-overlay-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-standalone-xml</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-dist</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked</outputDirectory>
</artifactItem>
</artifactItems>
<includes>*/standalone/configuration/standalone.xml</includes>
</configuration>
</execution>
<execution>
<id>unpack-module</id>
<id>unpack-server-dist</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
@ -61,9 +39,9 @@
<artifactItems>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jboss-modules</artifactId>
<artifactId>keycloak-server-dist</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked/modules</outputDirectory>
<outputDirectory>${project.build.directory}/unpacked</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
@ -71,32 +49,7 @@
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>generate-resources</id>
<phase>package</phase>
<goals>
<goal>transform</goal>
</goals>
<configuration>
<transformationSets>
<transformationSet>
<dir>${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration</dir>
<stylesheet>src/main/xslt/standalone.xsl</stylesheet>
<includes>
<include>standalone.xml</include>
</includes>
<outputDir>${project.build.directory}/unpacked/wildfly-${wildfly.version}/standalone/configuration</outputDir>
</transformationSet>
</transformationSets>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
@ -109,9 +62,11 @@
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<outputDirectory>target</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<recompressZippedFiles>true</recompressZippedFiles>
<finalName>${project.build.finalName}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<workDirectory>${project.build.directory}/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>

View file

@ -1,72 +0,0 @@
{
"admin": {
"realm": "master"
},
"eventsStore": {
"provider": "jpa",
"jpa": {
"exclude-events": [ "REFRESH_TOKEN" ]
}
},
"realm": {
"provider": "jpa"
},
"user": {
"provider": "jpa"
},
"userSessions": {
"provider" : "mem"
},
"realmCache": {
"provider": "mem"
},
"userCache": {
"provider": "mem",
"mem": {
"maxSize": 20000
}
},
"timer": {
"provider": "basic"
},
"theme": {
"default": "keycloak",
"staticMaxAge": 2592000,
"cacheTemplates": true,
"cacheThemes": true,
"folder": {
"dir": "${jboss.server.config.dir}/themes"
}
},
"login": {
"provider": "freemarker"
},
"account": {
"provider": "freemarker"
},
"email": {
"provider": "freemarker"
},
"scheduled": {
"interval": 900
},
"connectionsJpa": {
"default": {
"dataSource": "java:jboss/datasources/KeycloakDS",
"databaseSchema": "update"
}
}
}

View file

@ -1,2 +0,0 @@
Any provider implementation jars and libraries in this folder will be loaded by Keycloak. See the providers
section in the documentation for more details.

View file

@ -1,3 +0,0 @@
Themes to configure the look and feel of login pages and account management console. It's not recommended to
modify existing the built-in themes, instead you should create a new theme that extends a built-in theme. See the theme
section in the documentation for more details.

View file

@ -1,54 +0,0 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:j="urn:jboss:domain:3.0"
xmlns:ds="urn:jboss:domain:datasources:3.0"
xmlns:k="urn:jboss:domain:keycloak:1.1"
version="2.0"
exclude-result-prefixes="xalan j ds k">
<xsl:param name="config"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="//j:extensions">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<extension module="org.keycloak.keycloak-server-subsystem"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//ds:datasources">
<xsl:copy>
<xsl:apply-templates select="node()[name(.)='datasource']"/>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
<xsl:apply-templates select="node()[name(.)='drivers']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//j:profile">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<auth-server name="main-auth-server">
<enabled>true</enabled>
<web-context>auth</web-context>
</auth-server>
</subsystem>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

View file

@ -35,6 +35,7 @@
<!ENTITY UserFederation SYSTEM "modules/user-federation.xml">
<!ENTITY Kerberos SYSTEM "modules/kerberos.xml">
<!ENTITY ExportImport SYSTEM "modules/export-import.xml">
<!ENTITY AdminRecovery SYSTEM "modules/admin-recovery.xml">
<!ENTITY ServerCache SYSTEM "modules/cache.xml">
<!ENTITY SecurityVulnerabilities SYSTEM "modules/security-vulnerabilities.xml">
<!ENTITY Clustering SYSTEM "modules/clustering.xml">
@ -126,6 +127,7 @@ This one is short
&UserFederation;
&Kerberos;
&ExportImport;
&AdminRecovery;
&ServerCache;
&SAML;
&SecurityVulnerabilities;

View file

@ -79,6 +79,30 @@
<section>
<title>Version specific migration</title>
<section>
<title>Migrating to 1.3.0.Final</title>
<simplesect>
<title>Direct Grant API always enabled</title>
<para>
In the past Direct Grant API (or Resource Owner Password Credentials) was disabled by default and
there was an option on a realm to enable it. The Direct Grant API is now always enabled and the
option to enable/disable for a realm is removed.
</para>
</simplesect>
<simplesect>
<title>Database changed</title>
<para>
There are again few database changes. Remember to backup your database prior to upgrading.
</para>
</simplesect>
<simplesect>
<title>UserFederationProvider changed</title>
<para>
There are few minor changes in UserFederationProvider interface. You may need to sync your implementation when upgrade
to newer version and upgrade few methods, which has changed signature. Changes are really minor, but were needed to improve performance of federation.
</para>
</simplesect>
</section>
<section>
<title>Migrating from 1.2.0.Beta1 to 1.2.0.RC1</title>
<simplesect>

View file

@ -0,0 +1,15 @@
<chapter id="admin-recovery">
<title>Recovering the Master Admin User</title>
<para>
It is possible for the "admin" user in the master realm to become inoperable. This may be because it was
accidentally deleted, its role mappings were removed, or the password was simply forgotten.
</para>
<para>
To recover the master admin user, just start the server with the following system properties:
<programlisting><![CDATA[
bin/standalone.sh -Dkeycloak.recover-admin=true -Dkeycloak.temp-admin-password=temppassword
]]></programlisting>
Then you can log in to the master admin account with your temporary password. You will then be
prompted to immediately change this password.
</para>
</chapter>

View file

@ -3,8 +3,7 @@
<para>
Keycloak allows you to make direct REST invocations to obtain an access token.
(See <ulink url="http://tools.ietf.org/html/rfc6749#section-4.3">Resource Owner Password Credentials Grant</ulink>
from OAuth 2.0 spec). To use it, Direct Access Grants must be allowed by your realm. This is a configuration switch
in the admin console under Settings->General, specifically the "Direct Grant API" switch. You must also have
from OAuth 2.0 spec). To use it you must also have
registered a valid Client to use as the "client_id" for this grant request.
</para>
<warning>
@ -12,7 +11,9 @@
It is highly recommended that you do not use Direct Access Grants to write your own login pages for your application.
You will lose a lot of features that Keycloak has if you do this. Specifically all the account management, remember me,
lost password, account reset features of Keycloak. Instead, if you want to tailor the look and feel of Keycloak login
pages, you should create your own <link linkend="themes">theme</link>.
pages, you should create your own <link linkend="themes">theme</link>. There are also security implications
to using Direct Access Grants compared to the redirect based flows as you are exposing plain text passwords
to applications directly.
</para>
<para>
It is even highly recommended that you use the browser to log in for native mobile applications! Android

View file

@ -299,12 +299,24 @@
</entry>
<entry>
Allows you to force users to update their profile right after the authentication finishes and
before the account is actually created in Keycloak. When enabled, users will be presented with the
before the account is actually created in Keycloak. When "On", users will be always presented with the
<emphasis>update profile page</emphasis> asking for additional information in order to federate their identities.
If disabled, the account will be created with the minimal information obtained from the identity provider
When "On missing info", users will be presented with the <emphasis>update profile page</emphasis> only if some
mandatory information (email, first name, last name) is not provided by identity provider.
If "Off", the account will be created with the minimal information obtained from the identity provider
during the authentication process.
</entry>
</row>
<row>
<entry>
<literal>Trust email</literal>
</entry>
<entry>
Allows you to trust email address returned from the social provider. If enabled then email address returned by the provider
is marked as 'verified' in the Keycloak user profile. This means that email verification step is skipped even
if "Verify email" feature is enabled in realm settings.
</entry>
</row>
<row>
<entry>
<literal>GUI order</literal>

View file

@ -24,7 +24,8 @@
<section>
<title>LDAP and Active Directory Plugin</title>
<para>
Keycloak comes with a built-in LDAP/AD plugin. Currently it is set up only to import username, email, first and last name.
Keycloak comes with a built-in LDAP/AD plugin. By default, it is set up only to import username, email, first and last name, but you are free
to configure <link linkend='ldap_mappers'>mappers</link> and add more attributes or delete default ones.
It supports password validation via LDAP/AD protocols and different user metadata synchronization modes. To configure
a federated LDAP store go to the admin console. Click on the <literal>Users</literal> menu option to get you
to the user management page. Then click on the <literal>Federation</literal> submenu option. When
@ -41,7 +42,7 @@
<term>READONLY</term>
<listitem>
<para>
Username, email, first and last name will be unchangable. Keycloak will show an error
Username, email, first and last name and other mapped attributes will be unchangeable. Keycloak will show an error
anytime anybody tries to update these fields. Also, password updates will not be supported.
</para>
</listitem>
@ -50,7 +51,7 @@
<term>WRITABLE</term>
<listitem>
<para>
Username, email, first and last name, and passwords can all be updated and will
Username, email, first and last name, other mapped attributes and passwords can all be updated and will
be synchronized automatically with your LDAP store.
</para>
</listitem>
@ -158,6 +159,56 @@
</para>
<para>In admin console, you can trigger sync directly or you can enable periodic changed or full sync.</para>
</section>
<section id="ldap_mappers">
<title>LDAP/Federation mappers</title>
<para>
LDAP mappers are <literal>listeners</literal>, which are triggered by LDAP Federation provider at various points and provide
another extension point to LDAP integration. They are triggered during import LDAP user into Keycloak, registration Keycloak user back to LDAP or when querying LDAP user from Keycloak.
When you create LDAP Federation provider, Keycloak will automatically provide set of builtin <literal>mappers</literal> for this provider.
You are free to change this set and create new mapper or update/delete existing ones.
</para>
<para>
By default, we have those implementation of LDAP federation mapper:
<variablelist>
<varlistentry>
<term>User Attribute Mapper</term>
<listitem>
<para>
This allows to specify which LDAP attribute is mapped to which attribute of Keycloak User. So for example you can configure
that LDAP attribute <literal>mail</literal> is supposed to be mapped to the UserModel attribute <literal>email</literal> in Keycloak database.
For this mapper implementation, there is always one-to-one mapping (one LDAP attribute mapped to one Keycloak UserModel attribute)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>FullName Mapper</term>
<listitem>
<para>
This allows to specify that fullname of user, which is saved in some LDAP attribute (usualy <literal>cn</literal> ) will be mapped to
<literal>firstName</literal> and <literal>lastname</literal> attributes of UserModel. Having <literal>cn</literal> to contain full name of user
is common case for some LDAP deployments.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Role Mapper</term>
<listitem>
<para>
This allows to configure role mappings from LDAP into Keycloak role mappings. One Role mapper can be used to map LDAP roles
(usually groups from particular branch of LDAP tree) into roles corresponding to either realm roles or client roles of specified client.
It's not a problem to configure more Role mappers for same LDAP provider. So for example you can specify that role mappings from groups under
<literal>ou=main,dc=example,dc=org</literal> will be mapped to realm role mappings and role mappings from
groups under <literal>ou=finance,dc=example,dc=org</literal> will be mapped to client role mappings of client <literal>finance</literal> .
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>By default, there is set of User Attribute mappers to map basic UserModel attributes username, first name, lastname and email to corresponding LDAP attributes. You are free to extend this and provide
more attribute mappings (For example to street, postalCode etc), delete firstName/lastname mapper and put fullName mapper instead, add role mappers etc.
Admin console provides tooltips, which should help on how to configure corresponding mappers.
</para>
</section>
<section>
<title>Writing your own User Federation Provider</title>
<para>

View file

@ -24,5 +24,12 @@ public interface Details {
String NODE_HOST = "node_host";
String REASON = "reason";
String REVOKED_CLIENT = "revoked_client";
String CLIENT_SESSION_STATE = "client_session_state";
String CLIENT_SESSION_HOST = "client_session_host";
String CONSENT = "consent";
String CONSENT_VALUE_NO_CONSENT_REQUIRED = "no_consent_required"; // No consent is required by client
String CONSENT_VALUE_CONSENT_GRANTED = "consent_granted"; // Consent granted by user
String CONSENT_VALUE_PERSISTED_CONSENT = "persistent_consent"; // Persistent consent used (was already granted by user before)
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class EventListenerSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return false;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class EventStoreSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -76,13 +76,17 @@ public class MongoAdminEventQuery implements AdminEventQuery{
@Override
public AdminEventQuery fromTime(Date fromTime) {
query.put("time", BasicDBObjectBuilder.start("$gte", fromTime.getTime()).get());
BasicDBObject time = query.containsField("time") ? (BasicDBObject) query.get("time") : new BasicDBObject();
time.append("$gte", fromTime.getTime());
query.put("time", time);
return this;
}
@Override
public AdminEventQuery toTime(Date toTime) {
query.put("time", BasicDBObjectBuilder.start("$lte", toTime.getTime()).get());
BasicDBObject time = query.containsField("time") ? (BasicDBObject) query.get("time") : new BasicDBObject();
time.append("$lte", toTime.getTime());
query.put("time", time);
return this;
}

View file

@ -3,7 +3,6 @@
"enabled": true,
"sslRequired": "external",
"registrationAllowed": true,
"passwordCredentialGrantAllowed": 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" ],

View file

@ -6,7 +6,6 @@
"accessCodeLifespanUserAction": 300,
"ssoSessionIdleTimeout": 600,
"ssoSessionMaxLifespan": 36000,
"passwordCredentialGrantAllowed": true,
"sslRequired": "external",
"registrationAllowed": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",

View file

@ -2,7 +2,6 @@
"realm": "twitter-identity-provider-realm",
"enabled": true,
"sslRequired": "external",
"passwordCredentialGrantAllowed": 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",
"defaultRoles": [ "user" ],

View file

@ -33,7 +33,7 @@ Servlet User Principal <b><%=request.getUserPrincipal().getName()%>
try {
list = CustomerDatabaseClient.getCustomers(request);
} catch (CustomerDatabaseClient.Failure failure) {
out.println("There was a failure processing request. You either didn't configure Keycloak properly, or maybe" +
out.println("There was a failure processing request. You either didn't configure Keycloak properly, or maybe " +
"you just forgot to secure the database service?");
out.println("Status from database service invocation was: " + failure.getStatus());
return;

View file

@ -6,7 +6,6 @@
"accessCodeLifespanUserAction": 300,
"ssoSessionIdleTimeout": 600,
"ssoSessionMaxLifespan": 36000,
"passwordCredentialGrantAllowed": true,
"sslRequired": "external",
"registrationAllowed": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",

View file

@ -6,7 +6,6 @@
"accessCodeLifespanUserAction": 300,
"ssoSessionIdleTimeout": 600,
"ssoSessionMaxLifespan": 36000,
"passwordCredentialGrantAllowed": true,
"sslRequired": "external",
"registrationAllowed": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",

View file

@ -7,7 +7,6 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
"passwordCredentialGrantAllowed": 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" ],

View file

@ -7,7 +7,6 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
"passwordCredentialGrantAllowed": true,
"updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXQIBAAKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQABAoGADwFSvEOQuh0IjWRtKZjwjOo4BrmlbRDJ3rf6x2LoemTttSouXzGxx/H87fSZdxNNuU9HbBHoY4ko4POzmZEWhS0gV6UjM7VArc4YjID6Hh2tfU9vCbuuKZrRs7RjxL70b51WxycKc49PQ4JiR3g04punrpq2UzToPrm66zI+ICECQQD2Jauo6cXXoxHR0QychQf4dityZwFXUoR/8oI/YFiu9XwcWgSMwrFKUdWWNKYmrIRNqCBzrGyeiGdaAjsw41T3AkEAyIpn+XL7bek/uLno5/7ULauf2dFI6MEaHJixQJD7S6Tfo/CGuDK93H4K0GAdjgR0LA0tCnB09yyPCd5NmAYKpQJBAO7+BH4s/PsyScr+vs/6GpMTqXuap6KxbBUO0YfXdEPr9mVQwboqDxmp+0esNua1+n+sDlZBw/TpW+/42p/NGmECQF0sOQyjyH+TfGCmN7j6I7ioYZeA7h/9/9TDeK8n7SmDC8kOanlQUfgMs5eG4JRoK1WANaoA/8cLc9XA7EoynGUCQQDx/Gjg6qyWheVujxjKufH1XkqDNiQHClDRM1ntChCmGq/RmpVmce+mYeOYZ9eofv7UJUCBdamllRlB+056Ld2h",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQAB",

View file

@ -3,7 +3,6 @@
"realm": "saml-demo",
"enabled": true,
"sslRequired": "external",
"passwordCredentialGrantAllowed": 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" ],

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class ExportSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class ImportSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -1,8 +1,6 @@
package org.keycloak.federation.ldap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
@ -50,7 +48,14 @@ public class LDAPConfig {
}
public String getUsersDn() {
return config.get(LDAPConstants.USERS_DN);
String usersDn = config.get(LDAPConstants.USERS_DN);
if (usersDn == null) {
// Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later.
usersDn = config.get("userDnSuffix");
}
return usersDn;
}
public Collection<String> getUserObjectClasses() {
@ -103,31 +108,13 @@ public class LDAPConfig {
if (uuidAttrName == null) {
// Differences of unique attribute among various vendors
String vendor = getVendor();
if (vendor != null) {
switch (vendor) {
case LDAPConstants.VENDOR_RHDS:
uuidAttrName = "nsuniqueid";
break;
case LDAPConstants.VENDOR_TIVOLI:
uuidAttrName = "uniqueidentifier";
break;
case LDAPConstants.VENDOR_NOVELL_EDIRECTORY:
uuidAttrName = "guid";
break;
case LDAPConstants.VENDOR_ACTIVE_DIRECTORY:
uuidAttrName = LDAPConstants.OBJECT_GUID;
}
}
if (uuidAttrName == null) {
uuidAttrName = LDAPConstants.ENTRY_UUID;
}
uuidAttrName = LDAPConstants.getUuidAttributeName(vendor);
}
return uuidAttrName;
}
// TODO: Remove and use mapper instead
// TODO: Remove and use mapper instead?
public boolean isUserAccountControlsAfterPasswordUpdate() {
String userAccountCtrls = config.get(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE);
return userAccountCtrls==null ? false : Boolean.parseBoolean(userAccountCtrls);
@ -150,6 +137,12 @@ public class LDAPConfig {
String rdn = config.get(LDAPConstants.RDN_LDAP_ATTRIBUTE);
if (rdn == null) {
rdn = getUsernameLdapAttribute();
if (rdn.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
// Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later.
rdn = LDAPConstants.CN;
}
}
return rdn;
}

View file

@ -308,8 +308,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
@Override
public void preRemove(RealmModel realm, RoleModel role) {
// complete I don't think we have to do anything here
// TODO: requires implementation... Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper
// TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper?
}
public boolean validPassword(RealmModel realm, UserModel user, String password) {

View file

@ -41,7 +41,7 @@ import java.util.Set;
*/
public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory {
private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class);
public static final String PROVIDER_NAME = "ldap";
public static final String PROVIDER_NAME = LDAPConstants.LDAP_PROVIDER;
private LDAPIdentityStoreRegistry ldapStoreRegistry;
@ -79,7 +79,7 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
// Best effort to create appropriate mappers according to our LDAP config
@Override
protected void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) {
public void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) {
LDAPConfig ldapConfig = new LDAPConfig(newProviderModel.getConfig());
boolean activeDirectory = ldapConfig.isActiveDirectory();
@ -94,12 +94,42 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
// For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user
if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
// CN is typically used as RDN for Active Directory deployments
if (ldapConfig.getRdnLdapAttribute().equalsIgnoreCase(LDAPConstants.CN)) {
if (usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.CN)) {
// For AD deployments with "cn" as username, we will map "givenName" to first name
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME,
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
} else {
if (editMode == UserFederationProvider.EditMode.WRITABLE) {
// For AD deployments with "sAMAccountName" as username and writable, we need to map "cn" as username as well (this is needed so we can register new users from KC into LDAP) and we will map "givenName" to first name.
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME,
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username-cn", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
} else {
// For read-only LDAP, we map "cn" as full name
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
realm.addUserFederationMapper(mapperModel);
}
}
} else {
mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,

View file

@ -24,7 +24,7 @@ public class LDAPIdentityStoreRegistry {
// Ldap config might have changed for the realm. In this case, we must re-initialize
Map<String, String> config = model.getConfig();
if (context == null || !config.equals(context.config)) {
logLDAPConfig(model.getId(), config);
logLDAPConfig(model.getDisplayName(), config);
LDAPIdentityStore store = createLdapIdentityStore(config);
context = new LDAPIdentityStoreContext(config, store);
@ -34,10 +34,10 @@ public class LDAPIdentityStoreRegistry {
}
// Don't log LDAP password
private void logLDAPConfig(String fedProviderId, Map<String, String> ldapConfig) {
private void logLDAPConfig(String fedProviderDisplayName, Map<String, String> ldapConfig) {
Map<String, String> copy = new HashMap<String, String>(ldapConfig);
copy.remove(LDAPConstants.BIND_CREDENTIAL);
logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderId + ", LDAP Configuration: " + copy);
logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderDisplayName + ", LDAP Configuration: " + copy);
}
/**
@ -55,23 +55,6 @@ public class LDAPIdentityStoreRegistry {
checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
/*String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
if (ldapLoginNameMapping == null) {
ldapLoginNameMapping = activeDirectory ? LDAPConstants.CN : LDAPConstants.UID;
}
String ldapFirstNameMapping = activeDirectory ? "givenName" : LDAPConstants.CN;
String createTimestampMapping = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP;
String modifyTimestampMapping = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP;
String[] userObjectClasses = getUserObjectClasses(ldapConfig); */
/* if (activeDirectory && ldapLoginNameMapping.equals("sAMAccountName")) {
ldapUserMappingConfig.setBindingDnPropertyName("fullName");
ldapUserMappingConfig.addAttributeMapping("fullName", LDAPConstants.CN);
logger.infof("Using 'cn' attribute for DN of user and 'sAMAccountName' for username");
} */
return new LDAPIdentityStore(cfg);
}

View file

@ -45,16 +45,6 @@ public class LDAPUtils {
return ldapUser;
}
public static void removeAllUsers(LDAPFederationProvider ldapProvider, RealmModel realm) {
LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore();
LDAPIdentityQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm);
List<LDAPObject> allUsers = ldapQuery.getResultList();
for (LDAPObject ldapUser : allUsers) {
ldapStore.remove(ldapUser);
}
}
public static LDAPIdentityQuery createQueryForUserSearch(LDAPFederationProvider ldapProvider, RealmModel realm) {
LDAPIdentityQuery ldapQuery = new LDAPIdentityQuery(ldapProvider);
LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig();

View file

@ -1,7 +1,5 @@
package org.keycloak.federation.ldap.idm.query.internal;
import java.util.List;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;

View file

@ -71,11 +71,12 @@ public class LDAPIdentityStore implements IdentityStore {
public void add(LDAPObject ldapObject) {
// id will be assigned by the ldap server
if (ldapObject.getUuid() != null) {
throw new IllegalStateException("Can't add object with already assigned uuid");
throw new ModelException("Can't add object with already assigned uuid");
}
String entryDN = ldapObject.getDn().toString();
this.operationManager.createSubContext(entryDN, extractAttributes(ldapObject, true));
BasicAttributes ldapAttributes = extractAttributes(ldapObject, true);
this.operationManager.createSubContext(entryDN, ldapAttributes);
ldapObject.setUuid(getEntryIdentifier(ldapObject));
if (logger.isTraceEnabled()) {
@ -108,20 +109,20 @@ public class LDAPIdentityStore implements IdentityStore {
@Override
public List<LDAPObject> fetchQueryResults(LDAPIdentityQuery identityQuery) {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
throw new ModelException("LDAP Identity Store does not yet support sorted queries.");
}
List<LDAPObject> results = new ArrayList<>();
try {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
throw new ModelException("LDAP Identity Store does not yet support sorted queries.");
}
String baseDN = identityQuery.getSearchDn();
for (Condition condition : identityQuery.getConditions()) {
// Check if we are searching by ID
String uuidAttrName = getConfig().getUuidLDAPAttributeName();
if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) {
if (condition.getParameter() != null && condition.getParameter().getName().equalsIgnoreCase(uuidAttrName)) {
if (EqualCondition.class.isInstance(condition)) {
EqualCondition equalCondition = (EqualCondition) condition;
SearchResult search = this.operationManager
@ -147,7 +148,7 @@ public class LDAPIdentityStore implements IdentityStore {
}
for (SearchResult result : search) {
if (!result.getNameInNamespace().equals(baseDN)) {
if (!result.getNameInNamespace().equalsIgnoreCase(baseDN)) {
results.add(populateAttributedType(result, identityQuery.getReturningReadOnlyLdapAttributes()));
}
}
@ -180,8 +181,8 @@ public class LDAPIdentityStore implements IdentityStore {
public boolean validatePassword(LDAPObject user, String password) {
String userDN = user.getDn().toString();
if (logger.isDebugEnabled()) {
logger.debugf("Using DN [%s] for authentication of user", userDN);
if (logger.isTraceEnabled()) {
logger.tracef("Using DN [%s] for authentication of user", userDN);
}
if (operationManager.authenticate(userDN, password)) {
@ -258,7 +259,9 @@ public class LDAPIdentityStore implements IdentityStore {
filter.append(getObjectClassesFilter(identityQuery.getObjectClasses()));
filter.append(")");
logger.infof("Using filter for LDAP search: %s", filter);
if (logger.isTraceEnabled()) {
logger.tracef("Using filter for LDAP search: %s . Searching in DN: %s", filter, identityQuery.getSearchDn());
}
return filter;
}
@ -278,7 +281,7 @@ public class LDAPIdentityStore implements IdentityStore {
QueryParameter queryParameter = condition.getParameter();
if (!getConfig().getUuidLDAPAttributeName().equals(queryParameter.getName())) {
if (!getConfig().getUuidLDAPAttributeName().equalsIgnoreCase(queryParameter.getName())) {
String attributeName = queryParameter.getName();
if (attributeName != null) {
@ -377,10 +380,6 @@ public class LDAPIdentityStore implements IdentityStore {
ldapObject.setDn(dn);
ldapObject.setRdnAttributeName(dn.getFirstRdnAttrName());
if (logger.isTraceEnabled()) {
logger.tracef("Populating LDAP Object from DN [%s]", entryDN);
}
NamingEnumeration<? extends Attribute> ldapAttributes = attributes.getAll();
// Exact name of attributes might be different
@ -400,7 +399,7 @@ public class LDAPIdentityStore implements IdentityStore {
String ldapAttributeName = ldapAttribute.getID();
if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidLDAPAttributeName().toLowerCase())) {
if (ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName())) {
Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else {
@ -411,12 +410,9 @@ public class LDAPIdentityStore implements IdentityStore {
attrValues.add(attrVal);
}
if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) {
if (ldapAttributeName.equalsIgnoreCase(LDAPConstants.OBJECT_CLASS)) {
ldapObject.setObjectClasses(attrValues);
} else {
if (logger.isTraceEnabled()) {
logger.tracef("Populating ldap attribute [%s] with value [%s] for DN [%s].", ldapAttributeName, attrValues.toString(), entryDN);
}
if (attrValues.size() == 1) {
ldapObject.setAttribute(ldapAttributeName, attrValues.iterator().next());
} else {
@ -430,6 +426,9 @@ public class LDAPIdentityStore implements IdentityStore {
}
}
if (logger.isTraceEnabled()) {
logger.tracef("Found ldap object [%s] and populated with the attributes [%s]. Read-only attributes are [%s]", ldapObject.getDn().toString(), ldapObject.getAttributes(), ldapObject.getReadOnlyAttributeNames());
}
return ldapObject;
} catch (Exception e) {
@ -444,7 +443,7 @@ public class LDAPIdentityStore implements IdentityStore {
for (Map.Entry<String, Object> attrEntry : ldapObject.getAttributes().entrySet()) {
String attrName = attrEntry.getKey();
Object attrValue = attrEntry.getValue();
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) {
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) {
if (String.class.isInstance(attrValue)) {
if (attrValue.toString().trim().length() == 0) {
@ -461,7 +460,7 @@ public class LDAPIdentityStore implements IdentityStore {
} else if (attrValue == null || attrValue.toString().trim().length() == 0) {
entryAttributes.put(attrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE);
} else {
throw new IllegalArgumentException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue);
throw new ModelException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue);
}
}
}
@ -473,10 +472,10 @@ public class LDAPIdentityStore implements IdentityStore {
for (String objectClassValue : ldapObject.getObjectClasses()) {
objectClassAttribute.add(objectClassValue);
if (objectClassValue.equals(LDAPConstants.GROUP_OF_NAMES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_ENTRIES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) {
entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_ATTRIBUTE_VALUE);
if (objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_NAMES)
|| objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_ENTRIES)
|| objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) {
entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE);
}
}

View file

@ -128,8 +128,8 @@ public class LDAPOperationManager {
execute(new LdapOperation<SearchResult>() {
@Override
public SearchResult execute(LdapContext context) throws NamingException {
if (logger.isDebugEnabled()) {
logger.debugf("Removing entry with DN [%s]", entryDn);
if (logger.isTraceEnabled()) {
logger.tracef("Removing entry with DN [%s]", entryDn);
}
destroySubcontext(context, entryDn);
return null;
@ -357,8 +357,8 @@ public class LDAPOperationManager {
public void modifyAttributes(final String dn, final ModificationItem[] mods) {
try {
if (logger.isDebugEnabled()) {
logger.debugf("Modifying attributes for entry [%s]: [", dn);
if (logger.isTraceEnabled()) {
logger.tracef("Modifying attributes for entry [%s]: [", dn);
for (ModificationItem item : mods) {
Object values;
@ -369,10 +369,10 @@ public class LDAPOperationManager {
values = "No values";
}
logger.debugf(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values);
logger.tracef(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values);
}
logger.debugf("]");
logger.tracef("]");
}
execute(new LdapOperation<Void>() {
@ -389,18 +389,18 @@ public class LDAPOperationManager {
public void createSubContext(final String name, final Attributes attributes) {
try {
if (logger.isDebugEnabled()) {
logger.debugf("Creating entry [%s] with attributes: [", name);
if (logger.isTraceEnabled()) {
logger.tracef("Creating entry [%s] with attributes: [", name);
NamingEnumeration<? extends Attribute> all = attributes.getAll();
while (all.hasMore()) {
Attribute attribute = all.next();
logger.debugf(" %s = %s", attribute.getID(), attribute.get());
logger.tracef(" %s = %s", attribute.getID(), attribute.get());
}
logger.debugf("]");
logger.tracef("]");
}
execute(new LdapOperation<Void>() {
@ -513,7 +513,6 @@ public class LDAPOperationManager {
context = createLdapContext();
return operation.execute(context);
} catch (NamingException ne) {
logger.error("Could not create Ldap context or operation execution error.", ne);
throw ne;
} finally {
if (context != null) {

View file

@ -1,24 +1,19 @@
package org.keycloak.federation.ldap.mappers;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.internal.EqualCondition;
import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
/**
* Mapper useful for the LDAP deployments when some attribute (usually CN) is mapped to full name of user
@ -35,7 +30,7 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
String fullName = ldapUser.getAttributeAsString(ldapFullNameAttrName);
fullName = fullName.trim();
if (fullName != null) {
if (fullName != null && !fullName.trim().isEmpty()) {
int lastSpaceIndex = fullName.lastIndexOf(" ");
if (lastSpaceIndex == -1) {
user.setLastName(fullName);
@ -130,7 +125,7 @@ public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {
fullName = firstNameCondition.getValue() + " " + lastNameCondition.getValue();
} else if (firstNameCondition != null) {
fullName = (String) firstNameCondition.getValue();
} else if (firstNameCondition != null) {
} else if (lastNameCondition != null) {
fullName = (String) lastNameCondition.getValue();
} else {
return;

View file

@ -1,14 +1,11 @@
package org.keycloak.federation.ldap.mappers;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.naming.directory.SearchControls;
import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.idm.model.LDAPDn;
@ -82,8 +79,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm);
RoleModel role = roleContainer.getRole(roleName);
// TODO: debug
logger.infof("Granting role [%s] to user [%s] during import from LDAP", roleName, user.getUsername());
logger.debugf("Granting role [%s] to user [%s] during import from LDAP", roleName, user.getUsername());
user.grantRole(role);
}
}
@ -97,6 +93,8 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
// Sync roles from LDAP tree and create them in local Keycloak DB (if they don't exist here yet)
protected void syncRolesFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) {
if (!rolesSyncedModels.contains(mapperModel.getId())) {
logger.debugf("Syncing roles from LDAP into Keycloak DB. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName());
LDAPIdentityQuery ldapQuery = createRoleQuery(mapperModel, ldapProvider);
// Send query
@ -108,7 +106,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
String roleName = ldapRole.getAttributeAsString(rolesRdnAttr);
if (roleContainer.getRole(roleName) == null) {
// TODO: rather change to debug
logger.infof("Syncing role [%s] from LDAP to keycloak DB", roleName);
roleContainer.addRole(roleName);
}
@ -127,7 +124,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
String rolesDn = getRolesDn(mapperModel);
ldapQuery.setSearchDn(rolesDn);
Collection<String> roleObjectClasses = getRoleObjectClasses(mapperModel);
Collection<String> roleObjectClasses = getRoleObjectClasses(mapperModel, ldapProvider);
ldapQuery.addObjectClasses(roleObjectClasses);
String rolesRdnAttr = getRoleNameLdapAttribute(mapperModel);
@ -145,11 +142,11 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
} else {
String clientId = mapperModel.getConfig().get(CLIENT_ID);
if (clientId == null) {
throw new IllegalStateException("Using client roles mapping is requested, but parameter client.id not found!");
throw new ModelException("Using client roles mapping is requested, but parameter client.id not found!");
}
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
throw new IllegalStateException("Can't found requested client with clientId: " + clientId);
throw new ModelException("Can't found requested client with clientId: " + clientId);
}
return client;
}
@ -158,7 +155,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
protected String getRolesDn(UserFederationMapperModel mapperModel) {
String rolesDn = mapperModel.getConfig().get(ROLES_DN);
if (rolesDn == null) {
throw new IllegalStateException("Roles DN is null! Check your configuration");
throw new ModelException("Roles DN is null! Check your configuration");
}
return rolesDn;
}
@ -173,10 +170,11 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
return membershipAttrName!=null ? membershipAttrName : LDAPConstants.MEMBER;
}
protected Collection<String> getRoleObjectClasses(UserFederationMapperModel mapperModel) {
protected Collection<String> getRoleObjectClasses(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider) {
String objectClasses = mapperModel.getConfig().get(ROLE_OBJECT_CLASSES);
if (objectClasses == null) {
objectClasses = "groupOfNames";
// For Active directory, the default is 'group' . For other servers 'groupOfNames'
objectClasses = ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES;
}
String[] objClasses = objectClasses.split(",");
@ -192,26 +190,25 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
private Mode getMode(UserFederationMapperModel mapperModel) {
String modeString = mapperModel.getConfig().get(MODE);
if (modeString == null || modeString.trim().length() == 0) {
return Mode.LDAP_ONLY;
if (modeString == null || modeString.isEmpty()) {
throw new ModelException("Mode is missing! Check your configuration");
}
return Enum.valueOf(Mode.class, modeString.toUpperCase());
}
protected LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) {
public LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) {
LDAPObject ldapObject = new LDAPObject();
String roleNameAttribute = getRoleNameLdapAttribute(mapperModel);
ldapObject.setRdnAttributeName(roleNameAttribute);
ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel));
ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel, ldapProvider));
ldapObject.setAttribute(roleNameAttribute, roleName);
LDAPDn roleDn = LDAPDn.fromString(getRolesDn(mapperModel));
roleDn.addFirst(roleNameAttribute, roleName);
ldapObject.setDn(roleDn);
// TODO: debug
logger.infof("Creating role to [%s] to LDAP with DN [%s]", roleName, roleDn.toString());
logger.infof("Creating role [%s] to LDAP with DN [%s]", roleName, roleDn.toString());
ldapProvider.getLdapIdentityStore().add(ldapObject);
return ldapObject;
}
@ -233,9 +230,9 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
Set<String> memberships = getExistingMemberships(mapperModel, ldapRole);
memberships.remove(ldapUser.getDn().toString());
// Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers
if (memberships.size() == 0) {
memberships.add(LDAPConstants.EMPTY_ATTRIBUTE_VALUE);
// Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers. But on active directory! (Empty membership is not allowed here)
if (memberships.size() == 0 && !ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory()) {
memberships.add(LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE);
}
ldapRole.setAttribute(getMembershipLdapAttribute(mapperModel), memberships);
@ -280,23 +277,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
return ldapQuery.getResultList();
}
protected Set<RoleModel> getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) {
List<LDAPObject> ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser);
Set<RoleModel> roles = new HashSet<RoleModel>();
String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject role : ldapRoles) {
String roleName = role.getAttributeAsString(roleNameLdapAttr);
RoleModel modelRole = roleContainer.getRole(roleName);
if (modelRole == null) {
// Add role to local DB
modelRole = roleContainer.addRole(roleName);
}
roles.add(modelRole);
}
return roles;
}
@Override
public UserModel proxy(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel delegate, RealmModel realm) {
final Mode mode = getMode(mapperModel);
@ -323,6 +303,9 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
private final RealmModel realm;
private final Mode mode;
// Avoid loading role mappings from LDAP more times per-request
private Set<RoleModel> cachedLDAPRoleMappings;
public LDAPRoleMappingsUserDelegate(UserModel user, UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser,
RealmModel realm, Mode mode) {
super(user);
@ -387,6 +370,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
if (role.getContainer().equals(roleContainer)) {
// We need to create new role mappings in LDAP
cachedLDAPRoleMappings = null;
addRoleMappingInLDAP(mapperModel, role.getName(), ldapProvider, ldapUser);
} else {
super.grantRole(role);
@ -417,6 +401,30 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
return modelRoleMappings;
}
protected Set<RoleModel> getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) {
if (cachedLDAPRoleMappings != null) {
return new HashSet<>(cachedLDAPRoleMappings);
}
List<LDAPObject> ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser);
Set<RoleModel> roles = new HashSet<RoleModel>();
String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject role : ldapRoles) {
String roleName = role.getAttributeAsString(roleNameLdapAttr);
RoleModel modelRole = roleContainer.getRole(roleName);
if (modelRole == null) {
// Add role to local DB
modelRole = roleContainer.addRole(roleName);
}
roles.add(modelRole);
}
cachedLDAPRoleMappings = new HashSet<>(roles);
return roles;
}
@Override
public void deleteRoleMapping(RoleModel role) {
RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm);
@ -440,6 +448,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
throw new ModelException("Not possible to delete LDAP role mappings as mapper mode is READ_ONLY");
} else {
// Delete ldap role mappings
cachedLDAPRoleMappings = null;
deleteRoleMappingInLDAP(mapperModel, ldapProvider, ldapUser, ldapRole);
}
}

View file

@ -1,25 +1,33 @@
package org.keycloak.federation.ldap.mappers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.mappers.MapperConfigValidationException;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
private static final Logger logger = Logger.getLogger(RoleLDAPFederationMapperFactory.class);
public static final String PROVIDER_ID = "role-ldap-mapper";
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -40,8 +48,8 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
configProperties.add(membershipLDAPAttribute);
ProviderConfigProperty roleObjectClasses = createConfigProperty(RoleLDAPFederationMapper.ROLE_OBJECT_CLASSES, "Role Object Classes",
"Object classes of the role object divided by comma (if more values needed). In typical LDAP deployment it could be 'groupOfNames' or 'groupOfEntries' ",
ProviderConfigProperty.STRING_TYPE, LDAPConstants.GROUP_OF_NAMES);
"Object class (or classes) of the role object. It's divided by comma if more classes needed. In typical LDAP deployment it could be 'groupOfNames' . In Active Directory it's usually 'group' ",
ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(roleObjectClasses);
List<String> modes = new LinkedList<String>();
@ -90,9 +98,46 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
return PROVIDER_ID;
}
// Sync roles from LDAP to Keycloak DB during creation or update of mapperModel
@Override
public void postInit(KeycloakSessionFactory factory) {
factory.register(new ProviderEventListener() {
@Override
public void onEvent(ProviderEvent event) {
if (event instanceof RealmModel.UserFederationMapperEvent) {
RealmModel.UserFederationMapperEvent mapperEvent = (RealmModel.UserFederationMapperEvent)event;
UserFederationMapperModel mapperModel = mapperEvent.getFederationMapper();
RealmModel realm = mapperEvent.getRealm();
KeycloakSession session = mapperEvent.getSession();
if (mapperModel.getFederationMapperType().equals(PROVIDER_ID)) {
try {
String federationProviderId = mapperModel.getFederationProviderId();
UserFederationProviderModel providerModel = KeycloakModelUtils.findUserFederationProviderById(federationProviderId, realm);
if (providerModel == null) {
throw new IllegalStateException("Can't find federation provider with ID [" + federationProviderId + "] in realm " + realm.getName());
}
UserFederationProviderFactory ldapFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, providerModel.getProviderName());
LDAPFederationProvider ldapProvider = (LDAPFederationProvider) ldapFactory.getInstance(session, providerModel);
// Sync roles
new RoleLDAPFederationMapper().syncRolesFromLDAP(mapperModel, ldapProvider, realm);
} catch (Exception e) {
logger.warn("Exception during initial sync of roles from LDAP.", e);
}
}
}
}
});
}
@Override
public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
checkMandatoryConfigAttribute(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN", mapperModel);
checkMandatoryConfigAttribute(RoleLDAPFederationMapper.MODE, "Mode", mapperModel);
String realmMappings = mapperModel.getConfig().get(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING);
boolean useRealmMappings = Boolean.parseBoolean(realmMappings);

View file

@ -1,18 +1,13 @@
package org.keycloak.federation.ldap.mappers;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
@ -20,7 +15,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.utils.reflection.Property;
import org.keycloak.models.utils.reflection.PropertyCriteria;
import org.keycloak.models.utils.reflection.PropertyQueries;
import org.keycloak.provider.ProviderConfigProperty;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -55,7 +49,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE);
Object ldapAttrValue = ldapUser.getAttribute(ldapAttrName);
if (ldapAttrValue != null) {
if (ldapAttrValue != null && !ldapAttrValue.toString().trim().isEmpty()) {
Property<Object> userModelProperty = userModelProperties.get(userModelAttrName);
if (userModelProperty != null) {
@ -124,7 +118,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
}
protected void setLDAPAttribute(String modelAttrName, String value) {
if (modelAttrName.equals(userModelAttrName)) {
if (modelAttrName.equalsIgnoreCase(userModelAttrName)) {
if (logger.isTraceEnabled()) {
logger.tracef("Pushing user attribute to LDAP. Model attribute name: %s, LDAP attribute name: %s, Attribute value: %s", modelAttrName, ldapAttrName, value);
}
@ -157,7 +151,7 @@ public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMap
// Change conditions and use ldapAttribute instead of userModel
for (Condition condition : query.getConditions()) {
QueryParameter param = condition.getParameter();
if (param != null && param.getName().equals(userModelAttrName)) {
if (param != null && param.getName().equalsIgnoreCase(userModelAttrName)) {
param.setName(ldapAttrName);
}
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class AccountSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -38,7 +38,7 @@ public class AccountBean {
}
public String getUsername() {
return user.getUsername();
return profileFormData != null ? profileFormData.getFirst("username") : user.getUsername();
}
public String getEmail() {

View file

@ -46,4 +46,8 @@ public class RealmBean {
return realm.getSupportedLocales();
}
public boolean isEditUsernameAllowed() {
return realm.isEditUsernameAllowed();
}
}

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Spi;
public class ThemeSpi implements Spi {
@Override
public boolean isPrivate() {
public boolean isInternal() {
return true;
}

View file

@ -16,11 +16,11 @@
<div class="form-group ${messagesPerField.printIfExists('username','has-error')}">
<div class="col-sm-2 col-md-2">
<label for="username" class="control-label">${msg("username")}</label>
<label for="username" class="control-label">${msg("username")}</label> <#if realm.editUsernameAllowed><span class="required">*</span></#if>
</div>
<div class="col-sm-10 col-md-10">
<input type="text" class="form-control" id="username" name="username" disabled="disabled" value="${(account.username!'')?html}"/>
<input type="text" class="form-control" id="username" name="username" <#if !realm.editUsernameAllowed>disabled="disabled"</#if> value="${(account.username!'')?html}"/>
</div>
</div>

View file

@ -80,7 +80,7 @@ totpStep1=Installieren Sie <a href="https://fedorahosted.org/freeotp/" target="_
totpStep2=\u00D6ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein.
totpStep3=Geben Sie den One-time Code welcher die Applikation generiert hat ein und klicken Sie auf Speichern.
missingUsernameMessage=Bitte geben Sie einen Benutzernamen ein.
missingFirstNameMessage=Bitte geben Sie einen Vornamen ein.
missingEmailMessage=Bitte geben Sie eine E-Mail Adresse ein.
missingLastNameMessage=Bitte geben Sie einen Nachnamen ein.

View file

@ -96,6 +96,7 @@ totpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">Fr
totpStep2=Open the application and scan the barcode or enter the key.
totpStep3=Enter the one-time code provided by the application and click Save to finish the setup.
missingUsernameMessage=Please specify username.
missingFirstNameMessage=Please specify first name.
invalidEmailMessage=Invalid email address.
missingLastNameMessage=Please specify last name.

View file

@ -597,7 +597,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
if (instance && instance.alias) {
} else {
$scope.identityProvider.updateProfileFirstLogin = false;
$scope.identityProvider.updateProfileFirstLoginMode = "on";
}
};
@ -645,7 +645,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
} else {
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
$scope.identityProvider.updateProfileFirstLogin = false;
$scope.identityProvider.updateProfileFirstLoginMode = "off";
}
}
@ -663,7 +663,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
$scope.identityProvider.alias = providerFactory.id;
$scope.identityProvider.providerId = providerFactory.id;
$scope.identityProvider.enabled = true;
$scope.identityProvider.updateProfileFirstLogin = false;
$scope.identityProvider.updateProfileFirstLoginMode = "off";
$scope.identityProvider.authenticateByDefault = false;
$scope.newIdentityProvider = true;
}

View file

@ -198,6 +198,7 @@ module.controller('UserListCtrl', function($scope, realm, User) {
module.controller('UserDetailCtrl', function($scope, realm, user, User, UserFederationInstances, $location, Dialog, Notifications) {
$scope.realm = realm;
$scope.create = !user.id;
$scope.editUsername = $scope.create || $scope.realm.editUsernameAllowed;
if ($scope.create) {
$scope.user = { enabled: true, attributes: {} }

View file

@ -559,7 +559,7 @@ function roleControl($scope, realm, role, roles, clients,
$scope.realmMappings.push(role);
}
}
$scope.selectRealmRoles = [];
$scope.selectedRealmRoles = [];
});
};

View file

@ -17,7 +17,7 @@
<select id="available" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedRealmRoles"
ng-options="r for r in availableRealmRoles">
ng-options="r for r in availableRealmRoles | orderBy:'toString()'">
</select>
<button class="btn btn-default" type="submit" ng-click="addRealmDefaultRole()" tooltip="Assign role" tooltip-placement="right">
Add selected <i class="fa fa-angle-double-right"></i>
@ -29,7 +29,7 @@
<select id="assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedRealmDefRoles"
ng-options="r for r in realm.defaultRoles">
ng-options="r for r in realm.defaultRoles | orderBy:'toString()'">
</select>
<button class="btn btn-default" type="submit" ng-click="deleteRealmDefaultRole()" tooltip="Unassign role" tooltip-placement="left">
<i class="fa fa-angle-double-left"></i> Remove selected
@ -55,7 +55,7 @@
<select id="available-client" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedClientRoles"
ng-options="r for r in availableClientRoles">
ng-options="r for r in availableClientRoles | orderBy:'toString()'">
</select>
<button class="btn btn-default" type="submit" ng-click="addClientDefaultRole()" tooltip="Assign role" tooltip-placement="right">
Add selected <i class="fa fa-angle-double-right"></i>
@ -67,7 +67,7 @@
<select id="assigned-client" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedClientDefRoles"
ng-options="r for r in client.defaultRoles">
ng-options="r for r in client.defaultRoles | orderBy:'toString()'">
</select>
<button class="btn btn-default" type="submit" ng-click="rmClientDefaultRole()" tooltip="Unassign role" tooltip-placement="left">
<i class="fa fa-angle-double-left"></i> Remove selected

View file

@ -59,11 +59,24 @@
<kc-tooltip>Enable/disable new users can read any stored tokens. This assigns the broker.read-token role.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
<div class="col-md-6">
<input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
<option value="on">On</option>
<option value="missing">On missing info</option>
<option value="off">Off</option>
</select>
</div>
</div>
<kc-tooltip>Indicates if user must update his profile right after the first login.</kc-tooltip>
<kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="trustEmail">Trust email</label>
<div class="col-md-6">
<input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
</div>
<kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="guiOrder">GUI order</label>

View file

@ -60,11 +60,24 @@
<kc-tooltip>Enable/disable new users can read any stored tokens. This assigns the broker.read-token role.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
<div class="col-md-6">
<input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
<option value="on">On</option>
<option value="missing">On missing info</option>
<option value="off">Off</option>
</select>
</div>
</div>
<kc-tooltip>Indicates if user must update his profile right after the first login.</kc-tooltip>
<kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="trustEmail">Trust email</label>
<div class="col-md-6">
<input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
</div>
<kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="guiOrder">GUI order</label>

View file

@ -70,11 +70,24 @@
<kc-tooltip>Enable/disable this identity provider.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
<div class="col-md-6">
<input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
<label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
<option value="on">On</option>
<option value="missing">On missing info</option>
<option value="off">Off</option>
</select>
</div>
</div>
<kc-tooltip>Indicates if user must update his profile right after the first login.</kc-tooltip>
<kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="trustEmail">Trust email</label>
<div class="col-md-6">
<input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
</div>
<kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="authenticateByDefault">Authenticate By Default</label>

View file

@ -8,7 +8,7 @@
<caption class="hidden">Table of identity providers</caption>
<thead>
<tr>
<th colspan="3" class="kc-table-actions">
<th colspan="4" class="kc-table-actions">
<div class="dropdown pull-right">
<select class="form-control" ng-model="provider"
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
@ -21,6 +21,7 @@
<tr ng-show="configuredProviders.length > 0">
<th>Name</th>
<th>Provider</th>
<th>Enabled</th>
<th width="15%">GUI order</th>
</tr>
</thead>
@ -30,6 +31,7 @@
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a>
</td>
<td>{{identityProvider.providerId}}</td>
<td>{{identityProvider.enabled}}</td>
<td>{{identityProvider.config.guiOrder}}</td>
</tr>
</tbody>

View file

@ -19,6 +19,13 @@
</div>
<kc-tooltip>If enabled then username field is hidden from registration form and email is used as username for new user.</kc-tooltip>
</div>
<div class="form-group">
<label for="editUsernameAllowed" class="col-md-2 control-label">Edit username</label>
<div class="col-md-6">
<input ng-model="realm.editUsernameAllowed" name="editUsernameAllowed" id="editUsernameAllowed" onoffswitch />
</div>
<kc-tooltip>If enabled, the username field is editable, readonly otherwise.</kc-tooltip>
</div>
<div class="form-group">
<label for="resetPasswordAllowed" class="col-md-2 control-label">Forget password</label>
<div class="col-md-6">
@ -40,13 +47,6 @@
</div>
<kc-tooltip>Require the user to verify their email address the first time they login.</kc-tooltip>
</div>
<div class="form-group">
<label for="passwordCredentialGrantAllowed" class="col-md-2 control-label">Direct Grant API</label>
<div class="col-md-6">
<input ng-model="realm.passwordCredentialGrantAllowed" name="passwordCredentialGrantAllowed" id="passwordCredentialGrantAllowed" onoffswitch />
</div>
<kc-tooltip>Enable/disable REST API for login/token grant access</kc-tooltip>
</div>
<div class="form-group">
<label for="sslRequired" class="col-md-2 control-label">Require SSL</label>
<div class="col-md-2">

Some files were not shown because too many files have changed in this diff Show more