Merge pull request #574 from patriot1burke/master
federation registeration
This commit is contained in:
commit
2460eecd75
9 changed files with 92 additions and 19 deletions
|
@ -7,7 +7,6 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.picketlink.idm.IdentityManagementException;
|
||||
|
@ -28,12 +27,14 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LDAPFederationProvider implements UserFederationProvider {
|
||||
private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
|
||||
public static final String LDAP_ID = "LDAP_ID";
|
||||
public static final String SYNC_REGISTRATIONS = "syncRegistrations";
|
||||
|
||||
protected KeycloakSession session;
|
||||
protected UserFederationProviderModel model;
|
||||
|
@ -86,12 +87,13 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistrationSupported() {
|
||||
return true;
|
||||
public boolean synchronizeRegistrations() {
|
||||
return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel register(RealmModel realm, UserModel user) {
|
||||
if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server");
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
|
@ -100,6 +102,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
|||
picketlinkUser.setLastName(user.getLastName());
|
||||
picketlinkUser.setEmail(user.getEmail());
|
||||
identityManager.add(picketlinkUser);
|
||||
user.setAttribute(LDAP_ID, picketlinkUser.getId());
|
||||
return proxy(user);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
|
|
|
@ -434,7 +434,6 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
|
|||
});
|
||||
|
||||
|
||||
|
||||
module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog, realm, instance, UserFederationInstances, RealmLDAPConnectionTester) {
|
||||
console.log('LDAPCtrl');
|
||||
|
||||
|
@ -445,6 +444,9 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
|
|||
$scope.instance.providerName = "ldap";
|
||||
$scope.instance.config = {};
|
||||
$scope.instance.priority = 0;
|
||||
$scope.syncRegistrations = false;
|
||||
} else {
|
||||
$scope.syncRegistrations = instance.config.syncRegistrations && instance.config.syncRegistrations == "true";
|
||||
}
|
||||
|
||||
$scope.ldapVendors = [
|
||||
|
@ -464,6 +466,14 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
|
|||
|
||||
$scope.lastVendor = $scope.instance.config.vendor;
|
||||
|
||||
$scope.$watch('syncRegistrations', function() {
|
||||
if ($scope.syncRegistrations) {
|
||||
$scope.instance.config.syncRegistrations = "true";
|
||||
} else {
|
||||
$scope.instance.config.syncRegistrations = "false";
|
||||
}
|
||||
})
|
||||
|
||||
$scope.$watch('instance', function() {
|
||||
if (!angular.equals($scope.instance, instance)) {
|
||||
$scope.changed = true;
|
||||
|
@ -510,6 +520,7 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
|
|||
$scope.instance.providerName = "ldap";
|
||||
$scope.instance.config = {};
|
||||
$scope.instance.priority = 0;
|
||||
$scope.syncRegistrations = false;
|
||||
}
|
||||
$scope.changed = false;
|
||||
$scope.lastVendor = $scope.instance.config.vendor;
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
<input class="form-control" id="priority" type="text" ng-model="instance.priority">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group clearfix block">
|
||||
<label class="col-sm-2 control-label" for="syncRegistrations">Sync Registrations</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="syncRegistrations" name="syncRegistrations" id="syncRegistrations" onoffswitch />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-sm-2 control-label" for="vendor">Vendor</label>
|
||||
<div class="col-sm-4">
|
||||
|
|
|
@ -21,13 +21,7 @@ public class UserFederationManager implements UserProvider {
|
|||
@Override
|
||||
public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
|
||||
UserModel user = session.userStorage().addUser(realm, id, username, addDefaultRoles);
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = session.getProvider(UserFederationProvider.class, federation.getProviderName());
|
||||
if (fed.isRegistrationSupported()) {
|
||||
return fed.register(realm, user);
|
||||
}
|
||||
}
|
||||
return user;
|
||||
return registerWithFederation(realm, user);
|
||||
}
|
||||
|
||||
protected UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
|
||||
|
@ -39,9 +33,14 @@ public class UserFederationManager implements UserProvider {
|
|||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserModel user = session.userStorage().addUser(realm, username);
|
||||
return registerWithFederation(realm, user);
|
||||
}
|
||||
|
||||
protected UserModel registerWithFederation(RealmModel realm, UserModel user) {
|
||||
for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
|
||||
UserFederationProvider fed = getFederationProvider(federation);
|
||||
if (fed.isRegistrationSupported()) {
|
||||
if (fed.synchronizeRegistrations()) {
|
||||
user.setFederationLink(federation.getId());
|
||||
return fed.register(realm, user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SPI for plugging in federation storage.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -18,8 +19,25 @@ public interface UserFederationProvider extends Provider {
|
|||
public static final String FIRST_NAME = UserModel.EMAIL;
|
||||
public static final String LAST_NAME = UserModel.LAST_NAME;
|
||||
|
||||
/**
|
||||
* Gives the provider an option to proxy UserModels loaded from local storage.
|
||||
* This method is called whenever a UserModel is pulled from local storage.
|
||||
* For example, the LDAP provider proxies the UserModel and does on-demand synchronization with
|
||||
* LDAP whenever UserModel update methods are invoked. It also overrides UserModel.updateCredential for the
|
||||
* credential types it supports
|
||||
*
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
UserModel proxy(UserModel local);
|
||||
boolean isRegistrationSupported();
|
||||
|
||||
/**
|
||||
* Should user registrations be synchronized with this provider?
|
||||
* FYI, only one provider will be chosen (by priority) to have this synchronization
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean synchronizeRegistrations();
|
||||
UserModel register(RealmModel realm, UserModel user);
|
||||
boolean removeUser(RealmModel realm, UserModel user);
|
||||
|
||||
|
@ -54,8 +72,30 @@ public interface UserFederationProvider extends Provider {
|
|||
void preRemove(RealmModel realm);
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
|
||||
/**
|
||||
* Is the Keycloak UserModel still valid and/or existing in federated storage?
|
||||
*
|
||||
* @param local
|
||||
* @return
|
||||
*/
|
||||
boolean isValid(UserModel local);
|
||||
|
||||
/**
|
||||
* What UserCredentialModel types are supported by this provider. Keycloak will only call
|
||||
* validCredentials() with the credential types specified in this method.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Set<String> getSupportedCredentialTypes();
|
||||
|
||||
/**
|
||||
* Validate credentials for this user.
|
||||
*
|
||||
* @param realm
|
||||
* @param user
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
||||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||
void close();}
|
||||
|
|
|
@ -121,8 +121,8 @@ public class RealmAdminResource {
|
|||
CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
|
||||
rep.setRealmCacheEnabled(cacheRealmProvider.isEnabled());
|
||||
}
|
||||
if (session.users() instanceof CacheUserProvider) {
|
||||
CacheUserProvider cache = (CacheUserProvider)session.users();
|
||||
if (session.userStorage() instanceof CacheUserProvider) {
|
||||
CacheUserProvider cache = (CacheUserProvider)session.userStorage();
|
||||
rep.setUserCacheEnabled(cache.isEnabled());
|
||||
}
|
||||
return rep;
|
||||
|
@ -155,8 +155,8 @@ public class RealmAdminResource {
|
|||
CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
|
||||
cacheRealmProvider.setEnabled(rep.isRealmCacheEnabled());
|
||||
}
|
||||
if (rep.isUserCacheEnabled() != null && session.users() instanceof CacheUserProvider) {
|
||||
CacheUserProvider cache = (CacheUserProvider)session.users();
|
||||
if (rep.isUserCacheEnabled() != null && session.userStorage() instanceof CacheUserProvider) {
|
||||
CacheUserProvider cache = (CacheUserProvider)session.userStorage();
|
||||
cache.setEnabled(rep.isUserCacheEnabled());
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistrationSupported() {
|
||||
public boolean synchronizeRegistrations() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ import org.junit.rules.RuleChain;
|
|||
import org.junit.rules.TestRule;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.federation.ldap.LDAPFederationProvider;
|
||||
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.testutils.LDAPEmbeddedServer;
|
||||
import org.keycloak.testsuite.LDAPTestUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -45,6 +47,8 @@ public class FederationProvidersIntegrationTest {
|
|||
|
||||
private static Map<String,String> ldapConfig = null;
|
||||
|
||||
private static UserFederationProviderModel ldapModel = null;
|
||||
|
||||
private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
|
@ -61,10 +65,11 @@ public class FederationProvidersIntegrationTest {
|
|||
ldapConfig.put(LDAPConstants.USER_DN_SUFFIX, ldapServer.getUserDnSuffix());
|
||||
String vendor = ldapServer.getVendor();
|
||||
ldapConfig.put(LDAPConstants.VENDOR, vendor);
|
||||
ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true");
|
||||
|
||||
|
||||
|
||||
appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null);
|
||||
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null);
|
||||
|
||||
// Configure LDAP
|
||||
ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
|
||||
|
@ -187,5 +192,13 @@ public class FederationProvidersIntegrationTest {
|
|||
|
||||
registerPage.register("firstName", "lastName", "email2", "registerUserSuccess2", "password", "password");
|
||||
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
KeycloakSession session = keycloakRule.startSession();
|
||||
RealmModel appRealm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
|
||||
Assert.assertNotNull(user);
|
||||
Assert.assertNotNull(user.getFederationLink());
|
||||
Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
|
||||
keycloakRule.stopSession(session, false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue