KEYCLOAK-3493 KEYCLOAK-3532 Added KeyStorageProvider. Support key rotation for OIDC clients and identity providers with JWKS url.

This commit is contained in:
mposolda committed 2016-09-30 21:28:23 +02:00
1 parent 6e00587468
commit f9a0abcfc4
88 files changed
+1960 -429

No files matched your search

@@ -64,7 +64,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
identityProviderModel.setAlias("custom-provider");
CustomSocialProvider customSocialProvider = providerFactory.create(identityProviderModel);
CustomSocialProvider customSocialProvider = providerFactory.create(this.session, identityProviderModel);
assertNotNull(customSocialProvider);
IdentityProviderModel config = customSocialProvider.getConfig();
@@ -87,7 +87,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
identityProviderModel.setAlias("custom-provider");
CustomIdentityProvider provider = providerFactory.create(identityProviderModel);
CustomIdentityProvider provider = providerFactory.create(this.session, identityProviderModel);
assertNotNull(provider);
IdentityProviderModel config = provider.getConfig();
@@ -158,7 +158,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertGoogleIdentityProviderConfig(IdentityProviderModel identityProvider) {
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(identityProvider);
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(session, identityProvider);
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
assertEquals("model-google", config.getAlias());
@@ -176,7 +176,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertSamlIdentityProviderConfig(IdentityProviderModel identityProvider) {
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(identityProvider);
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(session, identityProvider);
SAMLIdentityProviderConfig config = samlIdentityProvider.getConfig();
assertEquals("model-saml-signed-idp", config.getAlias());
@@ -196,7 +196,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertOidcIdentityProviderConfig(IdentityProviderModel identityProvider) {
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(identityProvider);
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(session, identityProvider);
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
assertEquals("model-oidc-idp", config.getAlias());
@@ -210,7 +210,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertFacebookIdentityProviderConfig(RealmModel realm, IdentityProviderModel identityProvider) {
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(identityProvider);
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(session, identityProvider);
OAuth2IdentityProviderConfig config = facebookIdentityProvider.getConfig();
assertEquals("model-facebook", config.getAlias());
@@ -229,7 +229,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertGitHubIdentityProviderConfig(RealmModel realm, IdentityProviderModel identityProvider) {
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(identityProvider);
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(session, identityProvider);
OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
assertEquals("model-github", config.getAlias());
@@ -248,7 +248,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertLinkedInIdentityProviderConfig(IdentityProviderModel identityProvider) {
LinkedInIdentityProvider liIdentityProvider = new LinkedInIdentityProviderFactory().create(identityProvider);
LinkedInIdentityProvider liIdentityProvider = new LinkedInIdentityProviderFactory().create(session, identityProvider);
OAuth2IdentityProviderConfig config = liIdentityProvider.getConfig();
assertEquals("model-linkedin", config.getAlias());
@@ -265,7 +265,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertStackoverflowIdentityProviderConfig(IdentityProviderModel identityProvider) {
StackoverflowIdentityProvider soIdentityProvider = new StackoverflowIdentityProviderFactory().create(identityProvider);
StackoverflowIdentityProvider soIdentityProvider = new StackoverflowIdentityProviderFactory().create(session, identityProvider);
StackOverflowIdentityProviderConfig config = soIdentityProvider.getConfig();
assertEquals("model-stackoverflow", config.getAlias());
@@ -283,7 +283,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
}
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(session, identityProvider);
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
assertEquals("model-twitter", config.getAlias());
@@ -180,4 +180,5 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP
public void testAccountManagementLinkedIdentityAlreadyExists() {
super.testAccountManagementLinkedIdentityAlreadyExists();
}
}
@@ -0,0 +1,174 @@
/*
* 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.testsuite.broker;
import javax.ws.rs.core.UriBuilder;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
import org.keycloak.common.util.Time;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.Constants;
import org.keycloak.testsuite.KeycloakServer;
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class OIDCKeycloakServerBrokerWithSignatureTest extends AbstractIdentityProviderTest {
private static final int PORT = 8082;
private static Keycloak keycloak1;
private static Keycloak keycloak2;
@ClassRule
public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
@Override
protected void configureServer(KeycloakServer server) {
server.getConfig().setPort(PORT);
}
@Override
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
}
@Override
protected String[] getTestRealms() {
return new String[] { "realm-with-oidc-identity-provider" };
}
};
@BeforeClass
public static void beforeClazz() {
keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
}
@Override
public void onBefore() {
super.onBefore();
// Enable validate signatures
IdentityProviderModel idpModel = getIdentityProviderModel();
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
cfg.setValidateSignature(true);
getRealm().updateIdentityProvider(cfg);
brokerServerRule.stopSession(this.session, true);
this.session = brokerServerRule.startSession();
}
@Override
protected String getProviderId() {
return "kc-oidc-idp";
}
@Test
public void testSignatureVerificationJwksUrl() throws Exception {
// Configure OIDC identity provider with JWKS URL
IdentityProviderModel idpModel = getIdentityProviderModel();
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
cfg.setUseJwksUrl(true);
UriBuilder b = OIDCLoginProtocolService.certsUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT).port(PORT));
String jwksUrl = b.build("realm-with-oidc-identity-provider").toString();
cfg.setJwksUrl(jwksUrl);
getRealm().updateIdentityProvider(cfg);
brokerServerRule.stopSession(this.session, true);
this.session = brokerServerRule.startSession();
// Check that user is able to login
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
// Rotate public keys on the parent broker
RealmRepresentation realm = keycloak2.realm("realm-with-oidc-identity-provider").toRepresentation();
realm.setPublicKey(org.keycloak.models.Constants.GENERATE);
keycloak2.realm("realm-with-oidc-identity-provider").update(realm);
// User not able to login now as new keys can't be yet downloaded (10s timeout)
loginIDP("test-user");
assertTrue(errorPage.isCurrent());
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
// Set time offset. New keys can be downloaded. Check that user is able to login.
Time.setOffset(20);
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
Time.setOffset(0);
}
@Test
public void testSignatureVerificationHardcodedPublicKey() throws Exception {
// Configure OIDC identity provider with publicKeySignatureVerifier
IdentityProviderModel idpModel = getIdentityProviderModel();
OIDCIdentityProviderConfig cfg = new OIDCIdentityProviderConfig(idpModel);
cfg.setUseJwksUrl(false);
RealmRepresentation realm = keycloak2.realm("realm-with-oidc-identity-provider").toRepresentation();
cfg.setPublicKeySignatureVerifier(realm.getPublicKey());
getRealm().updateIdentityProvider(cfg);
brokerServerRule.stopSession(this.session, true);
this.session = brokerServerRule.startSession();
// Check that user is able to login
assertSuccessfulAuthentication(getIdentityProviderModel(), "test-user", "test-user@localhost", false);
// Rotate public keys on the parent broker
realm.setPublicKey(org.keycloak.models.Constants.GENERATE);
keycloak2.realm("realm-with-oidc-identity-provider").update(realm);
// User not able to login now as new keys can't be yet downloaded (10s timeout)
loginIDP("test-user");
assertTrue(errorPage.isCurrent());
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
// Even after time offset is user not able to login, because it uses old key hardcoded in identityProvider config
Time.setOffset(20);
loginIDP("test-user");
assertTrue(errorPage.isCurrent());
assertEquals("Unexpected error when authenticating with identity provider", errorPage.getError());
keycloak2.realm("realm-with-oidc-identity-provider").logoutAll();
Time.setOffset(0);
}
}
@@ -29,8 +29,8 @@ import javax.ws.rs.core.Response;
*/
public class CustomIdentityProvider extends AbstractIdentityProvider<IdentityProviderModel> {
public CustomIdentityProvider(IdentityProviderModel config) {
super(config);
public CustomIdentityProvider(KeycloakSession session, IdentityProviderModel config) {
super(session, config);
}
@Override
@@ -18,6 +18,7 @@ package org.keycloak.testsuite.broker.provider;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
/**
* @author pedroigor
@@ -32,8 +33,8 @@ public class CustomIdentityProviderFactory extends AbstractIdentityProviderFacto
}
@Override
public CustomIdentityProvider create(IdentityProviderModel model) {
return new CustomIdentityProvider(model);
public CustomIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
return new CustomIdentityProvider(session, model);
}
@Override
@@ -30,8 +30,8 @@ import javax.ws.rs.core.Response;
*/
public class CustomSocialProvider extends AbstractIdentityProvider<IdentityProviderModel> implements SocialIdentityProvider<IdentityProviderModel> {
public CustomSocialProvider(IdentityProviderModel config) {
super(config);
public CustomSocialProvider(KeycloakSession session, IdentityProviderModel config) {
super(session, config);
}
@Override
@@ -19,6 +19,7 @@ package org.keycloak.testsuite.broker.provider.social;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.broker.social.SocialIdentityProviderFactory;
import org.keycloak.models.KeycloakSession;
/**
* @author pedroigor
@@ -33,8 +34,8 @@ public class CustomSocialProviderFactory extends AbstractIdentityProviderFactory
}
@Override
public CustomSocialProvider create(IdentityProviderModel model) {
return new CustomSocialProvider(model);
public CustomSocialProvider create(KeycloakSession session, IdentityProviderModel model) {
return new CustomSocialProvider(session, model);
}
@Override