keycloak-uncached

Details

diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 17f9f86..930158c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -22,7 +22,6 @@ import org.keycloak.common.enums.SslRequired;
 import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.component.ComponentFactory;
 import org.keycloak.component.ComponentModel;
-import org.keycloak.jose.jwk.JWKBuilder;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.AuthenticatorConfigModel;
@@ -66,10 +65,6 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
-import java.security.Key;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -2058,6 +2053,8 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
         em.persist(c);
         setConfig(model, c);
         model.setId(c.getId());
+        KeycloakModelUtils.notifyCreated(session, this, model);
+
         return model;
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 23ab0a9..a17e020 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1966,7 +1966,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         model.setId(entity.getId());
         realm.getComponentEntities().add(entity);
         updateRealm();
-
+        KeycloakModelUtils.notifyCreated(session, this, model);
         return model;
     }
 
diff --git a/server-spi/src/main/java/org/keycloak/component/ComponentFactory.java b/server-spi/src/main/java/org/keycloak/component/ComponentFactory.java
index a0603db..cda4a34 100644
--- a/server-spi/src/main/java/org/keycloak/component/ComponentFactory.java
+++ b/server-spi/src/main/java/org/keycloak/component/ComponentFactory.java
@@ -17,6 +17,7 @@
 package org.keycloak.component;
 
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
 import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
@@ -35,4 +36,9 @@ public interface ComponentFactory<CreatedType, ProviderType extends Provider> ex
 
     void validateConfiguration(KeycloakSession session, ComponentModel model) throws ComponentValidationException;
 
+    default
+    void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
+
+    }
+
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 230d15f..1263dc8 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -24,6 +24,7 @@ import org.keycloak.common.util.CertificateUtils;
 import org.keycloak.common.util.KeyUtils;
 import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.common.util.PemUtils;
+import org.keycloak.component.ComponentFactory;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.keys.KeyProvider;
 import org.keycloak.models.AuthenticationExecutionModel;
@@ -48,6 +49,8 @@ import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderFactory;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.transaction.JtaTransactionManagerLookup;
 
@@ -680,4 +683,16 @@ public final class KeycloakModelUtils {
     }
 
 
+    public static void notifyCreated(KeycloakSession session, RealmModel realm, ComponentModel model) {
+        Class<? extends Provider> providerClass = null;
+        try {
+            providerClass = (Class<? extends Provider>)Class.forName(model.getProviderType());
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        ProviderFactory factory = session.getKeycloakSessionFactory().getProviderFactory(providerClass, model.getProviderId());
+        if (factory instanceof ComponentFactory) {
+            ((ComponentFactory)factory).onCreate(session, realm, model);
+        }
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java
index 5102355..dd4df81 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderFactory.java
@@ -23,6 +23,7 @@ import org.keycloak.component.ComponentModel;
 import org.keycloak.component.ComponentValidationException;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
 import org.keycloak.provider.ProviderConfigProperty;
 
 import java.util.Collections;
@@ -82,4 +83,16 @@ public interface UserStorageProviderFactory<T extends UserStorageProvider> exten
 
     }
 
+    /**
+     * Called when UserStorageProviderModel is created.  This allows you to do initialization of any additional configuration
+     * you need to add.  For example, you may be introspecting a database or ldap schema to automatically create mappings.
+     *
+     * @param session
+     * @param realm
+     * @param model
+     */
+    @Override
+    default void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
+
+    }
 }
diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 8f971b8..19b6dc2 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -243,16 +243,19 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             String preferredUsername = (String)idToken.getOtherClaims().get(IDToken.PREFERRED_USERNAME);
             String email = (String)idToken.getOtherClaims().get(IDToken.EMAIL);
 
-            if (getConfig().getUserInfoUrl() != null && (id == null || name == null || preferredUsername == null || email == null) ) {
-                SimpleHttp request = JsonSimpleHttp.doGet(getConfig().getUserInfoUrl())
-                        .header("Authorization", "Bearer " + accessToken);
-                JsonNode userInfo = JsonSimpleHttp.asJson(request);
-
-                id = getJsonProperty(userInfo, "sub");
-                name = getJsonProperty(userInfo, "name");
-                preferredUsername = getJsonProperty(userInfo, "preferred_username");
-                email = getJsonProperty(userInfo, "email");
-                AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
+            if (!getConfig().isDisableUserInfoService()) {
+                String userInfoUrl = getUserInfoUrl();
+                if (userInfoUrl != null && (id == null || name == null || preferredUsername == null || email == null)) {
+                    SimpleHttp request = JsonSimpleHttp.doGet(userInfoUrl)
+                            .header("Authorization", "Bearer " + accessToken);
+                    JsonNode userInfo = JsonSimpleHttp.asJson(request);
+
+                    id = getJsonProperty(userInfo, "sub");
+                    name = getJsonProperty(userInfo, "name");
+                    preferredUsername = getJsonProperty(userInfo, "preferred_username");
+                    email = getJsonProperty(userInfo, "email");
+                    AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
+                }
             }
             identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
             identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
@@ -287,6 +290,11 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
         }
     }
 
+    protected String getUserInfoUrl() {
+        return getConfig().getUserInfoUrl();
+    }
+
+
     private String verifyAccessToken(AccessTokenResponse tokenResponse) {
         String accessToken = tokenResponse.getToken();
 
diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
index e7fdfd2..c594f7f 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
@@ -93,6 +93,15 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
         getConfig().put("backchannelSupported", String.valueOf(backchannel));
     }
 
