Skip to content

Commit

Permalink
KEYCLOAK-3822 Changing signature validation settings of an external I…
Browse files Browse the repository at this point in the history
…dP is not sometimes reflected
  • Loading branch information
mposolda committed Nov 28, 2016
1 parent cb4f856 commit 69ce1e0
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.security.PublicKey;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
Expand All @@ -32,6 +33,7 @@
import org.keycloak.keys.PublicKeyLoader;
import org.keycloak.keys.PublicKeyStorageProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.cache.infinispan.ClearCacheEvent;
import org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory;

Expand All @@ -51,20 +53,74 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi

private final int minTimeBetweenRequests ;

private Set<String> invalidations = new HashSet<>();

public InfinispanPublicKeyStorageProvider(KeycloakSession session, Cache<String, PublicKeysEntry> keys, Map<String, FutureTask<PublicKeysEntry>> tasksInProgress, int minTimeBetweenRequests) {
this.session = session;
this.keys = keys;
this.tasksInProgress = tasksInProgress;
this.minTimeBetweenRequests = minTimeBetweenRequests;
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
}


@Override
public void clearCache() {
keys.clear();
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
cluster.notify(InfinispanPublicKeyStorageProviderFactory.KEYS_CLEAR_CACHE_EVENTS, new ClearCacheEvent(), true);
}


void addInvalidation(String cacheKey) {
this.invalidations.add(cacheKey);
}


protected KeycloakTransaction getAfterTransaction() {
return new KeycloakTransaction() {

@Override
public void begin() {
}

@Override
public void commit() {
runInvalidations();
}

@Override
public void rollback() {
runInvalidations();
}

@Override
public void setRollbackOnly() {
}

@Override
public boolean getRollbackOnly() {
return false;
}

@Override
public boolean isActive() {
return true;
}
};
}


protected void runInvalidations() {
ClusterProvider cluster = session.getProvider(ClusterProvider.class);

for (String cacheKey : invalidations) {
keys.remove(cacheKey);
cluster.notify(cacheKey, PublicKeyStorageInvalidationEvent.create(cacheKey), true);
}
}


