keycloak-uncached

Details

diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index e6378ce..a49a989 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -53,8 +53,11 @@ public class LDAPFederationProvider implements UserFederationProvider {
         this.model = model;
         this.partitionManager = partitionManager;
         String editModeString = model.getConfig().get(EDIT_MODE);
-        if (editModeString == null) editMode = EditMode.READ_ONLY;
-        editMode = EditMode.valueOf(editModeString);
+        if (editModeString == null) {
+            editMode = EditMode.READ_ONLY;
+        } else {
+            editMode = EditMode.valueOf(editModeString);
+        }
     }
 
     private ModelException convertIDMException(IdentityManagementException ie) {
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index 5472bc7..44987e9 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -16,7 +16,11 @@ import org.picketlink.idm.IdentityManager;
 import org.picketlink.idm.PartitionManager;
 import org.picketlink.idm.model.IdentityType;
 import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.AttributeParameter;
+import org.picketlink.idm.query.Condition;
 import org.picketlink.idm.query.IdentityQuery;
+import org.picketlink.idm.query.IdentityQueryBuilder;
+import org.picketlink.idm.query.QueryParameter;
 
 import java.util.Collections;
 import java.util.Date;
@@ -84,13 +88,15 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
 
         // Sync newly created users
         IdentityManager identityManager = partitionMgr.createIdentityManager();
-        IdentityQuery<User> userQuery = identityManager.createIdentityQuery(User.class)
-                .setParameter(IdentityType.CREATED_AFTER, lastSync);
+        IdentityQueryBuilder queryBuilder = identityManager.getQueryBuilder();
+        Condition condition = queryBuilder.greaterThanOrEqualTo(IdentityType.CREATED_DATE, lastSync);
+        IdentityQuery<User> userQuery = queryBuilder.createIdentityQuery(User.class).where(condition);
         syncImpl(sessionFactory, userQuery, realmId, model);
 
         // Sync updated users
-        userQuery = identityManager.createIdentityQuery(User.class)
-                .setParameter(IdentityType.MODIFIED_AFTER, lastSync);
+        queryBuilder = identityManager.getQueryBuilder();
+        condition = queryBuilder.greaterThanOrEqualTo(LDAPUtils.MODIFY_DATE, lastSync);
+        userQuery = queryBuilder.createIdentityQuery(User.class).where(condition);
         syncImpl(sessionFactory, userQuery, realmId, model);
     }
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
index e01a531..db0e9b8 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
@@ -10,6 +10,8 @@ import org.picketlink.idm.credential.UsernamePasswordCredentials;
 import org.picketlink.idm.model.Attribute;
 import org.picketlink.idm.model.basic.BasicModel;
 import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.query.AttributeParameter;
+import org.picketlink.idm.query.QueryParameter;
 
 import java.util.List;
 
@@ -20,6 +22,8 @@ import java.util.List;
  */
 public class LDAPUtils {
 
+    public static QueryParameter MODIFY_DATE = new AttributeParameter("modifyDate");
+
     public static User addUser(PartitionManager partitionManager, String username, String firstName, String lastName, String email) {
         IdentityManager identityManager = getIdentityManager(partitionManager);
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index 8c6154a..9855de2 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -529,7 +529,7 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
     ];
 
     $scope.usernameLDAPAttributes = [
-        "uid", "cn", "sAMAccountName"
+        "uid", "cn", "sAMAccountName", "entryDN"
     ];
 
     $scope.realm = realm;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index d94f8ab..69c53e2 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -129,7 +129,7 @@
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="ldapBindCredential">Bind Credential <span class="required">*</span></label>
                     <div class="col-sm-4">
-                        <input class="form-control" id="ldapBindCredential" type="text" ng-model="instance.config.bindCredential" placeholder="LDAP Bind Credentials" required>
+                        <input class="form-control" id="ldapBindCredential" type="password" ng-model="instance.config.bindCredential" placeholder="LDAP Bind Credentials" required>
                     </div>
                     <span tooltip-placement="right" tooltip="Password of LDAP admin" class="fa fa-info-circle"></span>
                     <div class="col-sm-4" data-ng-show="access.manageRealm">
diff --git a/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java b/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
index 0c82906..bc5278c 100755
--- a/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
+++ b/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
@@ -37,7 +37,7 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti
     protected boolean validateCredential(IdentityContext context, CredentialStorage credentialStorage, UsernamePasswordCredentials credentials, LDAPIdentityStore ldapIdentityStore) {
         Account account = getAccount(context, credentials.getUsername());
         char[] password = credentials.getPassword().getValue();
-        String userDN = getDNOfUser(ldapIdentityStore, account);
+        String userDN = (String) account.getAttribute(LDAPIdentityStore.ENTRY_DN_ATTRIBUTE_NAME).getValue();
         if (CREDENTIAL_LOGGER.isDebugEnabled()) {
             CREDENTIAL_LOGGER.debugf("Using DN [%s] for authentication of user [%s]", userDN, credentials.getUsername());
         }
@@ -48,16 +48,4 @@ public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredenti
 
         return false;
     }
-
-    protected String getDNOfUser(LDAPIdentityStore ldapIdentityStore, Account user) {
-        LDAPMappingConfiguration userMappingConfig = ldapIdentityStore.getConfig().getMappingConfig(User.class);
-        SearchResult sr = ldapIdentityStore.getOperationManager().lookupById(userMappingConfig.getBaseDN(), user.getId(), userMappingConfig);
-
-        if (sr != null) {
-            return sr.getNameInNamespace();
-        } else {
-            // Fallback
-            return ldapIdentityStore.getBindingDN(user, true);
-        }
-    }
 }

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index 14ee353..0070920 100755
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
         <resteasy.version.latest>3.0.9.Final</resteasy.version.latest>
         <undertow.version>1.0.15.Final</undertow.version>
 <!--        <picketlink.version>2.7.0.CR1-20140924</picketlink.version>  -->
