diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProvider.java deleted file mode 100644 index 8fd6f54215..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProvider.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 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.models.sessions.infinispan; - -import org.jboss.logging.Logger; -import org.keycloak.common.util.Time; -import org.keycloak.models.*; - -import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity; -import org.keycloak.models.sessions.infinispan.entities.ActionTokenReducedKey; -import java.util.*; -import java.util.concurrent.TimeUnit; -import org.infinispan.Cache; - -/** - * - * @author hmlnarik - */ -public class InfinispanActionTokenStoreProvider implements ActionTokenStoreProvider { - - private static final Logger LOG = Logger.getLogger(InfinispanActionTokenStoreProvider.class); - - private final Cache actionKeyCache; - private final InfinispanKeycloakTransaction tx; - private final KeycloakSession session; - - public InfinispanActionTokenStoreProvider(KeycloakSession session, Cache actionKeyCache) { - this.session = session; - this.actionKeyCache = actionKeyCache; - this.tx = new InfinispanKeycloakTransaction(); - - session.getTransactionManager().enlistAfterCompletion(tx); - } - - @Override - public void close() { - } - - @Override - public void put(ActionTokenKeyModel key, Map notes) { - if (key == null || key.getUserId() == null || key.getActionId() == null) { - return; - } - - ActionTokenReducedKey tokenKey = new ActionTokenReducedKey(key.getUserId(), key.getActionId(), key.getActionVerificationNonce()); - ActionTokenValueEntity tokenValue = new ActionTokenValueEntity(notes); - - LOG.debugf("Adding used action token to actionTokens cache: %s", tokenKey.toString()); - - this.tx.put(actionKeyCache, tokenKey, tokenValue, key.getExpiration() - Time.currentTime(), TimeUnit.SECONDS); - } - - @Override - public ActionTokenValueModel get(ActionTokenKeyModel actionTokenKey) { - if (actionTokenKey == null || actionTokenKey.getUserId() == null || actionTokenKey.getActionId() == null) { - return null; - } - - ActionTokenReducedKey key = new ActionTokenReducedKey(actionTokenKey.getUserId(), actionTokenKey.getActionId(), actionTokenKey.getActionVerificationNonce()); - - ActionTokenValueModel value = this.actionKeyCache.getAdvancedCache().get(key); - if (value == null) { - LOG.debugf("Not found any value in actionTokens cache for key: %s", key.toString()); - } else { - LOG.debugf("Found value in actionTokens cache for key: %s", key.toString()); - } - - return value; - } - - @Override - public ActionTokenValueModel remove(ActionTokenKeyModel actionTokenKey) { - if (actionTokenKey == null || actionTokenKey.getUserId() == null || actionTokenKey.getActionId() == null) { - return null; - } - - ActionTokenReducedKey key = new ActionTokenReducedKey(actionTokenKey.getUserId(), actionTokenKey.getActionId(), actionTokenKey.getActionVerificationNonce()); - ActionTokenValueEntity value = this.actionKeyCache.get(key); - - if (value != null) { - this.tx.remove(actionKeyCache, key); - } - - return value; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProviderFactory.java deleted file mode 100644 index 6aee60f2a9..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanActionTokenStoreProviderFactory.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 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.models.sessions.infinispan; - -import org.keycloak.Config; -import org.keycloak.Config.Scope; -import org.keycloak.connections.infinispan.InfinispanConnectionProvider; -import org.keycloak.models.*; - -import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity; -import org.keycloak.models.sessions.infinispan.entities.ActionTokenReducedKey; -import org.infinispan.Cache; -import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY; - -/** - * - * @author hmlnarik - */ -public class InfinispanActionTokenStoreProviderFactory implements ActionTokenStoreProviderFactory { - - private volatile Cache actionTokenCache; - - public static final String ACTION_TOKEN_EVENTS = "ACTION_TOKEN_EVENTS"; - - private Config.Scope config; - - @Override - public ActionTokenStoreProvider create(KeycloakSession session) { - return new InfinispanActionTokenStoreProvider(session, this.actionTokenCache); - } - - @Override - public void init(Scope config) { - this.config = config; - } - - private static Cache initActionTokenCache(KeycloakSession session) { - InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class); - Cache cache = connections.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE); - return cache; - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - Cache cache = this.actionTokenCache; - - // It is necessary to put the cache initialization here, otherwise the cache would be initialized lazily, that - // means also listeners will start only after first cache initialization - that would be too late - if (cache == null) { - synchronized (this) { - cache = this.actionTokenCache; - if (cache == null) { - this.actionTokenCache = initActionTokenCache(factory.create()); - } - } - } - } - - @Override - public void close() { - } - - @Override - public String getId() { - return "infinispan"; - } - - @Override - public int order() { - return PROVIDER_PRIORITY; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanKeycloakTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanKeycloakTransaction.java index 7899589fbd..2c2128af19 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanKeycloakTransaction.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanKeycloakTransaction.java @@ -16,6 +16,8 @@ */ package org.keycloak.models.sessions.infinispan; +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.commons.api.BasicCache; import org.keycloak.cluster.ClusterEvent; import org.keycloak.cluster.ClusterProvider; import org.infinispan.context.Flag; @@ -97,7 +99,7 @@ public class InfinispanKeycloakTransaction implements KeycloakTransaction { } } - public void put(Cache cache, K key, V value, long lifespan, TimeUnit lifespanUnit) { + public void put(BasicCache cache, K key, V value, long lifespan, TimeUnit lifespanUnit) { log.tracev("Adding cache operation: {0} on {1}", CacheOperation.ADD_WITH_LIFESPAN, key); Object taskKey = getTaskKey(cache, key); @@ -179,7 +181,7 @@ public class InfinispanKeycloakTransaction implements KeycloakTransaction { tasks.put(taskKey, () -> clusterProvider.notify(taskKey, event, ignoreSender, ClusterProvider.DCNotify.ALL_DCS)); } - public void remove(Cache cache, K key) { + public void remove(BasicCache cache, K key) { log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REMOVE, key); Object taskKey = getTaskKey(cache, key); @@ -201,7 +203,7 @@ public class InfinispanKeycloakTransaction implements KeycloakTransaction { } // This is for possibility to lookup for session by id, which was created in this transaction - public V get(Cache cache, K key) { + public V get(BasicCache cache, K key) { Object taskKey = getTaskKey(cache, key); CacheTask current = tasks.get(taskKey); if (current != null) { @@ -214,7 +216,7 @@ public class InfinispanKeycloakTransaction implements KeycloakTransaction { return cache.get(key); } - private static Object getTaskKey(Cache cache, K key) { + private static Object getTaskKey(BasicCache cache, K key) { if (key instanceof String) { return new StringBuilder(cache.getName()) .append("::") @@ -245,8 +247,10 @@ public class InfinispanKeycloakTransaction implements KeycloakTransaction { } // Ignore return values. Should have better performance within cluster / cross-dc env - private static Cache decorateCache(Cache cache) { - return cache.getAdvancedCache() + private static BasicCache decorateCache(BasicCache cache) { + if (cache instanceof RemoteCache) + return cache; + return ((Cache) cache).getAdvancedCache() .withFlags(Flag.IGNORE_RETURN_VALUES, Flag.SKIP_REMOTE_LOOKUP); } } \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProvider.java index 61a84bf37d..1c033da118 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProvider.java @@ -27,12 +27,12 @@ import org.jboss.logging.Logger; import org.keycloak.common.util.Time; import org.keycloak.models.KeycloakSession; import org.keycloak.models.SingleUseObjectProvider; -import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity; +import org.keycloak.models.sessions.infinispan.entities.SingleUseObjectValueEntity; import org.keycloak.connections.infinispan.InfinispanUtil; /** - * TODO: Check if Boolean can be used as single-use cache argument instead of ActionTokenValueEntity. With respect to other single-use cache usecases like "Revoke Refresh Token" . - * Also with respect to the usage of streams iterating over "actionTokens" cache (check there are no ClassCastExceptions when casting values directly to ActionTokenValueEntity) + * TODO: Check if Boolean can be used as single-use cache argument instead of SingleUseObjectValueEntity. With respect to other single-use cache usecases like "Revoke Refresh Token" . + * Also with respect to the usage of streams iterating over "actionTokens" cache (check there are no ClassCastExceptions when casting values directly to SingleUseObjectValueEntity) * * * @author Marek Posolda @@ -41,22 +41,24 @@ public class InfinispanSingleUseObjectProvider implements SingleUseObjectProvide public static final Logger logger = Logger.getLogger(InfinispanSingleUseObjectProvider.class); - private final Supplier> tokenCache; + private final Supplier> singleUseObjectCache; private final KeycloakSession session; + private final InfinispanKeycloakTransaction tx; - public InfinispanSingleUseObjectProvider(KeycloakSession session, Supplier> actionKeyCache) { + public InfinispanSingleUseObjectProvider(KeycloakSession session, Supplier> singleUseObjectCache) { this.session = session; - this.tokenCache = actionKeyCache; + this.singleUseObjectCache = singleUseObjectCache; + this.tx = new InfinispanKeycloakTransaction(); + + session.getTransactionManager().enlistAfterCompletion(tx); } @Override public void put(String key, long lifespanSeconds, Map notes) { - ActionTokenValueEntity tokenValue = new ActionTokenValueEntity(notes); - + SingleUseObjectValueEntity tokenValue = new SingleUseObjectValueEntity(notes); try { - BasicCache cache = tokenCache.get(); - long lifespanMs = InfinispanUtil.toHotrodTimeMs(cache, Time.toMillis(lifespanSeconds)); - cache.put(key, tokenValue, lifespanMs, TimeUnit.MILLISECONDS); + BasicCache cache = singleUseObjectCache.get(); + tx.put(cache, key, tokenValue, InfinispanUtil.toHotrodTimeMs(cache, Time.toMillis(lifespanSeconds)), TimeUnit.MILLISECONDS); } catch (HotRodClientException re) { // No need to retry. The hotrod (remoteCache) has some retries in itself in case of some random network error happened. if (logger.isDebugEnabled()) { @@ -69,17 +71,23 @@ public class InfinispanSingleUseObjectProvider implements SingleUseObjectProvide @Override public Map get(String key) { - BasicCache cache = tokenCache.get(); - ActionTokenValueEntity actionTokenValueEntity = cache.get(key); - return actionTokenValueEntity != null ? actionTokenValueEntity.getNotes() : null; + SingleUseObjectValueEntity singleUseObjectValueEntity; + + BasicCache cache = singleUseObjectCache.get(); + singleUseObjectValueEntity = tx.get(cache, key); + return singleUseObjectValueEntity != null ? singleUseObjectValueEntity.getNotes() : null; } @Override public Map remove(String key) { try { - BasicCache cache = tokenCache.get(); - ActionTokenValueEntity existing = cache.remove(key); - return existing == null ? null : existing.getNotes(); + BasicCache cache = singleUseObjectCache.get(); + SingleUseObjectValueEntity singleUseObjectValueEntity = tx.get(cache, key); + if (singleUseObjectValueEntity != null) { + tx.remove(cache, key); + return singleUseObjectValueEntity.getNotes(); + } + return null; } catch (HotRodClientException re) { // No need to retry. The hotrod (remoteCache) has some retries in itself in case of some random network error happened. // In case of lock conflict, we don't want to retry anyway as there was likely an attempt to remove the code from different place. @@ -93,18 +101,18 @@ public class InfinispanSingleUseObjectProvider implements SingleUseObjectProvide @Override public boolean replace(String key, Map notes) { - BasicCache cache = tokenCache.get(); - return cache.replace(key, new ActionTokenValueEntity(notes)) != null; + BasicCache cache = singleUseObjectCache.get(); + return cache.replace(key, new SingleUseObjectValueEntity(notes)) != null; } @Override public boolean putIfAbsent(String key, long lifespanInSeconds) { - ActionTokenValueEntity tokenValue = new ActionTokenValueEntity(null); + SingleUseObjectValueEntity tokenValue = new SingleUseObjectValueEntity(null); + BasicCache cache = singleUseObjectCache.get(); try { - BasicCache cache = tokenCache.get(); long lifespanMs = InfinispanUtil.toHotrodTimeMs(cache, Time.toMillis(lifespanInSeconds)); - ActionTokenValueEntity existing = cache.putIfAbsent(key, tokenValue, lifespanMs, TimeUnit.MILLISECONDS); + SingleUseObjectValueEntity existing = cache.putIfAbsent(key, tokenValue, lifespanMs, TimeUnit.MILLISECONDS); return existing == null; } catch (HotRodClientException re) { // No need to retry. The hotrod (remoteCache) has some retries in itself in case of some random network error happened. @@ -118,7 +126,7 @@ public class InfinispanSingleUseObjectProvider implements SingleUseObjectProvide @Override public boolean contains(String key) { - BasicCache cache = tokenCache.get(); + BasicCache cache = singleUseObjectCache.get(); return cache.containsKey(key); } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProviderFactory.java index 0b60b8b614..bc87e0e65c 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanSingleUseObjectProviderFactory.java @@ -17,7 +17,6 @@ package org.keycloak.models.sessions.infinispan; -import java.util.function.Supplier; import org.infinispan.Cache; import org.infinispan.client.hotrod.Flag; @@ -26,11 +25,14 @@ import org.infinispan.commons.api.BasicCache; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; +import org.keycloak.connections.infinispan.InfinispanUtil; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.SingleUseObjectProviderFactory; -import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity; -import org.keycloak.connections.infinispan.InfinispanUtil; +import org.keycloak.models.sessions.infinispan.entities.SingleUseObjectValueEntity; + +import java.util.function.Supplier; + import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY; /** @@ -40,26 +42,14 @@ public class InfinispanSingleUseObjectProviderFactory implements SingleUseObject private static final Logger LOG = Logger.getLogger(InfinispanSingleUseObjectProviderFactory.class); - // Reuse "actionTokens" infinispan cache for now - private volatile Supplier> tokenCache; + private volatile Supplier> singleUseObjectCache; @Override public InfinispanSingleUseObjectProvider create(KeycloakSession session) { - lazyInit(session); - return new InfinispanSingleUseObjectProvider(session, tokenCache); + return new InfinispanSingleUseObjectProvider(session, singleUseObjectCache); } - private void lazyInit(KeycloakSession session) { - if (tokenCache == null) { - synchronized (this) { - if (tokenCache == null) { - this.tokenCache = getActionTokenCache(session); - } - } - } - } - - static Supplier getActionTokenCache(KeycloakSession session) { + static Supplier getSingleUseObjectCache(KeycloakSession session) { InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class); Cache cache = connections.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE); @@ -67,15 +57,10 @@ public class InfinispanSingleUseObjectProviderFactory implements SingleUseObject if (remoteCache != null) { LOG.debugf("Having remote stores. Using remote cache '%s' for single-use cache of token", remoteCache.getName()); - return () -> { - // Doing this way as flag is per invocation - return remoteCache.withFlags(Flag.FORCE_RETURN_VALUE); - }; + return () -> remoteCache.withFlags(Flag.FORCE_RETURN_VALUE); } else { - LOG.debugf("Not having remote stores. Using normal cache '%s' for single-use cache of token", cache.getName()); - return () -> { - return cache; - }; + LOG.debugf("Not having remote stores. Using basic cache '%s' for single-use cache of token", cache.getName()); + return () -> cache; } } @@ -86,7 +71,11 @@ public class InfinispanSingleUseObjectProviderFactory implements SingleUseObject @Override public void postInit(KeycloakSessionFactory factory) { - + // It is necessary to put the cache initialization here, otherwise the cache would be initialized lazily, that + // means also listeners will start only after first cache initialization - that would be too late + if (singleUseObjectCache == null) { + this.singleUseObjectCache = getSingleUseObjectCache(factory.create()); + } } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenReducedKey.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenReducedKey.java deleted file mode 100644 index 24742e58d6..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenReducedKey.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2017 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.models.sessions.infinispan.entities; - -import java.io.*; -import java.util.Objects; -import java.util.UUID; -import org.infinispan.commons.marshall.Externalizer; -import org.infinispan.commons.marshall.SerializeWith; - -/** - * - * @author hmlnarik - */ -@SerializeWith(value = ActionTokenReducedKey.ExternalizerImpl.class) -public class ActionTokenReducedKey implements Serializable { - - private final String userId; - private final String actionId; - - /** - * Nonce that must match. - */ - private final UUID actionVerificationNonce; - - public ActionTokenReducedKey(String userId, String actionId, UUID actionVerificationNonce) { - this.userId = userId; - this.actionId = actionId; - this.actionVerificationNonce = actionVerificationNonce; - } - - public String getUserId() { - return userId; - } - - public String getActionId() { - return actionId; - } - - public UUID getActionVerificationNonce() { - return actionVerificationNonce; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 71 * hash + Objects.hashCode(this.userId); - hash = 71 * hash + Objects.hashCode(this.actionId); - hash = 71 * hash + Objects.hashCode(this.actionVerificationNonce); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ActionTokenReducedKey other = (ActionTokenReducedKey) obj; - return Objects.equals(this.userId, other.getUserId()) - && Objects.equals(this.actionId, other.getActionId()) - && Objects.equals(this.actionVerificationNonce, other.getActionVerificationNonce()); - } - - @Override - public String toString() { - return "userId=" + userId + ", actionId=" + actionId + ", actionVerificationNonce=" + actionVerificationNonce; - } - - public static class ExternalizerImpl implements Externalizer { - - @Override - public void writeObject(ObjectOutput output, ActionTokenReducedKey t) throws IOException { - output.writeUTF(t.userId); - output.writeUTF(t.actionId); - output.writeLong(t.actionVerificationNonce.getMostSignificantBits()); - output.writeLong(t.actionVerificationNonce.getLeastSignificantBits()); - } - - @Override - public ActionTokenReducedKey readObject(ObjectInput input) throws IOException, ClassNotFoundException { - return new ActionTokenReducedKey( - input.readUTF(), - input.readUTF(), - new UUID(input.readLong(), input.readLong()) - ); - } - } - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenValueEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SingleUseObjectValueEntity.java similarity index 75% rename from model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenValueEntity.java rename to model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SingleUseObjectValueEntity.java index 647a9df584..617c8ed614 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ActionTokenValueEntity.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SingleUseObjectValueEntity.java @@ -16,7 +16,7 @@ */ package org.keycloak.models.sessions.infinispan.entities; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import java.io.*; import java.util.*; @@ -26,12 +26,12 @@ import org.infinispan.commons.marshall.SerializeWith; /** * @author hmlnarik */ -@SerializeWith(ActionTokenValueEntity.ExternalizerImpl.class) -public class ActionTokenValueEntity implements ActionTokenValueModel { +@SerializeWith(SingleUseObjectValueEntity.ExternalizerImpl.class) +public class SingleUseObjectValueEntity implements SingleUseObjectValueModel { private final Map notes; - public ActionTokenValueEntity(Map notes) { + public SingleUseObjectValueEntity(Map notes) { this.notes = notes == null ? Collections.EMPTY_MAP : new HashMap<>(notes); } @@ -47,15 +47,15 @@ public class ActionTokenValueEntity implements ActionTokenValueModel { @Override public String toString() { - return String.format("ActionTokenValueEntity [ notes=%s ]", notes.toString()); + return String.format("SingleUseObjectValueEntity [ notes=%s ]", notes.toString()); } - public static class ExternalizerImpl implements Externalizer { + public static class ExternalizerImpl implements Externalizer { private static final int VERSION_1 = 1; @Override - public void writeObject(ObjectOutput output, ActionTokenValueEntity t) throws IOException { + public void writeObject(ObjectOutput output, SingleUseObjectValueEntity t) throws IOException { output.writeByte(VERSION_1); output.writeBoolean(t.notes.isEmpty()); @@ -65,7 +65,7 @@ public class ActionTokenValueEntity implements ActionTokenValueModel { } @Override - public ActionTokenValueEntity readObject(ObjectInput input) throws IOException, ClassNotFoundException { + public SingleUseObjectValueEntity readObject(ObjectInput input) throws IOException, ClassNotFoundException { byte version = input.readByte(); if (version != VERSION_1) { @@ -75,7 +75,7 @@ public class ActionTokenValueEntity implements ActionTokenValueModel { Map notes = notesEmpty ? Collections.EMPTY_MAP : (Map) input.readObject(); - return new ActionTokenValueEntity(notes); + return new SingleUseObjectValueEntity(notes); } } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenReducedKeyWFExternalizer.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenReducedKeyWFExternalizer.java deleted file mode 100644 index 30b79f6e73..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenReducedKeyWFExternalizer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 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.models.sessions.infinispan.entities.wildfly; - -import org.keycloak.models.sessions.infinispan.entities.ActionTokenReducedKey; - -public class ActionTokenReducedKeyWFExternalizer extends InfinispanExternalizerAdapter { - - public ActionTokenReducedKeyWFExternalizer() { - super(ActionTokenReducedKey.class, new ActionTokenReducedKey.ExternalizerImpl()); - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenValueEntityWFExternalizer.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenValueEntityWFExternalizer.java index e1ccd7a8a8..cd911d89bb 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenValueEntityWFExternalizer.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/wildfly/ActionTokenValueEntityWFExternalizer.java @@ -18,11 +18,11 @@ package org.keycloak.models.sessions.infinispan.entities.wildfly; -import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity; +import org.keycloak.models.sessions.infinispan.entities.SingleUseObjectValueEntity; -public class ActionTokenValueEntityWFExternalizer extends InfinispanExternalizerAdapter { +public class ActionTokenValueEntityWFExternalizer extends InfinispanExternalizerAdapter { public ActionTokenValueEntityWFExternalizer() { - super(ActionTokenValueEntity.class, new ActionTokenValueEntity.ExternalizerImpl()); + super(SingleUseObjectValueEntity.class, new SingleUseObjectValueEntity.ExternalizerImpl()); } } diff --git a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory deleted file mode 100644 index 4100eccf23..0000000000 --- a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory diff --git a/model/infinispan/src/main/resources/META-INF/services/org.wildfly.clustering.marshalling.Externalizer b/model/infinispan/src/main/resources/META-INF/services/org.wildfly.clustering.marshalling.Externalizer index 30ec605162..644eca4f6a 100644 --- a/model/infinispan/src/main/resources/META-INF/services/org.wildfly.clustering.marshalling.Externalizer +++ b/model/infinispan/src/main/resources/META-INF/services/org.wildfly.clustering.marshalling.Externalizer @@ -18,7 +18,6 @@ org.keycloak.models.sessions.infinispan.entities.wildfly.GroupListPredicateWFExternalizer org.keycloak.models.sessions.infinispan.entities.wildfly.RealmRemovedSessionEventWFExternalizer -org.keycloak.models.sessions.infinispan.entities.wildfly.ActionTokenReducedKeyWFExternalizer org.keycloak.models.sessions.infinispan.entities.wildfly.WrapperClusterEventWFExternalizer org.keycloak.models.sessions.infinispan.entities.wildfly.UserConsentsUpdatedEventWFExternalizer org.keycloak.models.sessions.infinispan.entities.wildfly.RoleRemovedEventWFExternalizer diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java index b72805bf79..34d232fc5c 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/HotRodMapStorageProviderFactory.java @@ -17,11 +17,10 @@ package org.keycloak.models.map.storage.hotRod; -import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.common.Profile; import org.keycloak.component.AmphibianProviderFactory; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; @@ -192,7 +191,7 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory HotRodConnectionProvider connectionProvider = session.getProvider(HotRodConnectionProvider.class); HotRodEntityDescriptor entityDescriptor = (HotRodEntityDescriptor) getEntityDescriptor(modelType); - if (modelType == ActionTokenValueModel.class) { + if (modelType == SingleUseObjectValueModel.class) { return new SingleUseObjectHotRodMapStorage(connectionProvider.getRemoteCache(entityDescriptor.getCacheName()), StringKeyConverter.StringKey.INSTANCE, entityDescriptor, CLONER); } if (modelType == AuthenticatedClientSessionModel.class) { return new HotRodMapStorage(connectionProvider.getRemoteCache(entityDescriptor.getCacheName()), diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/SingleUseObjectHotRodMapStorage.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/SingleUseObjectHotRodMapStorage.java index 96f20eaeea..9cb2f80f6d 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/SingleUseObjectHotRodMapStorage.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/SingleUseObjectHotRodMapStorage.java @@ -18,7 +18,7 @@ package org.keycloak.models.map.storage.hotRod; import org.infinispan.client.hotrod.RemoteCache; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.DeepCloner; @@ -45,7 +45,7 @@ import java.util.stream.Stream; * @author Martin Kanis */ public class SingleUseObjectHotRodMapStorage & AbstractEntity, M> - extends HotRodMapStorage { + extends HotRodMapStorage { private final StringKeyConverter keyConverter; private final HotRodEntityDescriptor storedEntityDescriptor; @@ -61,18 +61,15 @@ public class SingleUseObjectHotRodMapStorage createTransactionInternal(KeycloakSession session) { - Map, MapModelCriteriaBuilder.UpdatePredicatesFunc> fieldPredicates = - MapFieldPredicates.getPredicates((Class) storedEntityDescriptor.getModelTypeClass()); + protected MapKeycloakTransaction createTransactionInternal(KeycloakSession session) { + Map, MapModelCriteriaBuilder.UpdatePredicatesFunc> fieldPredicates = + MapFieldPredicates.getPredicates((Class) storedEntityDescriptor.getModelTypeClass()); return new SingleUseObjectKeycloakTransaction(this, keyConverter, cloner, fieldPredicates); } @Override public HotRodSingleUseObjectEntityDelegate create(HotRodSingleUseObjectEntityDelegate value) { if (value.getId() == null) { - if (value.getUserId() != null && value.getActionId() != null && value.getActionVerificationNonce() != null) { - value.setId(value.getUserId() + ":" + value.getActionId() + ":" + value.getActionVerificationNonce()); - } if (value.getObjectKey() != null) { value.setId(value.getObjectKey()); } @@ -81,8 +78,8 @@ public class SingleUseObjectHotRodMapStorage read(QueryParameters queryParameters) { - DefaultModelCriteria criteria = queryParameters.getModelCriteriaBuilder(); + public Stream read(QueryParameters queryParameters) { + DefaultModelCriteria criteria = queryParameters.getModelCriteriaBuilder(); if (criteria == null) { return Stream.empty(); diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/singleUseObject/HotRodSingleUseObjectEntity.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/singleUseObject/HotRodSingleUseObjectEntity.java index 13d64a99ad..0ff805ee97 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/singleUseObject/HotRodSingleUseObjectEntity.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/singleUseObject/HotRodSingleUseObjectEntity.java @@ -35,7 +35,7 @@ import java.util.Set; implementInterface = "org.keycloak.models.map.singleUseObject.MapSingleUseObjectEntity", inherits = "org.keycloak.models.map.storage.hotRod.singleUseObject.HotRodSingleUseObjectEntity.AbstractHotRodSingleUseObjectEntityDelegate", topLevelEntity = true, - modelClass = "org.keycloak.models.ActionTokenValueModel" + modelClass = "org.keycloak.models.SingleUseObjectValueModel" ) @ProtoDoc("@Indexed") @ProtoDoc("schema-version: " + HotRodSingleUseObjectEntity.VERSION) @@ -66,15 +66,6 @@ public class HotRodSingleUseObjectEntity extends AbstractHotRodEntity { @ProtoField(number = 3) public String objectKey; - @ProtoField(number = 4) - public String userId; - - @ProtoField(number = 5) - public String actionId; - - @ProtoField(number = 6) - public String actionVerificationNonce; - @ProtoField(number = 7) public Long expiration; diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java index cef2f98990..9752ab52d4 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java @@ -53,7 +53,7 @@ import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.component.AmphibianProviderFactory; import org.keycloak.events.Event; import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.GroupModel; @@ -232,7 +232,7 @@ public class JpaMapStorageProviderFactory implements //roles MODEL_TO_TX.put(RoleModel.class, JpaRoleMapKeycloakTransaction::new); //single-use-objects - MODEL_TO_TX.put(ActionTokenValueModel.class, JpaSingleUseObjectMapKeycloakTransaction::new); + MODEL_TO_TX.put(SingleUseObjectValueModel.class, JpaSingleUseObjectMapKeycloakTransaction::new); //user-login-failures MODEL_TO_TX.put(UserLoginFailureModel.class, JpaUserLoginFailureMapKeycloakTransaction::new); //users diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectMapKeycloakTransaction.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectMapKeycloakTransaction.java index cfe3f8959d..87e90e261a 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectMapKeycloakTransaction.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectMapKeycloakTransaction.java @@ -21,7 +21,7 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Root; import javax.persistence.criteria.Selection; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.map.singleUseObject.MapSingleUseObjectEntity; import org.keycloak.models.map.storage.jpa.JpaMapKeycloakTransaction; @@ -36,10 +36,10 @@ import static org.keycloak.models.map.storage.jpa.Constants.CURRENT_SCHEMA_VERSI * * @author Stefan Guilhen */ -public class JpaSingleUseObjectMapKeycloakTransaction extends JpaMapKeycloakTransaction { +public class JpaSingleUseObjectMapKeycloakTransaction extends JpaMapKeycloakTransaction { public JpaSingleUseObjectMapKeycloakTransaction(KeycloakSession session, final EntityManager em) { - super(session, JpaSingleUseObjectEntity.class, ActionTokenValueModel.class, em); + super(session, JpaSingleUseObjectEntity.class, SingleUseObjectValueModel.class, em); } @Override diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectModelCriteriaBuilder.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectModelCriteriaBuilder.java index e163e1a0c0..ef01735408 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectModelCriteriaBuilder.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/JpaSingleUseObjectModelCriteriaBuilder.java @@ -16,10 +16,7 @@ */ package org.keycloak.models.map.storage.jpa.singleUseObject; -import java.util.HashMap; -import java.util.Map; - -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.map.storage.CriterionNotSupportedException; import org.keycloak.models.map.storage.jpa.JpaModelCriteriaBuilder; import org.keycloak.models.map.storage.jpa.JpaPredicateFunction; @@ -31,14 +28,7 @@ import org.keycloak.storage.SearchableModelField; * * @author Stefan Guilhen */ -public class JpaSingleUseObjectModelCriteriaBuilder extends JpaModelCriteriaBuilder { - - private static final Map FIELD_TO_JSON_PROP = new HashMap<>(); - static { - FIELD_TO_JSON_PROP.put(ActionTokenValueModel.SearchableFields.USER_ID.getName(), "fUserId"); - FIELD_TO_JSON_PROP.put(ActionTokenValueModel.SearchableFields.ACTION_ID.getName(), "fActionId"); - FIELD_TO_JSON_PROP.put(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE.getName(), "fActionVerificationNonce"); - } +public class JpaSingleUseObjectModelCriteriaBuilder extends JpaModelCriteriaBuilder { public JpaSingleUseObjectModelCriteriaBuilder() { super(JpaSingleUseObjectModelCriteriaBuilder::new); @@ -49,20 +39,10 @@ public class JpaSingleUseObjectModelCriteriaBuilder extends JpaModelCriteriaBuil } @Override - public JpaSingleUseObjectModelCriteriaBuilder compare(SearchableModelField modelField, Operator op, Object... value) { + public JpaSingleUseObjectModelCriteriaBuilder compare(SearchableModelField modelField, Operator op, Object... value) { switch (op) { case EQ: - if (modelField == ActionTokenValueModel.SearchableFields.USER_ID || - modelField == ActionTokenValueModel.SearchableFields.ACTION_ID || - modelField == ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE) { - - validateValue(value, modelField, op, String.class); - - return new JpaSingleUseObjectModelCriteriaBuilder((cb, query, root) -> - cb.equal(cb.function("->>", String.class, root.get("metadata"), - cb.literal(FIELD_TO_JSON_PROP.get(modelField.getName()))), value[0]) - ); - } else if(modelField == ActionTokenValueModel.SearchableFields.OBJECT_KEY) { + if(modelField == SingleUseObjectValueModel.SearchableFields.OBJECT_KEY) { validateValue(value, modelField, op, String.class); return new JpaSingleUseObjectModelCriteriaBuilder((cb, query, root) -> cb.equal(root.get(modelField.getName()), value[0]) diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/entity/JpaSingleUseObjectEntity.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/entity/JpaSingleUseObjectEntity.java index a92bcf1fc3..3047f871e7 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/entity/JpaSingleUseObjectEntity.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/singleUseObject/entity/JpaSingleUseObjectEntity.java @@ -141,26 +141,6 @@ public class JpaSingleUseObjectEntity extends MapSingleUseObjectEntity.AbstractS return CURRENT_SCHEMA_VERSION_SINGLE_USE_OBJECT; } - @Override - public String getActionId() { - return this.metadata.getActionId(); - } - - @Override - public void setActionId(String actionId) { - this.metadata.setActionId(actionId); - } - - @Override - public String getActionVerificationNonce() { - return this.metadata.getActionVerificationNonce(); - } - - @Override - public void setActionVerificationNonce(String actionVerificationNonce) { - this.metadata.setActionVerificationNonce(actionVerificationNonce); - } - @Override public Map getNotes() { return this.notes.stream() @@ -192,16 +172,6 @@ public class JpaSingleUseObjectEntity extends MapSingleUseObjectEntity.AbstractS } } - @Override - public String getUserId() { - return this.metadata.getUserId(); - } - - @Override - public void setUserId(String userId) { - this.metadata.setUserId(userId); - } - @Override public Long getExpiration() { if (this.isMetadataInitialized()) return this.metadata.getExpiration(); diff --git a/model/map-jpa/src/main/resources/META-INF/single-use-objects/jpa-single-use-objects-changelog-1.xml b/model/map-jpa/src/main/resources/META-INF/single-use-objects/jpa-single-use-objects-changelog-1.xml index ad2e6fc4c2..904e2eb29e 100644 --- a/model/map-jpa/src/main/resources/META-INF/single-use-objects/jpa-single-use-objects-changelog-1.xml +++ b/model/map-jpa/src/main/resources/META-INF/single-use-objects/jpa-single-use-objects-changelog-1.xml @@ -66,4 +66,8 @@ limitations under the License. + + + + diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/AbstractSingleUseObjectModel.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/AbstractSingleUseObjectModel.java index 1be5ca1467..0e29cfcfb5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/AbstractSingleUseObjectModel.java +++ b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/AbstractSingleUseObjectModel.java @@ -17,8 +17,7 @@ package org.keycloak.models.map.singleUseObject; -import org.keycloak.models.ActionTokenKeyModel; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.map.common.AbstractEntity; @@ -27,7 +26,7 @@ import java.util.Objects; /** * @author Martin Kanis */ -public abstract class AbstractSingleUseObjectModel implements ActionTokenKeyModel, ActionTokenValueModel { +public abstract class AbstractSingleUseObjectModel implements SingleUseObjectValueModel { protected final KeycloakSession session; protected final E entity; @@ -42,7 +41,7 @@ public abstract class AbstractSingleUseObjectModel imp @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof ActionTokenValueModel)) return false; + if (!(o instanceof SingleUseObjectValueModel)) return false; MapSingleUseObjectAdapter that = (MapSingleUseObjectAdapter) o; return Objects.equals(that.entity.getId(), entity.getId()); diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapActionTokenProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapActionTokenProviderFactory.java deleted file mode 100644 index e0c02feedb..0000000000 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapActionTokenProviderFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2022 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.models.map.singleUseObject; - -import org.keycloak.models.ActionTokenStoreProviderFactory; -import org.keycloak.models.ActionTokenValueModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.map.common.AbstractMapProviderFactory; - -/** - * @author Martin Kanis - */ -public class MapActionTokenProviderFactory extends AbstractMapProviderFactory - implements ActionTokenStoreProviderFactory { - - public MapActionTokenProviderFactory() { - super(ActionTokenValueModel.class, MapSingleUseObjectProvider.class); - } - - @Override - public MapSingleUseObjectProvider createNew(KeycloakSession session) { - return new MapSingleUseObjectProvider(session, getStorage(session)); - } - - @Override - public String getHelpText() { - return "Action token provider"; - } - -} diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectAdapter.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectAdapter.java index e25ff24aca..b8146c2f41 100644 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectAdapter.java @@ -18,11 +18,9 @@ package org.keycloak.models.map.singleUseObject; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.map.common.TimeAdapter; import java.util.Collections; import java.util.Map; -import java.util.UUID; /** * @author Martin Kanis @@ -33,28 +31,6 @@ public class MapSingleUseObjectAdapter extends AbstractSingleUseObjectModel getNotes() { Map notes = entity.getNotes(); diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectEntity.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectEntity.java index 5330cd287a..78791c1aa7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectEntity.java @@ -51,18 +51,9 @@ public interface MapSingleUseObjectEntity extends AbstractEntity, UpdatableEntit } } - String getUserId(); - void setUserId(String userId); - String getObjectKey(); void setObjectKey(String objectKey); - String getActionId(); - void setActionId(String actionId); - - String getActionVerificationNonce(); - void setActionVerificationNonce(String actionVerificationNonce); - Map getNotes(); void setNotes(Map notes); String getNote(String name); diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProvider.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProvider.java index 2586f6afc1..8a8ae10f69 100644 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProvider.java @@ -19,9 +19,7 @@ package org.keycloak.models.map.singleUseObject; import org.jboss.logging.Logger; import org.keycloak.common.util.Time; -import org.keycloak.models.ActionTokenKeyModel; -import org.keycloak.models.ActionTokenStoreProvider; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.SingleUseObjectProvider; @@ -42,97 +40,17 @@ import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.crit /** * @author Martin Kanis */ -public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, SingleUseObjectProvider { +public class MapSingleUseObjectProvider implements SingleUseObjectProvider { private static final Logger LOG = Logger.getLogger(MapSingleUseObjectProvider.class); private final KeycloakSession session; - protected final MapKeycloakTransaction actionTokenStoreTx; + protected final MapKeycloakTransaction singleUseObjectTx; - public MapSingleUseObjectProvider(KeycloakSession session, MapStorage storage) { + public MapSingleUseObjectProvider(KeycloakSession session, MapStorage storage) { this.session = session; - actionTokenStoreTx = storage.createTransaction(session); + singleUseObjectTx = storage.createTransaction(session); - session.getTransactionManager().enlistAfterCompletion(actionTokenStoreTx); - } - - private ActionTokenValueModel singleUseEntityToAdapter(MapSingleUseObjectEntity origEntity) { - if (isExpired(origEntity, false)) { - actionTokenStoreTx.delete(origEntity.getId()); - return null; - } else { - return new MapSingleUseObjectAdapter(session, origEntity); - } - } - - @Override - public void put(ActionTokenKeyModel actionTokenKey, Map notes) { - if (actionTokenKey == null || actionTokenKey.getUserId() == null || actionTokenKey.getActionId() == null || actionTokenKey.getActionVerificationNonce() == null) { - return; - } - - LOG.tracef("put(%s, %s, %s)%s", actionTokenKey.getUserId(), actionTokenKey.getActionId(), actionTokenKey.getActionVerificationNonce(), getShortStackTrace()); - - DefaultModelCriteria mcb = criteria(); - mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getUserId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_ID, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getActionId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getActionVerificationNonce().toString()); - - ActionTokenValueModel existing = actionTokenStoreTx.read(withCriteria(mcb)) - .findFirst().map(this::singleUseEntityToAdapter).orElse(null); - - if (existing == null) { - MapSingleUseObjectEntity actionTokenStoreEntity = new MapSingleUseObjectEntityImpl(); - actionTokenStoreEntity.setUserId(actionTokenKey.getUserId()); - actionTokenStoreEntity.setActionId(actionTokenKey.getActionId()); - actionTokenStoreEntity.setActionVerificationNonce(actionTokenKey.getActionVerificationNonce().toString()); - actionTokenStoreEntity.setExpiration(TimeAdapter.fromSecondsToMilliseconds(actionTokenKey.getExpiration())); - actionTokenStoreEntity.setNotes(notes); - - LOG.debugf("Adding used action token to actionTokens cache: %s", actionTokenKey.toString()); - - actionTokenStoreTx.create(actionTokenStoreEntity); - } - } - - @Override - public ActionTokenValueModel get(ActionTokenKeyModel key) { - if (key == null || key.getUserId() == null || key.getActionId() == null || key.getActionVerificationNonce() == null) { - return null; - } - - LOG.tracef("get(%s, %s, %s)%s", key.getUserId(), key.getActionId(), key.getActionVerificationNonce(), getShortStackTrace()); - - DefaultModelCriteria mcb = criteria(); - mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, key.getUserId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_ID, ModelCriteriaBuilder.Operator.EQ, key.getActionId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString()); - - return actionTokenStoreTx.read(withCriteria(mcb)) - .findFirst().map(this::singleUseEntityToAdapter).orElse(null); - } - - @Override - public ActionTokenValueModel remove(ActionTokenKeyModel key) { - if (key == null || key.getUserId() == null || key.getActionId() == null || key.getActionVerificationNonce() == null) { - return null; - } - - LOG.tracef("remove(%s, %s, %s)%s", key.getUserId(), key.getActionId(), key.getActionVerificationNonce(), getShortStackTrace()); - - DefaultModelCriteria mcb = criteria(); - mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, key.getUserId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_ID, ModelCriteriaBuilder.Operator.EQ, key.getActionId()) - .compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString()); - MapSingleUseObjectEntity mapSingleUseObjectEntity = actionTokenStoreTx.read(withCriteria(mcb)).findFirst().orElse(null); - if (mapSingleUseObjectEntity != null) { - ActionTokenValueModel actionToken = singleUseEntityToAdapter(mapSingleUseObjectEntity); - if (actionToken != null) { - actionTokenStoreTx.delete(mapSingleUseObjectEntity.getId()); - return actionToken; - } - } - - return null; + session.getTransactionManager().enlistAfterCompletion(singleUseObjectTx); } @Override @@ -150,16 +68,16 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin singleUseEntity.setExpiration(Time.currentTimeMillis() + TimeAdapter.fromSecondsToMilliseconds(lifespanSeconds)); singleUseEntity.setNotes(notes); - actionTokenStoreTx.create(singleUseEntity); + singleUseObjectTx.create(singleUseEntity); } @Override public Map get(String key) { LOG.tracef("get(%s)%s", key, getShortStackTrace()); - MapSingleUseObjectEntity actionToken = getWithExpiration(key); - if (actionToken != null) { - Map notes = actionToken.getNotes(); + MapSingleUseObjectEntity singleUseObject = getWithExpiration(key); + if (singleUseObject != null) { + Map notes = singleUseObject.getNotes(); return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes); } @@ -174,7 +92,7 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin if (singleUseEntity != null) { Map notes = singleUseEntity.getNotes(); - if (actionTokenStoreTx.delete(singleUseEntity.getId())) { + if (singleUseObjectTx.delete(singleUseEntity.getId())) { return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes); } } @@ -207,7 +125,7 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin singleUseEntity.setObjectKey(key); singleUseEntity.setExpiration(Time.currentTimeMillis() + TimeAdapter.fromSecondsToMilliseconds(lifespanInSeconds)); - actionTokenStoreTx.create(singleUseEntity); + singleUseObjectTx.create(singleUseEntity); return true; } @@ -217,9 +135,9 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin public boolean contains(String key) { LOG.tracef("contains(%s)%s", key, getShortStackTrace()); - MapSingleUseObjectEntity actionToken = getWithExpiration(key); + MapSingleUseObjectEntity singleUseObject = getWithExpiration(key); - return actionToken != null; + return singleUseObject != null; } @Override @@ -228,13 +146,13 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin } private MapSingleUseObjectEntity getWithExpiration(String key) { - DefaultModelCriteria mcb = criteria(); - mcb = mcb.compare(ActionTokenValueModel.SearchableFields.OBJECT_KEY, ModelCriteriaBuilder.Operator.EQ, key); + DefaultModelCriteria mcb = criteria(); + mcb = mcb.compare(SingleUseObjectValueModel.SearchableFields.OBJECT_KEY, ModelCriteriaBuilder.Operator.EQ, key); - MapSingleUseObjectEntity singleUseEntity = actionTokenStoreTx.read(withCriteria(mcb)).findFirst().orElse(null); + MapSingleUseObjectEntity singleUseEntity = singleUseObjectTx.read(withCriteria(mcb)).findFirst().orElse(null); if (singleUseEntity != null) { if (isExpired(singleUseEntity, false)) { - actionTokenStoreTx.delete(singleUseEntity.getId()); + singleUseObjectTx.delete(singleUseEntity.getId()); } else { return singleUseEntity; } diff --git a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProviderFactory.java index f9ff465054..17f856e5e1 100644 --- a/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/singleUseObject/MapSingleUseObjectProviderFactory.java @@ -17,7 +17,7 @@ package org.keycloak.models.map.singleUseObject; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.SingleUseObjectProviderFactory; import org.keycloak.models.map.common.AbstractMapProviderFactory; @@ -25,11 +25,11 @@ import org.keycloak.models.map.common.AbstractMapProviderFactory; /** * @author Martin Kanis */ -public class MapSingleUseObjectProviderFactory extends AbstractMapProviderFactory +public class MapSingleUseObjectProviderFactory extends AbstractMapProviderFactory implements SingleUseObjectProviderFactory { public MapSingleUseObjectProviderFactory() { - super(ActionTokenValueModel.class, MapSingleUseObjectProvider.class); + super(SingleUseObjectValueModel.class, MapSingleUseObjectProvider.class); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/ModelEntityUtil.java b/model/map/src/main/java/org/keycloak/models/map/storage/ModelEntityUtil.java index 71cf6d4278..a9c27198fc 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/ModelEntityUtil.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/ModelEntityUtil.java @@ -22,7 +22,7 @@ import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.events.Event; import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; @@ -66,7 +66,7 @@ public class ModelEntityUtil { private static final Map, String> MODEL_TO_NAME = new HashMap<>(); static { - MODEL_TO_NAME.put(ActionTokenValueModel.class, "single-use-objects"); + MODEL_TO_NAME.put(SingleUseObjectValueModel.class, "single-use-objects"); MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes"); MODEL_TO_NAME.put(ClientModel.class, "clients"); MODEL_TO_NAME.put(GroupModel.class, "groups"); @@ -92,7 +92,7 @@ public class ModelEntityUtil { private static final Map, Class> MODEL_TO_ENTITY_TYPE = new HashMap<>(); static { - MODEL_TO_ENTITY_TYPE.put(ActionTokenValueModel.class, MapSingleUseObjectEntity.class); + MODEL_TO_ENTITY_TYPE.put(SingleUseObjectValueModel.class, MapSingleUseObjectEntity.class); MODEL_TO_ENTITY_TYPE.put(ClientScopeModel.class, MapClientScopeEntity.class); MODEL_TO_ENTITY_TYPE.put(ClientModel.class, MapClientEntity.class); MODEL_TO_ENTITY_TYPE.put(GroupModel.class, MapGroupEntity.class); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java index f8b96932c1..5d78b863e1 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java @@ -16,7 +16,7 @@ */ package org.keycloak.models.map.storage.chm; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.map.singleUseObject.MapSingleUseObjectEntity; import org.keycloak.models.map.authSession.MapAuthenticationSessionEntity; import org.keycloak.models.map.authSession.MapAuthenticationSessionEntityImpl; @@ -35,10 +35,8 @@ import org.keycloak.component.AmphibianProviderFactory; import org.keycloak.Config.Scope; import org.keycloak.common.Profile; import org.keycloak.component.ComponentModelScope; -import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.UserSessionModel; import org.keycloak.models.map.client.MapClientEntityImpl; import org.keycloak.models.map.client.MapProtocolMapperEntity; import org.keycloak.models.map.client.MapProtocolMapperEntityImpl; @@ -240,7 +238,7 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide LOG.debugf("Initializing new map storage: %s", mapName); ConcurrentHashMapStorage store; - if(modelType == ActionTokenValueModel.class) { + if(modelType == SingleUseObjectValueModel.class) { store = new SingleUseObjectConcurrentHashMapStorage(kc, CLONER) { @Override public String toString() { diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java index a2d84ef3e1..405cf1d57f 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java @@ -23,7 +23,7 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.events.Event; import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.GroupModel; @@ -98,7 +98,7 @@ public class MapFieldPredicates { public static final Map, UpdatePredicatesFunc> USER_SESSION_PREDICATES = basePredicates(UserSessionModel.SearchableFields.ID); public static final Map, UpdatePredicatesFunc> AUTH_EVENTS_PREDICATES = basePredicates(Event.SearchableFields.ID); public static final Map, UpdatePredicatesFunc> ADMIN_EVENTS_PREDICATES = basePredicates(AdminEvent.SearchableFields.ID); - public static final Map, UpdatePredicatesFunc> ACTION_TOKEN_PREDICATES = basePredicates(ActionTokenValueModel.SearchableFields.ID); + public static final Map, UpdatePredicatesFunc> ACTION_TOKEN_PREDICATES = basePredicates(SingleUseObjectValueModel.SearchableFields.ID); @SuppressWarnings("unchecked") private static final Map, Map> PREDICATES = new HashMap<>(); @@ -221,10 +221,7 @@ public class MapFieldPredicates { put(ADMIN_EVENTS_PREDICATES, AdminEvent.SearchableFields.RESOURCE_TYPE, MapAdminEventEntity::getResourceType); put(ADMIN_EVENTS_PREDICATES, AdminEvent.SearchableFields.RESOURCE_PATH, MapAdminEventEntity::getResourcePath); - put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.USER_ID, MapSingleUseObjectEntity::getUserId); - put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.ACTION_ID, MapSingleUseObjectEntity::getActionId); - put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, MapSingleUseObjectEntity::getActionVerificationNonce); - put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.OBJECT_KEY, MapSingleUseObjectEntity::getObjectKey); + put(ACTION_TOKEN_PREDICATES, SingleUseObjectValueModel.SearchableFields.OBJECT_KEY, MapSingleUseObjectEntity::getObjectKey); } static { @@ -244,7 +241,7 @@ public class MapFieldPredicates { PREDICATES.put(UserLoginFailureModel.class, USER_LOGIN_FAILURE_PREDICATES); PREDICATES.put(Event.class, AUTH_EVENTS_PREDICATES); PREDICATES.put(AdminEvent.class, ADMIN_EVENTS_PREDICATES); - PREDICATES.put(ActionTokenValueModel.class, ACTION_TOKEN_PREDICATES); + PREDICATES.put(SingleUseObjectValueModel.class, ACTION_TOKEN_PREDICATES); } private static > void put( diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectConcurrentHashMapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectConcurrentHashMapStorage.java index f4ea9169e9..95e5ecbae9 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectConcurrentHashMapStorage.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectConcurrentHashMapStorage.java @@ -17,7 +17,7 @@ package org.keycloak.models.map.storage.chm; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.DeepCloner; @@ -32,31 +32,28 @@ import java.util.stream.Stream; /** * @author Martin Kanis */ -public class SingleUseObjectConcurrentHashMapStorage extends ConcurrentHashMapStorage { +public class SingleUseObjectConcurrentHashMapStorage extends ConcurrentHashMapStorage { public SingleUseObjectConcurrentHashMapStorage(StringKeyConverter keyConverter, DeepCloner cloner) { - super(ActionTokenValueModel.class, keyConverter, cloner); + super(SingleUseObjectValueModel.class, keyConverter, cloner); } @Override @SuppressWarnings("unchecked") - public MapKeycloakTransaction createTransaction(KeycloakSession session) { - MapKeycloakTransaction actionTokenTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); + public MapKeycloakTransaction createTransaction(KeycloakSession session) { + MapKeycloakTransaction singleUseObjectTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); - if (actionTokenTransaction == null) { - actionTokenTransaction = new SingleUseObjectKeycloakTransaction(this, keyConverter, cloner, fieldPredicates); - session.setAttribute("map-transaction-" + hashCode(), actionTokenTransaction); + if (singleUseObjectTransaction == null) { + singleUseObjectTransaction = new SingleUseObjectKeycloakTransaction(this, keyConverter, cloner, fieldPredicates); + session.setAttribute("map-transaction-" + hashCode(), singleUseObjectTransaction); } - return actionTokenTransaction; + return singleUseObjectTransaction; } @Override public MapSingleUseObjectEntity create(MapSingleUseObjectEntity value) { if (value.getId() == null) { - if (value.getUserId() != null && value.getActionId() != null && value.getActionVerificationNonce() != null) { - value.setId(value.getUserId() + ":" + value.getActionId() + ":" + value.getActionVerificationNonce()); - } if (value.getObjectKey() != null) { value.setId(value.getObjectKey()); } @@ -65,8 +62,8 @@ public class SingleUseObjectConcurrentHashMapStorage read(QueryParameters queryParameters) { - DefaultModelCriteria criteria = queryParameters.getModelCriteriaBuilder(); + public Stream read(QueryParameters queryParameters) { + DefaultModelCriteria criteria = queryParameters.getModelCriteriaBuilder(); if (criteria == null) { return Stream.empty(); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectKeycloakTransaction.java index e6392ed545..408d1c87ee 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectKeycloakTransaction.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectKeycloakTransaction.java @@ -17,7 +17,7 @@ package org.keycloak.models.map.storage.chm; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.StringKeyConverter; import org.keycloak.models.map.singleUseObject.MapSingleUseObjectEntity; @@ -28,22 +28,19 @@ import java.util.Map; /** * @author Martin Kanis */ -public class SingleUseObjectKeycloakTransaction extends ConcurrentHashMapKeycloakTransaction { +public class SingleUseObjectKeycloakTransaction extends ConcurrentHashMapKeycloakTransaction { - public SingleUseObjectKeycloakTransaction(ConcurrentHashMapCrudOperations map, + public SingleUseObjectKeycloakTransaction(ConcurrentHashMapCrudOperations map, StringKeyConverter keyConverter, DeepCloner cloner, - Map, - MapModelCriteriaBuilder.UpdatePredicatesFunc> fieldPredicates) { + Map, + MapModelCriteriaBuilder.UpdatePredicatesFunc> fieldPredicates) { super(map, keyConverter, cloner, fieldPredicates); } @Override public MapSingleUseObjectEntity create(MapSingleUseObjectEntity value) { if (value.getId() == null) { - if (value.getUserId() != null && value.getActionId() != null && value.getActionVerificationNonce() != null) { - value.setId(value.getUserId() + ":" + value.getActionId() + ":" + value.getActionVerificationNonce()); - } if (value.getObjectKey() != null) { value.setId(value.getObjectKey()); } diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectModelCriteriaBuilder.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectModelCriteriaBuilder.java index 199df27882..9378dad7b3 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectModelCriteriaBuilder.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/SingleUseObjectModelCriteriaBuilder.java @@ -17,7 +17,7 @@ package org.keycloak.models.map.storage.chm; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.storage.SearchableModelField; @@ -26,61 +26,34 @@ import org.keycloak.storage.SearchableModelField; */ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder { - private String userId; - - private String actionId; - - private String actionVerificationNonce; - private String objectKey; public SingleUseObjectModelCriteriaBuilder() { } - public SingleUseObjectModelCriteriaBuilder(String userId, String actionId, String actionVerificationNonce, String objectKey) { - this.userId = userId; - this.actionId = actionId; - this.actionVerificationNonce = actionVerificationNonce; + public SingleUseObjectModelCriteriaBuilder(String objectKey) { this.objectKey = objectKey; } @Override public ModelCriteriaBuilder compare(SearchableModelField modelField, Operator op, Object... value) { - if (modelField == org.keycloak.models.ActionTokenValueModel.SearchableFields.USER_ID) { - userId = value[0].toString(); - } else if (modelField == org.keycloak.models.ActionTokenValueModel.SearchableFields.ACTION_ID) { - actionId = value[0].toString(); - } else if (modelField == org.keycloak.models.ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE) { - actionVerificationNonce = value[0].toString(); - } else if (modelField == ActionTokenValueModel.SearchableFields.OBJECT_KEY) { + if (modelField == SingleUseObjectValueModel.SearchableFields.OBJECT_KEY) { objectKey = value[0].toString(); } - return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce, objectKey); + return new SingleUseObjectModelCriteriaBuilder(objectKey); } @Override public ModelCriteriaBuilder and(ModelCriteriaBuilder[] builders) { - String userId = null; - String actionId = null; - String actionVerificationNonce = null; String objectKey = null; for (ModelCriteriaBuilder builder: builders) { SingleUseObjectModelCriteriaBuilder suoMcb = (SingleUseObjectModelCriteriaBuilder) builder; - if (suoMcb.userId != null) { - userId = suoMcb.userId; - } - if (suoMcb.actionId != null) { - actionId = suoMcb.actionId; - } - if (suoMcb.actionVerificationNonce != null) { - actionVerificationNonce = suoMcb.actionVerificationNonce; - } if (suoMcb.objectKey != null) { objectKey = suoMcb.objectKey; } } - return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce, objectKey); + return new SingleUseObjectModelCriteriaBuilder(objectKey); } @Override @@ -94,11 +67,10 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder } public boolean isValid() { - return (userId != null && actionId != null && actionVerificationNonce != null) || objectKey != null; + return objectKey != null; } public String getKey() { - if (objectKey != null) return objectKey; - return userId + ":" + actionId + ":" + actionVerificationNonce; + return objectKey; } } diff --git a/model/map/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory b/model/map/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory deleted file mode 100644 index 9aeb62a832..0000000000 --- a/model/map/src/main/resources/META-INF/services/org.keycloak.models.ActionTokenStoreProviderFactory +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2022 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. -# - -org.keycloak.models.map.singleUseObject.MapActionTokenProviderFactory - diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java index 7c5b3e296a..7e9d8fdba6 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/StorageOptions.java @@ -217,18 +217,6 @@ public class StorageOptions { .buildTime(true) .build(); - public static final Option STORAGE_ACTION_TOKEN_PROVIDER = new OptionBuilder<>("storage-action-token-provider", String.class) - .category(OptionCategory.STORAGE) - .hidden() - .buildTime(true) - .build(); - - public static final Option STORAGE_ACTION_TOKEN_STORE = new OptionBuilder<>("storage-area-action-token", StorageType.class) - .category(OptionCategory.STORAGE) - .description(descriptionForStorageAreas("action tokens")) - .buildTime(true) - .build(); - public static final Option STORAGE_DBLOCK = new OptionBuilder<>("storage-dblock", String.class) .category(OptionCategory.STORAGE) .hidden() diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java index f04107e827..59671587d2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/StoragePropertyMappers.java @@ -22,8 +22,6 @@ import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper. import java.util.Optional; -import org.jboss.logging.Logger; -import org.keycloak.common.util.SecretGenerator; import org.keycloak.config.StorageOptions; import org.keycloak.config.StorageOptions.StorageType; @@ -197,18 +195,6 @@ final class StoragePropertyMappers { .transformer(StoragePropertyMappers::resolveMapStorageProvider) .paramLabel("type") .build(), - fromOption(StorageOptions.STORAGE_ACTION_TOKEN_PROVIDER) - .to("kc.spi-action-token-provider") - .mapFrom("storage") - .transformer(StoragePropertyMappers::getCacheStorage) - .paramLabel("type") - .build(), - fromOption(StorageOptions.STORAGE_ACTION_TOKEN_STORE) - .to("kc.spi-action-token-map-storage-provider") - .mapFrom("storage") - .transformer(StoragePropertyMappers::resolveMapStorageProvider) - .paramLabel("type") - .build(), fromOption(StorageOptions.STORAGE_DBLOCK) .to("kc.spi-dblock-provider") .mapFrom("storage") diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt index 0f1df3d36f..2b35d521b8 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt @@ -32,9 +32,6 @@ Storage (Experimental): --storage Experimental: Sets the default storage mechanism for all areas. Possible values are: jpa, chm, hotrod. ---storage-area-action-token - Experimental: Sets a storage mechanism for action tokens. Possible values are: - jpa, chm, hotrod. --storage-area-auth-session Experimental: Sets a storage mechanism for authentication sessions. Possible values are: jpa, chm, hotrod. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt index f06de863eb..9c3d0a8bc9 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.windows.approved.txt @@ -32,9 +32,6 @@ Storage (Experimental): --storage Experimental: Sets the default storage mechanism for all areas. Possible values are: jpa, chm, hotrod. ---storage-area-action-token - Experimental: Sets a storage mechanism for action tokens. Possible values are: - jpa, chm, hotrod. --storage-area-auth-session Experimental: Sets a storage mechanism for authentication sessions. Possible values are: jpa, chm, hotrod. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt index 7835efc65c..98ccbd8c43 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt @@ -38,9 +38,6 @@ Storage (Experimental): --storage Experimental: Sets the default storage mechanism for all areas. Possible values are: jpa, chm, hotrod. ---storage-area-action-token - Experimental: Sets a storage mechanism for action tokens. Possible values are: - jpa, chm, hotrod. --storage-area-auth-session Experimental: Sets a storage mechanism for authentication sessions. Possible values are: jpa, chm, hotrod. diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.windows.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.windows.approved.txt index eaaae35054..8967791752 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.windows.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.windows.approved.txt @@ -38,9 +38,6 @@ Storage (Experimental): --storage Experimental: Sets the default storage mechanism for all areas. Possible values are: jpa, chm, hotrod. ---storage-area-action-token - Experimental: Sets a storage mechanism for action tokens. Possible values are: - jpa, chm, hotrod. --storage-area-auth-session Experimental: Sets a storage mechanism for authentication sessions. Possible values are: jpa, chm, hotrod. diff --git a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProvider.java b/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProvider.java deleted file mode 100644 index d71aa90870..0000000000 --- a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 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.models; - -import org.keycloak.provider.Provider; - -import java.util.Map; - -/** - * Internal action token store provider. - * - * It's used for store the details about used action tokens. There is separate provider for OAuth2 codes - {@link SingleUseObjectProvider}, - * which may reuse some components (eg. same infinispan cache) - * - * @author hmlnarik - */ -public interface ActionTokenStoreProvider extends Provider { - - /** - * Adds a given token to token store. - * @param actionTokenKey key - * @param notes Optional notes to be stored with the token. Can be {@code null} in which case it is treated as an empty map. - */ - void put(ActionTokenKeyModel actionTokenKey, Map notes); - - /** - * Returns token corresponding to the given key from the internal action token store - * @param key key - * @return {@code null} if no token is found for given key and nonce, value otherwise - */ - ActionTokenValueModel get(ActionTokenKeyModel key); - - /** - * Removes token corresponding to the given key from the internal action token store, and returns the stored value - * @param key key - * @param nonce nonce that must match a given key - * @return {@code null} if no token is found for given key and nonce, value otherwise - */ - ActionTokenValueModel remove(ActionTokenKeyModel key); -} diff --git a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProviderFactory.java deleted file mode 100644 index f8a6b6b054..0000000000 --- a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreProviderFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 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.models; - -import org.keycloak.provider.ProviderFactory; - -/** - * - * @author hmlnarik - */ -public interface ActionTokenStoreProviderFactory extends ProviderFactory { - -} diff --git a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreSpi.java b/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreSpi.java deleted file mode 100644 index 66ee518006..0000000000 --- a/server-spi-private/src/main/java/org/keycloak/models/ActionTokenStoreSpi.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 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.models; - -import org.keycloak.provider.*; - -/** - * SPI for action tokens. - * - * @author hmlnarik - */ -public class ActionTokenStoreSpi implements Spi { - - public static final String NAME = "actionToken"; - - @Override - public boolean isInternal() { - return true; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public Class getProviderClass() { - return ActionTokenStoreProvider.class; - } - - @Override - public Class getProviderFactoryClass() { - return ActionTokenStoreProviderFactory.class; - } - -} diff --git a/server-spi-private/src/main/java/org/keycloak/models/SingleUseObjectProvider.java b/server-spi-private/src/main/java/org/keycloak/models/SingleUseObjectProvider.java index 08a65245d0..cc21eef36e 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/SingleUseObjectProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/models/SingleUseObjectProvider.java @@ -22,7 +22,7 @@ import org.keycloak.provider.Provider; import java.util.Map; /** - * Provides a cache to store data for single-use use case. Data are represented by a {@code String} key. + * Provides a cache to store data for single-use use case or the details about used action tokens. * * @author Marek Posolda */ diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index 4fbe5fd28f..6ddd89006b 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -23,7 +23,6 @@ org.keycloak.models.GroupSpi org.keycloak.models.RealmSpi org.keycloak.models.RoleSpi org.keycloak.models.DeploymentStateSpi -org.keycloak.models.ActionTokenStoreSpi org.keycloak.models.OAuth2DeviceUserCodeSpi org.keycloak.models.SingleUseObjectSpi org.keycloak.models.UserSessionSpi diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java b/server-spi/src/main/java/org/keycloak/models/DefaultActionTokenKey.java similarity index 95% rename from services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java rename to server-spi/src/main/java/org/keycloak/models/DefaultActionTokenKey.java index 15c1aa655c..8af055c680 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java +++ b/server-spi/src/main/java/org/keycloak/models/DefaultActionTokenKey.java @@ -14,10 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.authentication.actiontoken; +package org.keycloak.models; import org.keycloak.common.util.Base64; -import org.keycloak.models.ActionTokenKeyModel; import org.keycloak.representations.JsonWebToken; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -30,7 +29,7 @@ import java.util.regex.Pattern; * * @author hmlnarik */ -public class DefaultActionTokenKey extends JsonWebToken implements ActionTokenKeyModel { +public class DefaultActionTokenKey extends JsonWebToken implements SingleUseObjectKeyModel { /** The authenticationSession note with ID of the user authenticated via the action token */ public static final String ACTION_TOKEN_USER_ID = "ACTION_TOKEN_USER"; diff --git a/server-spi/src/main/java/org/keycloak/models/ActionTokenKeyModel.java b/server-spi/src/main/java/org/keycloak/models/SingleUseObjectKeyModel.java similarity index 97% rename from server-spi/src/main/java/org/keycloak/models/ActionTokenKeyModel.java rename to server-spi/src/main/java/org/keycloak/models/SingleUseObjectKeyModel.java index 83f0e52e22..c297aa7183 100644 --- a/server-spi/src/main/java/org/keycloak/models/ActionTokenKeyModel.java +++ b/server-spi/src/main/java/org/keycloak/models/SingleUseObjectKeyModel.java @@ -24,7 +24,7 @@ import java.util.UUID; * * @author hmlnarik */ -public interface ActionTokenKeyModel { +public interface SingleUseObjectKeyModel { /** * @return ID of user which this token is for. diff --git a/server-spi/src/main/java/org/keycloak/models/ActionTokenValueModel.java b/server-spi/src/main/java/org/keycloak/models/SingleUseObjectValueModel.java similarity index 61% rename from server-spi/src/main/java/org/keycloak/models/ActionTokenValueModel.java rename to server-spi/src/main/java/org/keycloak/models/SingleUseObjectValueModel.java index a853cb1404..d452c72750 100644 --- a/server-spi/src/main/java/org/keycloak/models/ActionTokenValueModel.java +++ b/server-spi/src/main/java/org/keycloak/models/SingleUseObjectValueModel.java @@ -24,14 +24,11 @@ import java.util.Map; * This model represents contents of an action token shareable among Keycloak instances in the cluster. * @author hmlnarik */ -public interface ActionTokenValueModel { +public interface SingleUseObjectValueModel { class SearchableFields { - public static final SearchableModelField ID = new SearchableModelField<>("id", String.class); - public static final SearchableModelField OBJECT_KEY = new SearchableModelField<>("objectKey", String.class); - public static final SearchableModelField USER_ID = new SearchableModelField<>("userId", String.class); - public static final SearchableModelField ACTION_ID = new SearchableModelField<>("actionId", String.class); - public static final SearchableModelField ACTION_VERIFICATION_NONCE = new SearchableModelField<>("actionVerificationNonce", String.class); + public static final SearchableModelField ID = new SearchableModelField<>("id", String.class); + public static final SearchableModelField OBJECT_KEY = new SearchableModelField<>("objectKey", String.class); } /** diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java index 17a01ee34d..b894e4cad1 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java @@ -22,7 +22,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.keycloak.TokenVerifier.Predicate; import org.keycloak.common.VerificationException; import org.keycloak.common.util.Time; -import org.keycloak.models.ActionTokenValueModel; +import org.keycloak.models.SingleUseObjectValueModel; +import org.keycloak.models.DefaultActionTokenKey; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.services.Urls; @@ -38,7 +39,7 @@ import java.util.UUID; * * @author hmlnarik */ -public class DefaultActionToken extends DefaultActionTokenKey implements ActionTokenValueModel { +public class DefaultActionToken extends DefaultActionTokenKey implements SingleUseObjectValueModel { public static final String JSON_FIELD_AUTHENTICATION_SESSION_ID = "asid"; public static final String JSON_FIELD_EMAIL = "eml"; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java index acaffc3f85..78604dee45 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialChooseUser.java @@ -17,7 +17,7 @@ package org.keycloak.authentication.authenticators.resetcred; -import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; +import org.keycloak.models.DefaultActionTokenKey; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.authentication.*; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java index abb2f0b627..c615d7836c 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java @@ -17,7 +17,7 @@ package org.keycloak.authentication.authenticators.resetcred; -import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; +import org.keycloak.models.DefaultActionTokenKey; import org.keycloak.Config; import org.keycloak.authentication.*; import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken; diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index f09f71603f..c7df8ed6ec 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -30,7 +30,6 @@ import org.keycloak.authentication.RequiredActionContext; import org.keycloak.authentication.RequiredActionContextResult; import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionProvider; -import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.common.ClientConnection; @@ -47,16 +46,17 @@ import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; import org.keycloak.forms.login.LoginFormsProvider; -import org.keycloak.models.ActionTokenKeyModel; -import org.keycloak.models.ActionTokenStoreProvider; +import org.keycloak.models.SingleUseObjectKeyModel; import org.keycloak.models.AuthenticatedClientSessionModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientSessionContext; import org.keycloak.models.Constants; +import org.keycloak.models.DefaultActionTokenKey; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RequiredActionProviderModel; +import org.keycloak.models.SingleUseObjectProvider; import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; @@ -1037,11 +1037,10 @@ public class AuthenticationManager { ClientConnection clientConnection, HttpRequest request, UriInfo uriInfo, EventBuilder event) { String actionTokenKeyToInvalidate = authSession.getAuthNote(INVALIDATE_ACTION_TOKEN); if (actionTokenKeyToInvalidate != null) { - ActionTokenKeyModel actionTokenKey = DefaultActionTokenKey.from(actionTokenKeyToInvalidate); - + SingleUseObjectKeyModel actionTokenKey = DefaultActionTokenKey.from(actionTokenKeyToInvalidate); if (actionTokenKey != null) { - ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class); - actionTokenStore.put(actionTokenKey, null); // Token is invalidated + SingleUseObjectProvider singleUseObjectProvider = session.getProvider(SingleUseObjectProvider.class); + singleUseObjectProvider.put(actionTokenKeyToInvalidate, actionTokenKey.getExpiration() - Time.currentTime(), null); // Token is invalidated } } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index b071535d8c..c991349f8c 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -29,7 +29,7 @@ import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.authentication.actiontoken.ActionTokenContext; import org.keycloak.authentication.actiontoken.ActionTokenHandler; -import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; +import org.keycloak.models.DefaultActionTokenKey; import org.keycloak.authentication.actiontoken.ExplainedTokenVerificationException; import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionTokenHandler; import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator; @@ -47,9 +47,7 @@ import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; import org.keycloak.exceptions.TokenNotActiveException; -import org.keycloak.locale.LocaleSelectorProvider; -import org.keycloak.locale.LocaleUpdaterProvider; -import org.keycloak.models.ActionTokenKeyModel; +import org.keycloak.models.SingleUseObjectKeyModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; @@ -79,7 +77,6 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.messages.Messages; -import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.util.AuthenticationFlowURLHelper; import org.keycloak.services.util.BrowserHistoryHelper; import org.keycloak.services.util.CacheControlUtil; @@ -461,7 +458,7 @@ public class LoginActionsService { return handleActionToken(key, execution, clientId, tabId); } - protected Response handleActionToken(String tokenString, String execution, String clientId, String tabId) { + protected Response handleActionToken(String tokenString, String execution, String clientId, String tabId) { T token; ActionTokenHandler handler; ActionTokenContext tokenContext; diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java index dce80dd806..d885860ec6 100644 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java @@ -24,12 +24,12 @@ import org.keycloak.authentication.actiontoken.ExplainedTokenVerificationExcepti import org.keycloak.common.VerificationException; import org.keycloak.events.Errors; import org.keycloak.forms.login.LoginFormsProvider; -import org.keycloak.models.ActionTokenKeyModel; -import org.keycloak.models.ActionTokenStoreProvider; +import org.keycloak.models.SingleUseObjectKeyModel; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.SingleUseObjectProvider; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.utils.RedirectUtils; @@ -165,7 +165,7 @@ public class LoginActionsServiceChecks { * Verifies whether the user given by ID both exists in the current realm. If yes, * it optionally also injects the user using the given function (e.g. into session context). */ - public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { + public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { try { checkIsUserValid(context.getSession(), context.getRealm(), token.getUserId(), context.getAuthenticationSession()::setAuthenticatedUser); } catch (ExplainedVerificationException ex) { @@ -299,10 +299,10 @@ public class LoginActionsServiceChecks { return true; } - public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { - ActionTokenStoreProvider actionTokenStore = context.getSession().getProvider(ActionTokenStoreProvider.class); + public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { + SingleUseObjectProvider singleUseObjectProvider = context.getSession().getProvider(SingleUseObjectProvider.class); - if (actionTokenStore.get(token) != null) { + if (singleUseObjectProvider.get(token.serializeKey()) != null) { throw new ExplainedTokenVerificationException(token, Errors.EXPIRED_CODE, Messages.EXPIRED_ACTION); } } diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index ac615e12c4..491c6f733b 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -811,7 +811,6 @@ map map map - map map map false @@ -865,7 +864,6 @@ jpa jpa jpa - jpa jpa jpa jpa diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index d6e11c55df..b48c374b3a 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -139,15 +139,6 @@ } }, - "actionToken": { - "provider": "${keycloak.actionToken.provider:infinispan}", - "map": { - "storage": { - "provider": "${keycloak.actionToken.map.storage.provider:concurrenthashmap}" - } - } - }, - "singleUseObject": { "provider": "${keycloak.singleUseObject.provider:infinispan}", "map": { diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index c12622ea4d..4d62d9f58a 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -1360,7 +1360,6 @@ hotrod hotrod hotrod - hotrod hotrod ${infinispan.version} ${keycloak.testsuite.start-hotrod-container} diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java index 48934178c2..34fcdc219f 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java @@ -20,7 +20,6 @@ import com.google.common.collect.ImmutableSet; import org.jboss.logging.Logger; import org.keycloak.authorization.store.StoreFactorySpi; import org.keycloak.events.EventStoreSpi; -import org.keycloak.models.ActionTokenStoreSpi; import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.SingleUseObjectSpi; import org.keycloak.models.UserLoginFailureSpi; @@ -81,7 +80,6 @@ public class HotRodMapStorage extends KeycloakModelParameters { @Override public void updateConfig(Config cf) { cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) - .spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) .spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) .spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Infinispan.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Infinispan.java index 6d32c97a92..40c4a0d39d 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Infinispan.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Infinispan.java @@ -22,13 +22,11 @@ import org.keycloak.connections.infinispan.InfinispanConnectionSpi; import org.keycloak.keys.PublicKeyStorageSpi; import org.keycloak.keys.infinispan.InfinispanCachePublicKeyProviderFactory; import org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProviderFactory; -import org.keycloak.models.ActionTokenStoreSpi; import org.keycloak.models.SingleUseObjectSpi; import org.keycloak.models.cache.authorization.CachedStoreFactorySpi; import org.keycloak.models.cache.infinispan.authorization.InfinispanCacheStoreFactoryProviderFactory; import org.keycloak.models.cache.CachePublicKeyProviderSpi; import org.keycloak.models.session.UserSessionPersisterSpi; -import org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory; import org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory; import org.keycloak.models.sessions.infinispan.InfinispanSingleUseObjectProviderFactory; import org.keycloak.models.sessions.infinispan.InfinispanUserLoginFailureProviderFactory; @@ -68,7 +66,6 @@ public class Infinispan extends KeycloakModelParameters { .add(InfinispanConnectionSpi.class) .add(StickySessionEncoderSpi.class) .add(UserSessionPersisterSpi.class) - .add(ActionTokenStoreSpi.class) .add(SingleUseObjectSpi.class) .add(PublicKeyStorageSpi.class) .add(CachePublicKeyProviderSpi.class) @@ -86,7 +83,6 @@ public class Infinispan extends KeycloakModelParameters { .add(InfinispanUserCacheProviderFactory.class) .add(InfinispanUserSessionProviderFactory.class) .add(InfinispanUserLoginFailureProviderFactory.class) - .add(InfinispanActionTokenStoreProviderFactory.class) .add(InfinispanSingleUseObjectProviderFactory.class) .add(StickySessionEncoderProviderFactory.class) .add(TimerProviderFactory.class) diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java index 6704843cf8..8171a9e70a 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorage.java @@ -20,7 +20,6 @@ import com.google.common.collect.ImmutableSet; import java.util.Set; import org.jboss.logging.Logger; import org.keycloak.authorization.store.StoreFactorySpi; -import org.keycloak.models.ActionTokenStoreSpi; import org.keycloak.events.EventStoreSpi; import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.SingleUseObjectSpi; @@ -99,7 +98,6 @@ public class JpaMapStorage extends KeycloakModelParameters { .spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) - .spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) .spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LdapMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LdapMapStorage.java index 26f101f509..f5a6041d4b 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LdapMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/LdapMapStorage.java @@ -22,7 +22,6 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.keycloak.authorization.store.StoreFactorySpi; import org.keycloak.events.EventStoreSpi; -import org.keycloak.models.ActionTokenStoreSpi; import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.LDAPConstants; import org.keycloak.models.SingleUseObjectSpi; @@ -103,7 +102,6 @@ public class LdapMapStorage extends KeycloakModelParameters { .spi("authenticationSessions").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(EventStoreSpi.NAME).config("map.storage-admin-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(EventStoreSpi.NAME).config("map.storage-auth-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) - .spi(ActionTokenStoreSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi(SingleUseObjectSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) .spi("publicKeyStorage").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID); diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java index 241e3ffd54..169d80f061 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/Map.java @@ -19,8 +19,6 @@ package org.keycloak.testsuite.model.parameters; import org.keycloak.authorization.store.StoreFactorySpi; import org.keycloak.events.EventStoreSpi; import org.keycloak.keys.PublicKeyStorageSpi; -import org.keycloak.models.ActionTokenStoreProviderFactory; -import org.keycloak.models.ActionTokenStoreSpi; import org.keycloak.models.DeploymentStateSpi; import org.keycloak.models.SingleUseObjectProviderFactory; import org.keycloak.models.SingleUseObjectSpi; @@ -59,7 +57,6 @@ public class Map extends KeycloakModelParameters { static final Set> ALLOWED_SPIS = ImmutableSet.>builder() .add(AuthenticationSessionSpi.class) - .add(ActionTokenStoreSpi.class) .add(SingleUseObjectSpi.class) .add(PublicKeyStorageSpi.class) .add(MapStorageSpi.class) @@ -80,7 +77,6 @@ public class Map extends KeycloakModelParameters { .add(MapUserLoginFailureProviderFactory.class) .add(NoLockingDBLockProviderFactory.class) .add(MapEventStoreProviderFactory.class) - .add(ActionTokenStoreProviderFactory.class) .add(SingleUseObjectProviderFactory.class) .add(MapPublicKeyStorageProviderFactory.class) .build(); @@ -92,7 +88,6 @@ public class Map extends KeycloakModelParameters { @Override public void updateConfig(Config cf) { cf.spi(AuthenticationSessionSpi.PROVIDER_ID).defaultProvider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID) - .spi(ActionTokenStoreSpi.NAME).defaultProvider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .spi(SingleUseObjectSpi.NAME).defaultProvider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .spi("client").defaultProvider(MapClientProviderFactory.PROVIDER_ID) .spi("clientScope").defaultProvider(MapClientScopeProviderFactory.PROVIDER_ID) diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java index f8936d4acd..c80e46d57c 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/singleUseObject/SingleUseObjectModelTest.java @@ -20,26 +20,28 @@ package org.keycloak.testsuite.model.singleUseObject; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; -import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; +import org.keycloak.models.DefaultActionTokenKey; import org.keycloak.common.util.Time; -import org.keycloak.models.ActionTokenKeyModel; -import org.keycloak.models.ActionTokenStoreProvider; -import org.keycloak.models.ActionTokenValueModel; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.SingleUseObjectProvider; import org.keycloak.models.UserModel; +import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory; +import org.keycloak.models.map.userSession.MapUserSessionProviderFactory; import org.keycloak.testsuite.model.KeycloakModelTest; import org.keycloak.testsuite.model.RequireProvider; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assume.assumeFalse; -@RequireProvider(ActionTokenStoreProvider.class) @RequireProvider(SingleUseObjectProvider.class) public class SingleUseObjectModelTest extends KeycloakModelTest { @@ -64,46 +66,47 @@ public class SingleUseObjectModelTest extends KeycloakModelTest { @Test public void testActionTokens() { - ActionTokenKeyModel key = withRealm(realmId, (session, realm) -> { - ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class); - DefaultActionTokenKey actionTokenKey = new DefaultActionTokenKey(userId, UUID.randomUUID().toString(), Time.currentTime() + 60, null); + DefaultActionTokenKey key = withRealm(realmId, (session, realm) -> { + SingleUseObjectProvider singleUseObjectProvider = session.getProvider(SingleUseObjectProvider.class); + int time = Time.currentTime(); + DefaultActionTokenKey actionTokenKey = new DefaultActionTokenKey(userId, UUID.randomUUID().toString(), time + 60, null); Map notes = new HashMap<>(); notes.put("foo", "bar"); - actionTokenStore.put(actionTokenKey, notes); + singleUseObjectProvider.put(actionTokenKey.serializeKey(), actionTokenKey.getExp() - time, notes); return actionTokenKey; }); inComittedTransaction(session -> { - ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class); - ActionTokenValueModel valueModel = actionTokenStore.get(key); - Assert.assertNotNull(valueModel); - Assert.assertEquals("bar", valueModel.getNote("foo")); + SingleUseObjectProvider singleUseObjectProvider = session.getProvider(SingleUseObjectProvider.class); + Map notes = singleUseObjectProvider.get(key.serializeKey()); + Assert.assertNotNull(notes); + Assert.assertEquals("bar", notes.get("foo")); - valueModel = actionTokenStore.remove(key); - Assert.assertNotNull(valueModel); - Assert.assertEquals("bar", valueModel.getNote("foo")); + notes = singleUseObjectProvider.remove(key.serializeKey()); + Assert.assertNotNull(notes); + Assert.assertEquals("bar", notes.get("foo")); }); inComittedTransaction(session -> { - ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class); - ActionTokenValueModel valueModel = actionTokenStore.get(key); - Assert.assertNull(valueModel); + SingleUseObjectProvider singleUseObjectProvider = session.getProvider(SingleUseObjectProvider.class); + Map notes = singleUseObjectProvider.get(key.serializeKey()); + Assert.assertNull(notes); - Map notes = new HashMap<>(); + notes = new HashMap<>(); notes.put("foo", "bar"); - actionTokenStore.put(key, notes); + singleUseObjectProvider.put(key.serializeKey(), key.getExp() - Time.currentTime(), notes); }); inComittedTransaction(session -> { - ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class); - ActionTokenValueModel valueModel = actionTokenStore.get(key); - Assert.assertNotNull(valueModel); - Assert.assertEquals("bar", valueModel.getNote("foo")); + SingleUseObjectProvider singleUseObjectProvider = session.getProvider(SingleUseObjectProvider.class); + Map notes = singleUseObjectProvider.get(key.serializeKey()); + Assert.assertNotNull(notes); + Assert.assertEquals("bar", notes.get("foo")); Time.setOffset(70); - valueModel = actionTokenStore.get(key); - Assert.assertNull(valueModel); + notes = singleUseObjectProvider.get(key.serializeKey()); + Assert.assertNull(notes); }); } @@ -156,4 +159,89 @@ public class SingleUseObjectModelTest extends KeycloakModelTest { Assert.assertNull(singleUseStore.get(key)); }); } + + @Test + public void testCluster() throws InterruptedException { + // Skip the test if MapUserSessionProvider == CHM + String usProvider = CONFIG.getConfig().get("userSessions.provider"); + String usMapStorageProvider = CONFIG.getConfig().get("userSessions.map.storage.provider"); + assumeFalse(MapUserSessionProviderFactory.PROVIDER_ID.equals(usProvider) && + (usMapStorageProvider == null || ConcurrentHashMapStorageProviderFactory.PROVIDER_ID.equals(usMapStorageProvider))); + + AtomicInteger index = new AtomicInteger(); + CountDownLatch afterFirstNodeLatch = new CountDownLatch(1); + CountDownLatch afterDeleteLatch = new CountDownLatch(1); + CountDownLatch clusterJoined = new CountDownLatch(4); + CountDownLatch replicationDone = new CountDownLatch(4); + + String key = UUID.randomUUID().toString(); + AtomicReference actionTokenKey = new AtomicReference<>(); + Map notes = new HashMap<>(); + notes.put("foo", "bar"); + + inIndependentFactories(4, 60, () -> { + log.debug("Joining the cluster"); + clusterJoined.countDown(); + awaitLatch(clusterJoined); + log.debug("Cluster joined"); + + if (index.incrementAndGet() == 1) { + actionTokenKey.set(withRealm(realmId, (session, realm) -> { + SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); + singleUseStore.put(key, 60, notes); + + int time = Time.currentTime(); + DefaultActionTokenKey atk = new DefaultActionTokenKey(userId, UUID.randomUUID().toString(), time + 60, null); + singleUseStore.put(atk.serializeKey(), atk.getExp() - time, notes); + + return atk.serializeKey(); + })); + + afterFirstNodeLatch.countDown(); + } + awaitLatch(afterFirstNodeLatch); + + // check if single-use object/action token is available on all nodes + inComittedTransaction(session -> { + SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); + while (singleUseStore.get(key) == null || singleUseStore.get(actionTokenKey.get()) == null) { + sleep(1000); + } + replicationDone.countDown(); + }); + + awaitLatch(replicationDone); + + // remove objects on one node + if (index.incrementAndGet() == 5) { + inComittedTransaction(session -> { + SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); + singleUseStore.remove(key); + singleUseStore.remove(actionTokenKey.get()); + }); + + afterDeleteLatch.countDown(); + } + + awaitLatch(afterDeleteLatch); + + // check if single-use object/action token is removed + inComittedTransaction(session -> { + SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); + + while (singleUseStore.get(key) != null && singleUseStore.get(actionTokenKey.get()) != null) { + sleep(1000); + } + }); + }); + } + + private void awaitLatch(CountDownLatch latch) { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } } diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index ec416a9d55..f95fd4dbad 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -327,7 +327,6 @@ keycloak.userSession.providermap keycloak.loginFailure.providermap keycloak.authorization.providermap - keycloak.actionToken.providermap keycloak.singleUseObject.providermap keycloak.eventsStore.providermap keycloak.publicKeyStorage.providermap diff --git a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json index e933be53d0..0b042e8ebd 100755 --- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json @@ -111,15 +111,6 @@ } }, - "actionToken": { - "provider": "${keycloak.actionToken.provider:infinispan}", - "map": { - "storage": { - "provider": "${keycloak.actionToken.map.storage.provider:concurrenthashmap}" - } - } - }, - "singleUseObject": { "provider": "${keycloak.singleUseObject.provider:infinispan}", "map": {