@Override
public PublicKey getPublicKey(String modelKey, String kid, PublicKeyLoader loader) {
// Check if key is in cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@
import org.keycloak.keys.PublicKeyStorageProvider;
import org.keycloak.keys.PublicKeyStorageSpi;
import org.keycloak.keys.PublicKeyStorageProviderFactory;
import org.keycloak.keys.PublicKeyStorageUtils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand All @@ -45,7 +50,7 @@ public class InfinispanPublicKeyStorageProviderFactory implements PublicKeyStora

public static final String KEYS_CLEAR_CACHE_EVENTS = "KEYS_CLEAR_CACHE_EVENTS";

private Cache<String, PublicKeysEntry> keysCache;
private volatile Cache<String, PublicKeysEntry> keysCache;

private final Map<String, FutureTask<PublicKeysEntry>> tasksInProgress = new ConcurrentHashMap<>();

Expand All @@ -64,6 +69,15 @@ private void lazyInit(KeycloakSession session) {
this.keysCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME);

ClusterProvider cluster = session.getProvider(ClusterProvider.class);
cluster.registerListener(ClusterProvider.ALL, (ClusterEvent event) -> {

if (event instanceof PublicKeyStorageInvalidationEvent) {
PublicKeyStorageInvalidationEvent invalidationEvent = (PublicKeyStorageInvalidationEvent) event;
keysCache.remove(invalidationEvent.getCacheKey());
}

});

cluster.registerListener(KEYS_CLEAR_CACHE_EVENTS, (ClusterEvent event) -> {

keysCache.clear();
Expand All @@ -82,6 +96,55 @@ public void init(Config.Scope config) {

@Override
public void postInit(KeycloakSessionFactory factory) {
factory.register(new ProviderEventListener() {

@Override
public void onEvent(ProviderEvent event) {
if (keysCache == null) {
return;
}

SessionAndKeyHolder cacheKey = getCacheKeyToInvalidate(event);
if (cacheKey != null) {
log.debugf("Invalidating %s from keysCache", cacheKey);
InfinispanPublicKeyStorageProvider provider = (InfinispanPublicKeyStorageProvider) cacheKey.session.getProvider(PublicKeyStorageProvider.class, getId());
provider.addInvalidation(cacheKey.cacheKey);
}
}

});
}

private SessionAndKeyHolder getCacheKeyToInvalidate(ProviderEvent event) {
if (event instanceof RealmModel.ClientUpdatedEvent) {
RealmModel.ClientUpdatedEvent eventt = (RealmModel.ClientUpdatedEvent) event;
String cacheKey = PublicKeyStorageUtils.getClientModelCacheKey(eventt.getUpdatedClient().getRealm().getId(), eventt.getUpdatedClient().getId());
return new SessionAndKeyHolder(eventt.getKeycloakSession(), cacheKey);
} else if (event instanceof RealmModel.ClientRemovedEvent) {
RealmModel.ClientRemovedEvent eventt = (RealmModel.ClientRemovedEvent) event;
String cacheKey = PublicKeyStorageUtils.getClientModelCacheKey(eventt.getClient().getRealm().getId(), eventt.getClient().getId());
return new SessionAndKeyHolder(eventt.getKeycloakSession(), cacheKey);
} else if (event instanceof RealmModel.IdentityProviderUpdatedEvent) {
RealmModel.IdentityProviderUpdatedEvent eventt = (RealmModel.IdentityProviderUpdatedEvent) event;
String cacheKey = PublicKeyStorageUtils.getIdpModelCacheKey(eventt.getRealm().getId(), eventt.getUpdatedIdentityProvider().getInternalId());
return new SessionAndKeyHolder(eventt.getKeycloakSession(), cacheKey);
} else if (event instanceof RealmModel.IdentityProviderRemovedEvent) {
RealmModel.IdentityProviderRemovedEvent eventt = (RealmModel.IdentityProviderRemovedEvent) event;
String cacheKey = PublicKeyStorageUtils.getIdpModelCacheKey(eventt.getRealm().getId(), eventt.getRemovedIdentityProvider().getInternalId());
return new SessionAndKeyHolder(eventt.getKeycloakSession(), cacheKey);
} else {
return null;
}
}

private class SessionAndKeyHolder {
private final KeycloakSession session;
private final String cacheKey;

public SessionAndKeyHolder(KeycloakSession session, String cacheKey) {
this.session = session;
this.cacheKey = cacheKey;
}

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.keys.infinispan;

import org.keycloak.models.cache.infinispan.events.InvalidationEvent;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PublicKeyStorageInvalidationEvent extends InvalidationEvent {

private String cacheKey;

public static PublicKeyStorageInvalidationEvent create(String cacheKey) {
PublicKeyStorageInvalidationEvent event = new PublicKeyStorageInvalidationEvent();
event.cacheKey = cacheKey;
return event;
}

@Override
public String getId() {
return cacheKey;
}

public String getCacheKey() {
return cacheKey;
}

@Override
public String toString() {
return "PublicKeyStorageInvalidationEvent [ " + cacheKey + " ]";
}
}
77 changes: 60 additions & 17 deletions model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -960,30 +960,35 @@ public List<IdentityProviderModel> getIdentityProviders() {
List<IdentityProviderModel> identityProviders = new ArrayList<IdentityProviderModel>();

for (IdentityProviderEntity entity: entities) {
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
identityProviderModel.setProviderId(entity.getProviderId());
identityProviderModel.setAlias(entity.getAlias());
identityProviderModel.setDisplayName(entity.getDisplayName());

identityProviderModel.setInternalId(entity.getInternalId());
Map<String, String> config = entity.getConfig();
Map<String, String> copy = new HashMap<>();
copy.putAll(config);
identityProviderModel.setConfig(copy);
identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setTrustEmail(entity.isTrustEmail());
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
identityProviderModel.setPostBrokerLoginFlowId(entity.getPostBrokerLoginFlowId());
identityProviderModel.setStoreToken(entity.isStoreToken());
identityProviderModel.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate());
IdentityProviderModel identityProviderModel = entityToModel(entity);

identityProviders.add(identityProviderModel);
}

return Collections.unmodifiableList(identityProviders);
}

private IdentityProviderModel entityToModel(IdentityProviderEntity entity) {
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
identityProviderModel.setProviderId(entity.getProviderId());
identityProviderModel.setAlias(entity.getAlias());
identityProviderModel.setDisplayName(entity.getDisplayName());

identityProviderModel.setInternalId(entity.getInternalId());
Map<String, String> config = entity.getConfig();
Map<String, String> copy = new HashMap<>();
copy.putAll(config);
identityProviderModel.setConfig(copy);
identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setTrustEmail(entity.isTrustEmail());
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
identityProviderModel.setPostBrokerLoginFlowId(entity.getPostBrokerLoginFlowId());
identityProviderModel.setStoreToken(entity.isStoreToken());
identityProviderModel.setAddReadTokenRoleOnCreate(entity.isAddReadTokenRoleOnCreate());
return identityProviderModel;
}

@Override
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
Expand Down Expand Up @@ -1024,8 +1029,28 @@ public void addIdentityProvider(IdentityProviderModel identityProvider) {
public void removeIdentityProviderByAlias(String alias) {
for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
if (entity.getAlias().equals(alias)) {

em.remove(entity);
em.flush();

session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderRemovedEvent() {

@Override
public RealmModel getRealm() {
return RealmAdapter.this;
}

@Override
public IdentityProviderModel getRemovedIdentityProvider() {
return entityToModel(entity);
}

@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});

}
}
}
Expand All @@ -1048,6 +1073,24 @@ public void updateIdentityProvider(IdentityProviderModel identityProvider) {
}

em.flush();

session.getKeycloakSessionFactory().publish(new RealmModel.IdentityProviderUpdatedEvent() {

@Override
public RealmModel getRealm() {
return RealmAdapter.this;
}

@Override
public IdentityProviderModel getUpdatedIdentityProvider() {
return identityProvider;
}

@Override
public KeycloakSession getKeycloakSession() {
return session;
}
});
}

@Override
Expand Down
Loading

0 comments on commit 69ce1e0

Please sign in to comment.