-        <picketlink.version>2.7.0.CR1</picketlink.version>
+        <picketlink.version>2.7.0.CR2</picketlink.version>
         <picketbox.ldap.version>1.0.2.Final</picketbox.ldap.version>
         <mongo.driver.version>2.11.3</mongo.driver.version>
         <jboss.logging.version>3.1.4.GA</jboss.logging.version>
@@ -252,7 +252,7 @@
             </dependency>
             <dependency>
                 <groupId>org.picketlink</groupId>
-                <artifactId>picketlink-wildlfy-common</artifactId>
+                <artifactId>picketlink-wildfly-common</artifactId>
                 <version>${picketlink.version}</version>
             </dependency>
             <dependency>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
index 0b37379..2bf82ae 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SALM2LoginResponseBuilder.java
@@ -38,7 +38,8 @@ public class SALM2LoginResponseBuilder extends SAML2BindingBuilder<SALM2LoginRes
     protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
 
     protected List<String> roles = new LinkedList<String>();
-    protected String userPrincipal;
+    protected String nameId;
+    protected String nameIdFormat;
     protected boolean multiValuedRoles;
     protected boolean disableAuthnStatement;
     protected String requestID;
@@ -88,8 +89,9 @@ public class SALM2LoginResponseBuilder extends SAML2BindingBuilder<SALM2LoginRes
         return this;
     }
 
-    public SALM2LoginResponseBuilder userPrincipal(String userPrincipal) {
-        this.userPrincipal = userPrincipal;
+    public SALM2LoginResponseBuilder nameIdentifier(String nameIdFormat, String nameId) {
+        this.nameIdFormat = nameIdFormat;
+        this.nameId = nameId;
         return this;
     }
 
@@ -129,8 +131,8 @@ public class SALM2LoginResponseBuilder extends SAML2BindingBuilder<SALM2LoginRes
         issuerHolder.setStatusCode(JBossSAMLURIConstants.STATUS_SUCCESS.get());
 
         IDPInfoHolder idp = new IDPInfoHolder();
-        idp.setNameIDFormatValue(userPrincipal);
-        idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+        idp.setNameIDFormatValue(nameId);
+        idp.setNameIDFormat(nameIdFormat);
 
         SPInfoHolder sp = new SPInfoHolder();
         sp.setResponseDestinationURI(destination);
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index e5d1571..1d0367a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -54,6 +54,7 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_ENCRYPT = "saml.encrypt";
     public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
     public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
+    public static final String SAML_DEFAULT_NAMEID_FORMAT = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
 
     protected KeycloakSession session;
 
@@ -117,6 +118,23 @@ public class SamlProtocol implements LoginProtocol {
         return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || "true".equals(client.getAttribute(SAML_FORCE_POST_BINDING));
     }
 
+    protected String getNameIdFormat(ClientSessionModel clientSession) {
+        String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT);
+        if(nameIdFormat == null) return SAML_DEFAULT_NAMEID_FORMAT;
+        return nameIdFormat;
+    }
+
+    protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
+        if(nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
+            return userSession.getUser().getEmail();
+        } else if(nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) {
+            return clientSession.getNote(ClientSessionCode.ACTION_KEY);
+        } else {
+            // TODO: Support for persistent NameID (pseudo-random identifier persisted in user object)
+            return userSession.getUser().getUsername();
+        }
+    }
+
     @Override
     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
         ClientSessionModel clientSession = accessCode.getClientSession();
@@ -125,6 +143,8 @@ public class SamlProtocol implements LoginProtocol {
         String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
         String redirectUri = clientSession.getRedirectUri();
         String responseIssuer = getResponseIssuer(realm);
+        String nameIdFormat = getNameIdFormat(clientSession);
+        String nameId = getNameId(nameIdFormat, clientSession, userSession);
 
         SALM2LoginResponseBuilder builder = new SALM2LoginResponseBuilder();
         builder.requestID(requestID)
@@ -132,8 +152,7 @@ public class SamlProtocol implements LoginProtocol {
                .destination(redirectUri)
                .responseIssuer(responseIssuer)
                .requestIssuer(clientSession.getClient().getClientId())
-               .userPrincipal(userSession.getUser().getUsername()) // todo userId instead?  There is no username claim it seems
-               .attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), userSession.getUser().getId())
+               .nameIdentifier(nameIdFormat, nameId)
                .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
         initClaims(builder, clientSession.getClient(), userSession.getUser());
         if (clientSession.getRoles() != null) {
@@ -148,12 +167,12 @@ public class SamlProtocol implements LoginProtocol {
         }
         if (requiresRealmSignature(client)) {
             builder.signatureAlgorithm(getSignatureAlgorithm(client))
-                   .signWith(realm.getPrivateKey(), realm.getPublicKey())
+                   .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                    .signDocument();
         }
         if (requiresAssertionSignature(client)) {
             builder.signatureAlgorithm(getSignatureAlgorithm(client))
-                    .signWith(realm.getPrivateKey(), realm.getPublicKey())
+                    .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                     .signAssertions();
         }
         if (!includeAuthnStatement(client)) {
@@ -218,9 +237,13 @@ public class SamlProtocol implements LoginProtocol {
             builder.attribute(X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(), user.getFirstName());
             builder.attribute(X500SAMLProfileConstants.SURNAME.getFriendlyName(), user.getLastName());
         }
+        if (ClaimMask.hasUsername(model.getAllowedClaimsMask())) {
+            builder.attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), user.getUsername());
+        }
     }
 
 
+
     @Override
     public Response consentDenied(ClientSessionModel clientSession) {
         return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
@@ -238,7 +261,7 @@ public class SamlProtocol implements LoginProtocol {
                                          .destination(client.getClientId());
         if (requiresRealmSignature(client)) {
             logoutBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
-                         .signWith(realm.getPrivateKey(), realm.getPublicKey())
+                         .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                          .signDocument();
         }
         /*
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 5bcbb87..8d542bf 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -24,10 +24,12 @@ import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.util.StreamUtil;
 import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
 import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
 import org.picketlink.identity.federation.saml.v2.SAML2Object;
 import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
 import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
+import org.picketlink.identity.federation.saml.v2.protocol.NameIDPolicyType;
 import org.picketlink.identity.federation.saml.v2.protocol.RequestAbstractType;
 import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
 
@@ -207,6 +209,21 @@ public class SamlService {
             clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
             clientSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
 
+            // Handle NameIDPolicy from SP
+            NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy();
+            if(nameIdPolicy != null) {
+                String nameIdFormat = nameIdPolicy.getFormat().toString();
+                // TODO: Handle AllowCreate too, relevant for persistent NameID.
+                if(isSupportedNameIdFormat(nameIdFormat)) {
+                    clientSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat);
+                } else {
+                    event.error(Errors.INVALID_TOKEN);
+                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unsupported NameIDFormat.");
+                }
+            } else {
+                clientSession.setNote(GeneralConstants.NAMEID_FORMAT, JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get());
+            }
+
             Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
             if (response != null) return response;
 
@@ -226,6 +243,15 @@ public class SamlService {
             return forms.createLogin();
         }
 
+        private boolean isSupportedNameIdFormat(String nameIdFormat) {
+            if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get()) ||
+                    nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get()) ||
+                    nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())) {
+                return true;
+            }
+            return false;
+        }
+
         protected abstract String getBindingType();
 
         protected Response logoutRequest(LogoutRequestType requestAbstractType, ClientModel client) {
diff --git a/saml/saml-protocol/src/main/resources/idp-metadata-template.xml b/saml/saml-protocol/src/main/resources/idp-metadata-template.xml
index 5468cfc..25455c0 100755
--- a/saml/saml-protocol/src/main/resources/idp-metadata-template.xml
+++ b/saml/saml-protocol/src/main/resources/idp-metadata-template.xml
@@ -5,8 +5,10 @@
 	<EntityDescriptor entityID="${idp.entityID}">
 		<IDPSSODescriptor WantAuthnRequestsSigned="true"
 			protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
-			<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient
-			</NameIDFormat>
+			<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+			<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
+			<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
+
 			<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
 				Location="${idp.sso.HTTP-POST}" />
 			<SingleSignOnService
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 6ca12ba..e3db10a 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -215,7 +215,7 @@
         </dependency>
         <dependency>
             <groupId>org.picketlink</groupId>
-            <artifactId>picketlink-wildlfy-common</artifactId>
+            <artifactId>picketlink-wildfly-common</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
index 8cd9793..1c631bb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
@@ -25,8 +25,9 @@ public class LDAPRule extends ExternalResource {
     protected void after() {
         try {
             embeddedServer.tearDown();
+            embeddedServer = null;
         } catch (Exception e) {
-            throw new RuntimeException("Error starting Embedded LDAP server.", e);
+            throw new RuntimeException("Error tearDown Embedded LDAP server.", e);
         }
     }