+    public boolean isDisableUserInfoService() {
+        String disableUserInfo = getConfig().get("disableUserInfo");
+        return disableUserInfo == null ? false : Boolean.valueOf(disableUserInfo);
+    }
+
+    public void setDisableUserInfoService(boolean disable) {
+        getConfig().put("disableUserInfo", String.valueOf(disable));
+    }
+
 
 
 
diff --git a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
index 060f51a..29429d0 100755
--- a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
+++ b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProvider.java
@@ -16,9 +16,12 @@
  */
 package org.keycloak.social.google;
 
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.broker.oidc.OIDCIdentityProvider;
 import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
 import org.keycloak.broker.social.SocialIdentityProvider;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.common.util.KeycloakUriBuilder;
 import org.keycloak.models.KeycloakSession;
 
 /**
@@ -31,7 +34,7 @@ public class GoogleIdentityProvider extends OIDCIdentityProvider implements Soci
     public static final String PROFILE_URL = "https://www.googleapis.com/plus/v1/people/me/openIdConnect";
     public static final String DEFAULT_SCOPE = "openid profile email";
 
-    public GoogleIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) {
+    public GoogleIdentityProvider(KeycloakSession session, GoogleIdentityProviderConfig config) {
         super(session, config);
         config.setAuthorizationUrl(AUTH_URL);
         config.setTokenUrl(TOKEN_URL);
@@ -42,4 +45,19 @@ public class GoogleIdentityProvider extends OIDCIdentityProvider implements Soci
     protected String getDefaultScopes() {
         return DEFAULT_SCOPE;
     }
+
+    @Override
+    protected String getUserInfoUrl() {
+        String uri = super.getUserInfoUrl();
+        if (((GoogleIdentityProviderConfig)getConfig()).isUserIp()) {
+            ClientConnection connection = ResteasyProviderFactory.getContextData(ClientConnection.class);
+            if (connection != null) {
+                uri = KeycloakUriBuilder.fromUri(super.getUserInfoUrl()).queryParam("userIp", connection.getRemoteAddr()).build().toString();
+            }
+
+        }
+        logger.debugv("GOOGLE userInfoUrl: {0}", uri);
+        return uri;
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java
new file mode 100644
index 0000000..859bacd
--- /dev/null
+++ b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social.google;
+
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
+import org.keycloak.models.IdentityProviderModel;
+
+/**
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public class GoogleIdentityProviderConfig extends OIDCIdentityProviderConfig {
+
+    public GoogleIdentityProviderConfig(IdentityProviderModel model) {
+        super(model);
+    }
+
+    public boolean isUserIp() {
+        String userIp = getConfig().get("userIp");
+        return userIp == null ? false : Boolean.valueOf(userIp);
+    }
+
+    public void setUserIp(boolean ip) {
+        getConfig().put("userIp", String.valueOf(ip));
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java
index 6db6219..b35fd17 100755
--- a/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java
+++ b/services/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java
@@ -36,7 +36,7 @@ public class GoogleIdentityProviderFactory extends AbstractIdentityProviderFacto
 
     @Override
     public GoogleIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
-        return new GoogleIdentityProvider(session, new OIDCIdentityProviderConfig(model));
+        return new GoogleIdentityProvider(session, new GoogleIdentityProviderConfig(model));
     }
 
     @Override
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 37055f2..8956c33 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -434,6 +434,10 @@ store-tokens=Store Tokens
 identity-provider.store-tokens.tooltip=Enable/disable if tokens must be stored after authenticating users.
 stored-tokens-readable=Stored Tokens Readable
 identity-provider.stored-tokens-readable.tooltip=Enable/disable if new users can read any stored tokens. This assigns the broker.read-token role.
+disableUserInfo=Disable User Info
+identity-provider.disableUserInfo.tooltip=Disable usage of User Info service to obtain additional user information?  Default is to use this OIDC service.
+userIp=Use userIp Param
+identity-provider.google-userIp.tooltip=Set 'userIp' query parameter when invoking on Google's User Info service.  This will use the user's ip address.  Useful if Google is throttling access to the User Info service.
 update-profile-on-first-login=Update Profile on First Login
 on=On
 on-missing-info=On missing info
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html
index e69de29..9d6632d 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google-ext.html
@@ -0,0 +1,7 @@
+<div class="form-group">
+    <label class="col-md-2 control-label" for="userIp">{{:: 'userIp' | translate}}</label>
+    <div class="col-md-6">
+        <input ng-model="identityProvider.config.userIp" id="userIp" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+    </div>
+    <kc-tooltip>{{:: 'identity-provider.google-userIp.tooltip' | translate}}</kc-tooltip>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
index 2c51dd5..d5c0d85 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
@@ -65,6 +65,13 @@
                 <kc-tooltip>{{:: 'identity-provider.enabled.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
+                <label class="col-md-2 control-label" for="disableUserInfo">{{:: 'disableUserInfo' | translate}}</label>
+                <div class="col-md-6">
+                    <input ng-model="identityProvider.config.disableUserInfo" id="disableUserInfo" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                </div>
+                <kc-tooltip>{{:: 'identity-provider.disableUserInfo.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
                 <label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
                 <div class="col-md-6">
                     <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />