keycloak-aplcache

Changes

Details

diff --git a/broker/core/pom.xml b/broker/core/pom.xml
index 853d988..011055f 100755
--- a/broker/core/pom.xml
+++ b/broker/core/pom.xml
@@ -26,6 +26,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-events-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-jaxrs</artifactId>
             <exclusions>
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
index 1d9b5d1..a0f8309 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
@@ -17,6 +17,7 @@
  */
 package org.keycloak.broker.provider;
 
+import org.keycloak.events.EventBuilder;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
@@ -50,7 +51,7 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> 
     }
 
     @Override
-    public Object callback(RealmModel realm, AuthenticationCallback callback) {
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
         return null;
     }
 
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
index 6b5f466..ebc2ccd 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -17,6 +17,7 @@
  */
 package org.keycloak.broker.provider;
 
+import org.keycloak.events.EventBuilder;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.RealmModel;
@@ -51,7 +52,7 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
      *
      * @return
      */
-    Object callback(RealmModel realm, AuthenticationCallback callback);
+    Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event);
 
     /**
      * <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index 71b7450..78c890b 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -19,7 +19,7 @@ package org.keycloak.broker.oidc;
 
 import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.map.ObjectMapper;
-import org.jboss.resteasy.logging.Logger;
+import org.jboss.logging.Logger;
 import org.keycloak.ClientConnection;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.broker.oidc.util.SimpleHttp;
@@ -34,7 +34,6 @@ import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.representations.AccessTokenResponse;
-import org.keycloak.services.managers.EventsManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.util.JsonSerialization;
@@ -85,8 +84,8 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
     }
 
     @Override
-    public Object callback(RealmModel realm, AuthenticationCallback callback) {
-        return new Endpoint(callback, realm);
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
+        return new Endpoint(callback, realm, event);
     }
 
     @Override
@@ -171,6 +170,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
     protected class Endpoint {
         protected AuthenticationCallback callback;
         protected RealmModel realm;
+        protected EventBuilder event;
 
         @Context
         protected KeycloakSession session;
@@ -184,17 +184,16 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
         @Context
         protected UriInfo uriInfo;
 
-        public Endpoint(AuthenticationCallback callback, RealmModel realm) {
+        public Endpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
             this.callback = callback;
             this.realm = realm;
+            this.event = event;
         }
 
         @GET
         public Response authResponse(@QueryParam(AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_STATE) String state,
                                      @QueryParam(AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_CODE) String authorizationCode,
                                      @QueryParam(OAuth2Constants.ERROR) String error) {
-
-            EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder();
             if (error != null) {
                 //logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
                 event.event(EventType.LOGIN);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 9800010..68b6929 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -78,13 +78,13 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
     }
 
     @Override
-    public Object callback(RealmModel realm, AuthenticationCallback callback) {
-        return new OIDCEndpoint(callback, realm);
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
+        return new OIDCEndpoint(callback, realm, event);
     }
 
     protected class OIDCEndpoint extends Endpoint {
-        public OIDCEndpoint(AuthenticationCallback callback, RealmModel realm) {
-            super(callback, realm);
+        public OIDCEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
+            super(callback, realm, event);
         }
 
 
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index a96ed5e..60acd10 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -20,6 +20,7 @@ package org.keycloak.broker.saml;
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.events.EventBuilder;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
@@ -45,7 +46,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
     }
 
     @Override
-    public Object callback(RealmModel realm, AuthenticationCallback callback) {
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
         return new SAMLEndpoint(realm, getConfig(), callback);
     }
 
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index b15755c..e66ad65 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -140,7 +140,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                             }
 
                             if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
-                                updater.update(connection);
+                                updater.update(session, connection);
                             } else {
                                 logger.debug("Database is up to date");
                             }
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
index c5dc31f..f3355e0 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.connections.jpa.updater;
 
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.provider.Provider;
 
 import java.sql.Connection;
@@ -15,7 +16,7 @@ public interface JpaUpdaterProvider extends Provider {
 
     public String getCurrentVersionSql();
 
-    public void update(Connection connection);
+    public void update(KeycloakSession session, Connection connection);
 
     public void validate(Connection connection);
 
diff --git a/connections/jpa-liquibase/pom.xml b/connections/jpa-liquibase/pom.xml
index d877dea..d541d2a 100755
--- a/connections/jpa-liquibase/pom.xml
+++ b/connections/jpa-liquibase/pom.xml
@@ -20,6 +20,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.liquibase</groupId>
             <artifactId>liquibase-core</artifactId>
             <exclusions>
@@ -33,6 +38,7 @@
             <groupId>org.jboss.logging</groupId>
             <artifactId>jboss-logging</artifactId>
         </dependency>
+
     </dependencies>
 
     <build>
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java
new file mode 100644
index 0000000..40d71cb
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java
@@ -0,0 +1,97 @@
+package org.keycloak.connections.jpa.updater.liquibase.custom;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+
+import liquibase.change.custom.CustomSqlChange;
+import liquibase.database.Database;
+import liquibase.database.jvm.JdbcConnection;
+import liquibase.exception.CustomChangeException;
+import liquibase.exception.SetupException;
+import liquibase.exception.ValidationErrors;
+import liquibase.resource.ResourceAccessor;
+import liquibase.snapshot.SnapshotGeneratorFactory;
+import liquibase.statement.SqlStatement;
+import liquibase.structure.core.Table;
+import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class CustomKeycloakTask implements CustomSqlChange {
+
+    protected KeycloakSession kcSession;
+
+    protected Database database;
+    protected JdbcConnection jdbcConnection;
+    protected Connection connection;
+    protected StringBuilder confirmationMessage = new StringBuilder();
+    protected List<SqlStatement> statements = new ArrayList<SqlStatement>();
+
+    @Override
+    public ValidationErrors validate(Database database) {
+        return null;
+    }
+
+    @Override
+    public void setFileOpener(ResourceAccessor resourceAccessor) {
+
+    }
+
+    @Override
+    public String getConfirmationMessage() {
+        return confirmationMessage.toString();
+    }
+
+    @Override
+    public void setUp() throws SetupException {
+        this.kcSession = ThreadLocalSessionContext.getCurrentSession();
+        if (this.kcSession == null) {
+            throw new SetupException("No KeycloakSession provided in ThreadLocal");
+        }
+    }
+
+    @Override
+    public SqlStatement[] generateStatements(Database database) throws CustomChangeException {
+        this.database = database;
+        jdbcConnection = (JdbcConnection) database.getConnection();
+        connection = jdbcConnection.getWrappedConnection();
+
+        if (isApplicable()) {
+            confirmationMessage.append(getTaskId() + ": ");
+            generateStatementsImpl();
+        } else {
+            confirmationMessage.append(getTaskId() + ": no update applicable for this task");
+        }
+
+        return statements.toArray(new SqlStatement[statements.size()]);
+    }
+
+    protected boolean isApplicable() throws CustomChangeException {
+        try {
+            String correctedTableName = database.correctObjectName("REALM", Table.class);
+            if (SnapshotGeneratorFactory.getInstance().has(new Table().setName(correctedTableName), database)) {
+                ResultSet resultSet = connection.createStatement().executeQuery("SELECT ID FROM REALM");
+                try {
+                    return (resultSet.next());
+                } finally {
+                    resultSet.close();
+                }
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            throw new CustomChangeException("Failed to check database availability", e);
+        }
+    }
+
+    /**
+     * It's supposed to fill SQL statements to the "statements" variable and fill "confirmationMessage"
+     */
+    protected abstract void generateStatementsImpl() throws CustomChangeException;
+
+    protected abstract String getTaskId();
+}
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java
new file mode 100644
index 0000000..b28dce5
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java
@@ -0,0 +1,337 @@
+package org.keycloak.connections.jpa.updater.liquibase.custom;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import liquibase.exception.CustomChangeException;
+import liquibase.exception.DatabaseException;
+import liquibase.statement.core.InsertStatement;
+import liquibase.statement.core.UpdateStatement;
+import liquibase.structure.core.Table;
+import org.keycloak.Config;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClaimMask;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.util.MigrationUtils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask {
+
+    private String realmTableName;
+
+    @Override
+    protected String getTaskId() {
+        return "Update 1.2.0.Beta1";
+    }
+
+    @Override
+    protected void generateStatementsImpl() throws CustomChangeException {
+        realmTableName = database.correctObjectName("REALM", Table.class);
+
+        try {
+            convertSocialToIdFedRealms();
+            convertSocialToIdFedUsers();
+            addAccessCodeLoginTimeout();
+            addNewAdminRoles();
+            addDefaultProtocolMappers();
+        } catch (Exception e) {
+            throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+        }
+    }
+
+
+    protected void convertSocialToIdFedRealms() throws SQLException, DatabaseException {
+        String identityProviderTableName = database.correctObjectName("IDENTITY_PROVIDER", Table.class);
+        String idpConfigTableName = database.correctObjectName("IDENTITY_PROVIDER_CONFIG", Table.class);
+
+        PreparedStatement statement = jdbcConnection.prepareStatement("select RSC.NAME, VALUE, REALM_ID, UPDATE_PROFILE_ON_SOC_LOGIN from REALM_SOCIAL_CONFIG RSC,REALM where RSC.REALM_ID = REALM.ID ORDER BY RSC.REALM_ID, RSC.NAME");
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+                boolean providerInProgress = false;
+                String socialProviderId = null;
+                String clientId = null;
+                String clientSecret;
+                String realmId = null;
+                boolean updateProfileOnSocialLogin = false;
+                boolean first = true;
+
+                while (resultSet.next()) {
+                    if (first) {
+                        confirmationMessage.append("Migrating social to identity providers: ");
+                        first = false;
+                    }
+
+                    if (!providerInProgress) {
+                        String key = resultSet.getString("NAME");
+                        int keyIndex = key.indexOf(".key");
+                        if (keyIndex == -1) {
+                            throw new IllegalStateException("Can't parse the provider from column: " + key);
+                        }
+
+                        socialProviderId = key.substring(0, keyIndex);
+                        clientId = resultSet.getString("VALUE");
+                        realmId = resultSet.getString("REALM_ID");
+                        updateProfileOnSocialLogin = resultSet.getBoolean("UPDATE_PROFILE_ON_SOC_LOGIN");
+                        providerInProgress = true;
+                    } else {
+                        clientSecret = resultSet.getString("VALUE");
+
+                        String internalId = KeycloakModelUtils.generateId();
+                        InsertStatement idpInsert = new InsertStatement(null, null, identityProviderTableName)
+                                .addColumnValue("INTERNAL_ID", internalId)
+                                .addColumnValue("ENABLED", true)
+                                .addColumnValue("PROVIDER_ALIAS", socialProviderId)
+                                .addColumnValue("PROVIDER_ID", socialProviderId)
+                                .addColumnValue("UPDATE_PROFILE_FIRST_LOGIN", updateProfileOnSocialLogin)
+                                .addColumnValue("STORE_TOKEN", false)
+                                .addColumnValue("AUTHENTICATE_BY_DEFAULT", false)
+                                .addColumnValue("REALM_ID", realmId);
+                        InsertStatement clientIdInsert = new InsertStatement(null, null, idpConfigTableName)
+                                .addColumnValue("IDENTITY_PROVIDER_ID", internalId)
+                                .addColumnValue("NAME", "clientId")
+                                .addColumnValue("VALUE", clientId);
+                        InsertStatement clientSecretInsert = new InsertStatement(null, null, idpConfigTableName)
+                                .addColumnValue("IDENTITY_PROVIDER_ID", internalId)
+                                .addColumnValue("NAME", "clientSecret")
+                                .addColumnValue("VALUE", clientSecret);
+
+                        statements.add(idpInsert);
+                        statements.add(clientIdInsert);
+                        statements.add(clientSecretInsert);
+                        confirmationMessage.append(socialProviderId + " in realm " + realmId + ", ");
+
+                        providerInProgress = false;
+                    }
+                }
+
+                // It means that some provider where processed
+                if (!first) {
+                    confirmationMessage.append(". ");
+                }
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+
+    protected void convertSocialToIdFedUsers() throws SQLException, DatabaseException {
+        String federatedIdentityTableName = database.correctObjectName("FEDERATED_IDENTITY", Table.class);
+        PreparedStatement statement = jdbcConnection.prepareStatement("select REALM_ID, USER_ID, SOCIAL_PROVIDER, SOCIAL_USER_ID, SOCIAL_USERNAME from USER_SOCIAL_LINK");
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+                int count = 0;
+                while (resultSet.next()) {
+                    InsertStatement insert = new InsertStatement(null, null, federatedIdentityTableName)
+                            .addColumnValue("REALM_ID", resultSet.getString("REALM_ID"))
+                            .addColumnValue("USER_ID", resultSet.getString("USER_ID"))
+                            .addColumnValue("IDENTITY_PROVIDER", resultSet.getString("SOCIAL_PROVIDER"))
+                            .addColumnValue("FEDERATED_USER_ID", resultSet.getString("SOCIAL_USER_ID"))
+                            .addColumnValue("FEDERATED_USERNAME", resultSet.getString("SOCIAL_USERNAME"));
+                    count++;
+                    statements.add(insert);
+                }
+
+                confirmationMessage.append("Updating " + count + " social links to federated identities. ");
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+
+    protected void addAccessCodeLoginTimeout() {
+        UpdateStatement statement = new UpdateStatement(null, null, realmTableName)
+                .addNewColumnValue("LOGIN_LIFESPAN", 1800)
+                .setWhereClause("LOGIN_LIFESPAN IS NULL");
+        statements.add(statement);
+
+        confirmationMessage.append("Updated LOGIN_LIFESPAN of all realms to 1800 seconds. ");
+    }
+
+    private void addNewAdminRoles() throws SQLException, DatabaseException{
+        addNewMasterAdminRoles();
+        addNewRealmAdminRoles();
+
+        confirmationMessage.append("Adding new admin roles. ");
+    }
+
+    protected void addNewMasterAdminRoles() throws SQLException, DatabaseException {
+        // Retrieve ID of admin role of master realm
+        String adminRoleId = getAdminRoleId();
+        String masterRealmId = Config.getAdminRealm();
+
+        PreparedStatement statement = jdbcConnection.prepareStatement("select NAME from REALM");
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+                while (resultSet.next()) {
+                    String realmName = resultSet.getString("NAME");
+                    String masterAdminAppName = realmName + "-realm";
+
+                    PreparedStatement statement2 = jdbcConnection.prepareStatement("select ID from CLIENT where REALM_ID = ? AND NAME = ?");
+                    statement2.setString(1, masterRealmId);
+                    statement2.setString(2, masterAdminAppName);
+
+                    try {
+                        ResultSet resultSet2 = statement2.executeQuery();
+                        try {
+                            if (resultSet2.next()) {
+                                String masterAdminAppId = resultSet2.getString("ID");
+
+                                addAdminRole(AdminRoles.VIEW_IDENTITY_PROVIDERS, masterRealmId, masterAdminAppId, adminRoleId);
+                                addAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS, masterRealmId, masterAdminAppId, adminRoleId);
+                            } else {
+                                throw new IllegalStateException("Couldn't find ID of '" + masterAdminAppName + "' application in 'master' realm. ");
+                            }
+                        } finally {
+                            resultSet2.close();
+                        }
+                    } finally {
+                        statement2.close();
+                    }
+                }
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+
+    private String getAdminRoleId() throws SQLException, DatabaseException {
+        PreparedStatement statement = jdbcConnection.prepareStatement("select ID from KEYCLOAK_ROLE where NAME = ? AND REALM = ?");
+        statement.setString(1, AdminRoles.ADMIN);
+        statement.setString(2, Config.getAdminRealm());
+
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+                if (resultSet.next()) {
+                    return resultSet.getString("ID");
+                } else {
+                    throw new IllegalStateException("Couldn't find ID of 'admin' role in 'master' realm");
+                }
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+
+
+    protected void addNewRealmAdminRoles() throws SQLException, DatabaseException {
+        PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.ID REALM_ADMIN_APP_ID, CLIENT.REALM_ID REALM_ID, KEYCLOAK_ROLE.ID ADMIN_ROLE_ID from CLIENT,KEYCLOAK_ROLE where KEYCLOAK_ROLE.APPLICATION = CLIENT.ID AND CLIENT.NAME = 'realm-management' AND KEYCLOAK_ROLE.NAME = ?");
+        statement.setString(1, AdminRoles.REALM_ADMIN);
+
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+
+                while (resultSet.next()) {
+                    String realmAdminAppId = resultSet.getString("REALM_ADMIN_APP_ID");
+                    String realmId = resultSet.getString("REALM_ID");
+                    String adminRoleId = resultSet.getString("ADMIN_ROLE_ID");
+
+                    addAdminRole(AdminRoles.VIEW_IDENTITY_PROVIDERS, realmId, realmAdminAppId, adminRoleId);
+                    addAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS, realmId, realmAdminAppId, adminRoleId);
+                }
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+
+    private void addAdminRole(String roleName, String realmId, String applicationId, String realmAdminAppRoleId) {
+        String roleTableName = database.correctObjectName("KEYCLOAK_ROLE", Table.class);
+        String compositeRoleTableName = database.correctObjectName("COMPOSITE_ROLE", Table.class);
+        String newRoleId = KeycloakModelUtils.generateId();
+
+        InsertStatement insertRole = new InsertStatement(null, null, roleTableName)
+                .addColumnValue("ID", newRoleId)
+                .addColumnValue("APP_REALM_CONSTRAINT", applicationId)
+                .addColumnValue("APPLICATION_ROLE", true)
+                .addColumnValue("NAME", roleName)
+                .addColumnValue("REALM_ID", realmId)
+                .addColumnValue("APPLICATION", applicationId);
+
+        // Add newly created role to the composite roles of 'realm-admin' role
+        InsertStatement insertCompRole = new InsertStatement(null, null, compositeRoleTableName)
+                .addColumnValue("COMPOSITE", realmAdminAppRoleId)
+                .addColumnValue("CHILD_ROLE", newRoleId);
+
+        statements.add(insertRole);
+        statements.add(insertCompRole);
+    }
+
+    protected void addDefaultProtocolMappers() throws SQLException, DatabaseException {
+        String protocolMapperTableName = database.correctObjectName("PROTOCOL_MAPPER", Table.class);
+        String protocolMapperCfgTableName = database.correctObjectName("PROTOCOL_MAPPER_CONFIG", Table.class);
+
+        PreparedStatement statement = jdbcConnection.prepareStatement("select ID, NAME, ALLOWED_CLAIMS_MASK from CLIENT");
+
+        try {
+            ResultSet resultSet = statement.executeQuery();
+            try {
+                boolean first = true;
+                while (resultSet.next()) {
+                    if (first) {
+                        confirmationMessage.append("Migrating claimsMask to protocol mappers for clients: ");
+                        first = false;
+                    }
+
+                    Object acmObj = resultSet.getObject("ALLOWED_CLAIMS_MASK");
+                    long mask = (acmObj != null) ? (Long) acmObj : ClaimMask.ALL;
+
+                    Collection<ProtocolMapperModel> protocolMappers = MigrationUtils.getMappersForClaimMask(this.kcSession, mask);
+                    for (ProtocolMapperModel protocolMapper : protocolMappers) {
+                        String mapperId = KeycloakModelUtils.generateId();
+
+                        InsertStatement insert = new InsertStatement(null, null, protocolMapperTableName)
+                                .addColumnValue("ID", mapperId)
+                                .addColumnValue("PROTOCOL", protocolMapper.getProtocol())
+                                .addColumnValue("NAME", protocolMapper.getName())
+                                .addColumnValue("CONSENT_REQUIRED", protocolMapper.isConsentRequired())
+                                .addColumnValue("CONSENT_TEXT", protocolMapper.getConsentText())
+                                .addColumnValue("PROTOCOL_MAPPER_NAME", protocolMapper.getProtocolMapper())
+                                .addColumnValue("CLIENT_ID", resultSet.getString("ID"));
+                        statements.add(insert);
+
+                        for (Map.Entry<String, String> cfgEntry : protocolMapper.getConfig().entrySet()) {
+                            InsertStatement cfgInsert = new InsertStatement(null, null, protocolMapperCfgTableName)
+                                    .addColumnValue("PROTOCOL_MAPPER_ID", mapperId)
+                                    .addColumnValue("NAME", cfgEntry.getKey())
+                                    .addColumnValue("VALUE", cfgEntry.getValue());
+                            statements.add(cfgInsert);
+                        }
+
+                    }
+
+                    confirmationMessage.append(resultSet.getString("NAME") + ", ");
+                }
+
+                // It means that some provider where processed
+                if (!first) {
+                    confirmationMessage.append(". ");
+                }
+            } finally {
+                resultSet.close();
+            }
+        } finally {
+            statement.close();
+        }
+    }
+}
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
index 04a186c..a611c3c 100644
--- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
@@ -28,6 +28,7 @@ import liquibase.resource.ClassLoaderResourceAccessor;
 import liquibase.servicelocator.ServiceLocator;
 import org.jboss.logging.Logger;
 import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+import org.keycloak.models.KeycloakSession;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
@@ -50,9 +51,12 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
     }
 
     @Override
-    public void update(Connection connection) {
+    public void update(KeycloakSession session, Connection connection) {
         logger.debug("Starting database update");
 
+        // Need ThreadLocal as liquibase doesn't seem to have API to inject custom objects into tasks
+        ThreadLocalSessionContext.setCurrentSession(session);
+
         try {
             Liquibase liquibase = getLiquibase(connection);
 
@@ -81,7 +85,10 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
             }
         } catch (Exception e) {
             throw new RuntimeException("Failed to update database", e);
+        } finally {
+            ThreadLocalSessionContext.removeCurrentSession();
         }
+
         logger.debug("Completed database update");
     }
 
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/ThreadLocalSessionContext.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/ThreadLocalSessionContext.java
new file mode 100644
index 0000000..24a583f
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/ThreadLocalSessionContext.java
@@ -0,0 +1,23 @@
+package org.keycloak.connections.jpa.updater.liquibase;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ThreadLocalSessionContext {
+
+    private static final ThreadLocal<KeycloakSession> currentSession = new ThreadLocal<KeycloakSession>();
+
+    public static KeycloakSession getCurrentSession() {
+        return currentSession.get();
+    }
+
+    public static void setCurrentSession(KeycloakSession session) {
+        currentSession.set(session);
+    }
+
+    public static void removeCurrentSession() {
+        currentSession.remove();
+    }
+}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
index a97473d..9459ca3 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -19,9 +19,6 @@
             <column name="PROTOCOL_MAPPER_NAME" type="VARCHAR(255)">
                 <constraints nullable="false"/>
             </column>
-            <column name="APPLIED_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false">
-                <constraints nullable="false"/>
-            </column>
             <column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
                 <constraints nullable="false"/>
             </column>
@@ -153,5 +150,15 @@
         <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_82S3P0DIUXAWWQQSA528UBY2Q" referencedColumnNames="ID" referencedTableName="CLIENT"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_M6QGA3RFME47335JY8JXYXH3I" referencedColumnNames="ID" referencedTableName="REALM"/>
 
+        <customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate1_2_0_Beta1"/>
+
+        <!-- Remove obsolete 'social' stuff -->
+        <dropForeignKeyConstraint baseTableName="USER_SOCIAL_LINK" constraintName="FK_68CJYS5UWM55UY823Y75XG4OM" />
+        <dropPrimaryKey tableName="USER_SOCIAL_LINK" constraintName="CONSTRAINT_3" />
+        <dropTable tableName="USER_SOCIAL_LINK" />
+        <dropForeignKeyConstraint baseTableName="REALM_SOCIAL_CONFIG" constraintName="FK_SV5I3C2TI7G0G922FGE683SOV" />
+        <dropPrimaryKey tableName="REALM_SOCIAL_CONFIG" constraintName="CONSTRAINT_1" />
+        <dropTable tableName="REALM_SOCIAL_CONFIG" />
+
     </changeSet>
 </databaseChangeLog>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-core/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-core/main/module.xml
index 870ffcc..33525e9 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-core/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-core/main/module.xml
@@ -9,7 +9,9 @@
     <dependencies>
         <module name="org.keycloak.keycloak-core"/>
         <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-events-api"/>
         <module name="javax.ws.rs.api"/>
+        <module name="org.jboss.logging"/>
     </dependencies>
 
 </module>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-oidc/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-oidc/main/module.xml
index 4f14840..5457d86 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-oidc/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-oidc/main/module.xml
@@ -9,10 +9,13 @@
     <dependencies>
         <module name="org.keycloak.keycloak-core"/>
         <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-events-api"/>
         <module name="org.keycloak.keycloak-broker-core"/>
+        <module name="org.keycloak.keycloak-services"/>
         <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.jboss.logging"/>
         <module name="javax.ws.rs.api"/>
     </dependencies>
 
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-saml/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-saml/main/module.xml
index d51fd74..f1cfeba 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-saml/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-broker-saml/main/module.xml
@@ -8,10 +8,12 @@
         <module name="javax.api" />
         <module name="org.keycloak.keycloak-core"/>
         <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-events-api"/>
         <module name="org.keycloak.keycloak-broker-core"/>
         <module name="org.keycloak.keycloak-saml-protocol"/>
         <module name="org.picketlink.common"/>
         <module name="org.picketlink.federation"/>
+        <module name="org.jboss.logging"/>
         <module name="javax.ws.rs.api"/>
         <module name="org.jboss.resteasy.resteasy-jaxrs"/>
     </dependencies>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-jpa-liquibase/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-jpa-liquibase/main/module.xml
index f52bc84..c5b7d85 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-jpa-liquibase/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-connections-jpa-liquibase/main/module.xml
@@ -10,6 +10,7 @@
         <module name="org.keycloak.keycloak-core"/>
         <module name="org.keycloak.keycloak-model-api"/>
         <module name="org.keycloak.keycloak-connections-jpa"/>
+        <module name="org.keycloak.keycloak-services"/>
         <module name="org.liquibase"/>
         <module name="javax.persistence.api"/>
         <module name="org.jboss.logging"/>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-twitter/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-twitter/main/module.xml
index beef9a7..7d5022d 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-twitter/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-twitter/main/module.xml
@@ -12,6 +12,7 @@
         <module name="org.keycloak.keycloak-broker-core"/>
         <module name="org.keycloak.keycloak-broker-oidc"/>
         <module name="org.keycloak.keycloak-model-api"/>
+        <module name="org.keycloak.keycloak-services"/>
         <module name="org.twitter4j"/>
         <module name="org.jboss.logging"/>
         <module name="javax.api"/>
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 7bacbcc..7a4bea5 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -145,7 +145,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
     @Path("{provider_id}/endpoint")
     public Object getEndpoint(@PathParam("provider_id") String providerId) {
         IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
-        Object callback = identityProvider.callback(realmModel, this);
+        Object callback = identityProvider.callback(realmModel, this, event);
         ResteasyProviderFactory.getInstance().injectProperties(callback);
         //resourceContext.initResource(brokerService);
         return callback;
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
index f997d27..c892e70 100644
--- a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java
@@ -1,6 +1,6 @@
 package org.keycloak.services.scheduled;
 
-import org.jboss.resteasy.logging.Logger;
+import org.jboss.logging.Logger;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
index 6ca63bf..e9ced5f 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
@@ -69,7 +69,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
     }
 
     @Override
-    public Object callback(RealmModel realm, AuthenticationCallback callback) {
+    public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
         return new Endpoint(realm, callback);
     }
 
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index 79b36de..772cd5a 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
@@ -134,6 +134,14 @@ public class KeycloakServer {
             }
         }
 
+        if (System.getProperty("keycloak.port") != null) {
+            config.setPort(Integer.valueOf(System.getProperty("keycloak.port")));
+        }
+
+        if (System.getProperty("keycloak.bind.address") != null) {
+            config.setHost(System.getProperty("keycloak.bind.address"));
+        }
+
         if (System.getenv("KEYCLOAK_DEV_PORT") != null) {
             config.setPort(Integer.valueOf(System.getenv("KEYCLOAK_DEV_PORT")));
         }