keycloak-aplcache

Merge pull request #1292 from mposolda/ldap2 Ldap enhancements

5/28/2015 7:40:11 AM

Changes

Details

diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
index 33ace78..75e77f3 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
@@ -36,11 +36,6 @@ public abstract class AbstractLDAPFederationMapperFactory implements UserFederat
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties() {
-        throw new IllegalStateException("Method not supported for this implementation");
-    }
-
-    @Override
     public void close() {
     }
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
index d0d6230..e26cd66 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
@@ -46,7 +46,7 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
index a5eadd9..b02e2d3 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
@@ -59,7 +59,10 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
                 "If true, then LDAP role mappings will be mapped to realm role mappings in Keycloak. Otherwise it will be mapped to client role mappings", ProviderConfigProperty.BOOLEAN_TYPE, "true");
         configProperties.add(useRealmRolesMappings);
 
-        // NOTE: ClientID will be computed dynamically from available clients
+        ProviderConfigProperty clientIdProperty = createConfigProperty(RoleLDAPFederationMapper.CLIENT_ID, "Client ID",
+                "Client ID of client to which LDAP role mappings will be mapped. Applicable just if 'Use Realm Roles Mapping' is false",
+                ProviderConfigProperty.CLIENT_LIST_TYPE, null);
+        configProperties.add(clientIdProperty);
     }
 
     @Override
@@ -78,18 +81,8 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
-        List<ProviderConfigProperty> props = new ArrayList<ProviderConfigProperty>(configProperties);
-
-        Map<String, ClientModel> clients = realm.getClientNameMap();
-        List<String> clientIds = new ArrayList<String>(clients.keySet());
-
-        ProviderConfigProperty clientIdProperty = createConfigProperty(RoleLDAPFederationMapper.CLIENT_ID, "Client ID",
-                "Client ID of client to which LDAP role mappings will be mapped. Applicable just if 'Use Realm Roles Mapping' is false",
-                ProviderConfigProperty.LIST_TYPE, clientIds);
-        props.add(clientIdProperty);
-
-        return props;
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return configProperties;
     }
 
     @Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
index c0b9d79..90dd21a 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
@@ -48,7 +48,7 @@ public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFedera
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 7a0ae1a..cc3092f 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -984,7 +984,10 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 mapper : function(UserFederationMapperLoader) {
                     return UserFederationMapperLoader();
-                }
+                },
+                clients : function(ClientListLoader) {
+                    return ClientListLoader();
+                },
             },
             controller : 'UserFederationMapperCtrl'
         })
@@ -1000,6 +1003,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 mapperTypes : function(UserFederationMapperTypesLoader) {
                     return UserFederationMapperTypesLoader();
                 },
+                clients : function(ClientListLoader) {
+                    return ClientListLoader();
+                }
             },
             controller : 'UserFederationMapperCreateCtrl'
         })
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index bc723f3..cee2689 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -754,10 +754,11 @@ module.controller('UserFederationMapperListCtrl', function($scope, $location, No
 
 });
 
-module.controller('UserFederationMapperCtrl', function($scope, realm,  provider, mapperTypes, mapper, UserFederationMapper, Notifications, Dialog, $location) {
+module.controller('UserFederationMapperCtrl', function($scope, realm,  provider, mapperTypes, mapper, clients, UserFederationMapper, Notifications, Dialog, $location) {
     console.log('UserFederationMapperCtrl');
     $scope.realm = realm;
     $scope.provider = provider;
+    $scope.clients = clients;
     $scope.create = false;
     $scope.mapper = angular.copy(mapper);
     $scope.changed = false;
@@ -780,10 +781,10 @@ module.controller('UserFederationMapperCtrl', function($scope, realm,  provider,
             $location.url("/realms/" + realm.realm + '/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + mapper.id);
             Notifications.success("Your changes have been saved.");
         }, function(error) {
-            if (error.status == 400) {
+            if (error.status == 400 && error.data.error_description) {
                 Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
             } else {
-                Notification.error('Unexpected error when creating mapper');
+                Notifications.error('Unexpected error when creating mapper');
             }
         });
     };
@@ -808,10 +809,11 @@ module.controller('UserFederationMapperCtrl', function($scope, realm,  provider,
 
 });
 
-module.controller('UserFederationMapperCreateCtrl', function($scope, realm, provider, mapperTypes, UserFederationMapper, Notifications, Dialog, $location) {
+module.controller('UserFederationMapperCreateCtrl', function($scope, realm, provider, mapperTypes, clients, UserFederationMapper, Notifications, Dialog, $location) {
     console.log('UserFederationMapperCreateCtrl');
     $scope.realm = realm;
     $scope.provider = provider;
+    $scope.clients = clients;
     $scope.create = true;
     $scope.mapper = { federationProviderDisplayName: provider.displayName, config: {}};
     $scope.mapperTypes = mapperTypes;
@@ -844,10 +846,10 @@ module.controller('UserFederationMapperCreateCtrl', function($scope, realm, prov
             $location.url('/realms/' + realm.realm +'/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + id);
             Notifications.success("Mapper has been created.");
         }, function(error) {
-            if (error.status == 400) {
+            if (error.status == 400 && error.data.error_description) {
                 Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
             } else {
-                Notification.error('Unexpected error when creating mapper');
+                Notifications.error('Unexpected error when creating mapper');
             }
         });
     };
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
index 0b9c144..f06d032 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
@@ -47,7 +47,7 @@
             <div data-ng-repeat="option in mapperType.properties" class="form-group">
                 <label class="col-md-2 control-label">{{option.label}}</label>
 
-                <div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
+                <div class="col-sm-4" data-ng-show="option.type == 'String'">
                     <input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]">
                 </div>
                 <div class="col-sm-4" data-ng-show="option.type == 'boolean'">
@@ -58,6 +58,11 @@
                         <option value="" selected> Select one... </option>
                     </select>
                 </div>
+                <div class="col-sm-4" data-ng-show="option.type == 'ClientList'">
+                    <select ng-model="mapper.config[ option.name ]" ng-options="client.clientId as client.clientId for client in clients">
+                        <option value="" selected> Select one... </option>
+                    </select>
+                </div>
                 <kc-tooltip>{{option.helpText}}</kc-tooltip>
             </div>
 
diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
index 309036c..a4c8776 100644
--- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
+++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
@@ -31,6 +31,4 @@ public interface UserFederationMapperFactory extends ProviderFactory<UserFederat
      */
     void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException;
 
-    // TODO: Remove this and add realm to the method on ConfiguredProvider?
-    List<ProviderConfigProperty> getConfigProperties(RealmModel realm);
 }
diff --git a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java
index 94e792d..8e5d567 100644
--- a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java
+++ b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java
@@ -71,6 +71,6 @@ public class LDAPConstants {
     public static final String CUSTOM_ATTRIBUTE_EXPIRY_DATE = "expiryDate";
     public static final String ENTRY_UUID = "entryUUID";
     public static final String OBJECT_GUID = "objectGUID";
-    public static final String CREATE_TIMESTAMP = "createTimeStamp";
-    public static final String MODIFY_TIMESTAMP = "modifyTimeStamp";
+    public static final String CREATE_TIMESTAMP = "createTimestamp";
+    public static final String MODIFY_TIMESTAMP = "modifyTimestamp";
 }
diff --git a/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java b/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
index e3a217d..fad9d6c 100755
--- a/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
+++ b/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
@@ -8,6 +8,7 @@ public class ProviderConfigProperty {
     public static final String BOOLEAN_TYPE="boolean";
     public static final String STRING_TYPE="String";
     public static final String LIST_TYPE="List";
+    public static final String CLIENT_LIST_TYPE="ClientList";
 
     protected String name;
     protected String label;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
index c899fdf..6fe3837 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
@@ -118,6 +118,9 @@ public class ClientsResource {
         if (clientModel == null) {
             throw new NotFoundException("Could not find client: " + name);
         }
+
+        session.getContext().setClient(clientModel);
+
         ClientResource clientResource = new ClientResource(realm, auth, clientModel, session, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(clientResource);
         return clientResource;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index e2899fe..820fcfb 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -438,8 +438,8 @@ public class RealmAdminResource {
     /**
      * Query admin events.  Returns all admin events, or will query based on URL query parameters listed here
      *
-     * @param client app or oauth client name
-     * @param operationTypes operation type
+     * @param authRealm
+     * @param authClient
      * @param authUser user id
      * @param authIpAddress
      * @param resourcePath
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index fbe401c..2d14b4b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -227,7 +227,8 @@ public class RealmsAdminResource {
         }
         
         AdminEventBuilder adminEvent = new AdminEventBuilder(realm, auth, session, clientConnection);
-        
+        session.getContext().setRealm(realm);
+
         RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(adminResource);
         //resourceContext.initResource(adminResource);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
index e5deb3d..b8216c4 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
@@ -171,7 +171,7 @@ public class UserFederationProviderResource {
                 rep.setCategory(mapperFactory.getDisplayCategory());
                 rep.setName(mapperFactory.getDisplayType());
                 rep.setHelpText(mapperFactory.getHelpText());
-                List<ProviderConfigProperty> configProperties = mapperFactory.getConfigProperties(realm);
+                List<ProviderConfigProperty> configProperties = mapperFactory.getConfigProperties();
                 for (ProviderConfigProperty prop : configProperties) {
                     ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
                     propRep.setName(prop.getName());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
index aa61ee6..104d627 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
@@ -13,6 +13,10 @@ import org.keycloak.federation.ldap.LDAPFederationProvider;
 import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
 import org.keycloak.federation.ldap.LDAPUtils;
 import org.keycloak.federation.ldap.idm.model.LDAPObject;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory;
+import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper;
+import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.LDAPConstants;
@@ -20,9 +24,11 @@ import org.keycloak.models.ModelReadOnlyException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.pages.AccountPasswordPage;
@@ -36,7 +42,9 @@ import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.openqa.selenium.WebDriver;
 
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -265,6 +273,55 @@ public class FederationProvidersIntegrationTest {
     }
 
     @Test
+    public void testFullNameMapper() {
+        KeycloakSession session = keycloakRule.startSession();
+        UserFederationMapperModel firstNameMapper = null;
+
+        try {
+            RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+
+            // assert that user "fullnameUser" is not in local DB
+            Assert.assertNull(session.users().getUserByUsername("fullname", appRealm));
+
+            // Add the user with some fullName into LDAP directly. Ensure that fullName is saved into "cn" attribute in LDAP (currently mapped to model firstName)
+            LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
+            FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "fullname", "James Dee", "Dee", "fullname@email.org", "4578");
+
+            // add fullname mapper to the provider and remove "firstNameMapper"
+            UserFederationMapperModel fullNameMapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", ldapModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
+                    FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
+                    UserAttributeLDAPFederationMapper.READ_ONLY, "false");
+            appRealm.addUserFederationMapper(fullNameMapperModel);
+
+            firstNameMapper = appRealm.getUserFederationMapperByName(ldapModel.getId(), "first name");
+            appRealm.removeUserFederationMapper(firstNameMapper);
+
+            // Assert user is successfully imported in Keycloak DB now with correct firstName and lastName
+            FederationTestUtils.assertUserImported(session.users(), appRealm, "fullname", "James", "Dee", "fullname@email.org", "4578");
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+
+        session = keycloakRule.startSession();
+        try {
+            RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+
+            // Remove "fullnameUser" to assert he is removed from LDAP. Revert mappers to previous state
+            UserModel fullnameUser = session.users().getUserByUsername("fullname", appRealm);
+            session.users().removeUser(appRealm, fullnameUser);
+
+            // Revert mappers
+            UserFederationMapperModel fullNameMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "full name");
+            appRealm.removeUserFederationMapper(fullNameMapperModel);
+
+            firstNameMapper.setId(null);
+            appRealm.addUserFederationMapper(firstNameMapper);
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+    }
+
+    @Test
     public void testReadonly() {
         KeycloakSession session = keycloakRule.startSession();
         try {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
index d3dd9ab..80bf539 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
@@ -91,9 +91,13 @@ class FederationTestUtils {
     }
 
     public static void addZipCodeLDAPMapper(RealmModel realm, UserFederationProviderModel providerModel) {
-        UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
-                UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "postal_code",
-                UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.POSTAL_CODE,
+        addUserAttributeMapper(realm, providerModel, "zipCodeMapper", "postal_code", LDAPConstants.POSTAL_CODE); 
+    }
+
+    public static void addUserAttributeMapper(RealmModel realm, UserFederationProviderModel providerModel, String mapperName, String userModelAttributeName, String ldapAttributeName) {
+        UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel(mapperName, providerModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
+                UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, userModelAttributeName,
+                UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, ldapAttributeName,
                 UserAttributeLDAPFederationMapper.READ_ONLY, "false");
         realm.addUserFederationMapper(mapperModel);
     }