parent
2ba38d9fdc
commit
22f9b0fee3
13 changed files with 96 additions and 37 deletions
|
@ -19,7 +19,6 @@ package org.keycloak.models.map.storage.hotRod.realm;
|
||||||
|
|
||||||
import org.infinispan.protostream.annotations.ProtoDoc;
|
import org.infinispan.protostream.annotations.ProtoDoc;
|
||||||
import org.infinispan.protostream.annotations.ProtoField;
|
import org.infinispan.protostream.annotations.ProtoField;
|
||||||
import org.keycloak.common.util.Time;
|
|
||||||
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
|
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
|
||||||
import org.keycloak.models.map.common.UpdatableEntity;
|
import org.keycloak.models.map.common.UpdatableEntity;
|
||||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||||
|
@ -64,6 +63,8 @@ import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
|
|
||||||
@GenerateHotRodEntityImplementation(
|
@GenerateHotRodEntityImplementation(
|
||||||
implementInterface = "org.keycloak.models.map.realm.MapRealmEntity",
|
implementInterface = "org.keycloak.models.map.realm.MapRealmEntity",
|
||||||
inherits = "org.keycloak.models.map.storage.hotRod.realm.HotRodRealmEntity.AbstractHotRodRealmEntityDelegate"
|
inherits = "org.keycloak.models.map.storage.hotRod.realm.HotRodRealmEntity.AbstractHotRodRealmEntityDelegate"
|
||||||
|
@ -424,8 +425,7 @@ public class HotRodRealmEntity extends AbstractHotRodEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfExpired(MapClientInitialAccessEntity cia) {
|
private boolean checkIfExpired(MapClientInitialAccessEntity cia) {
|
||||||
return cia.getRemainingCount() < 1 ||
|
return cia.getRemainingCount() < 1 || isExpired(cia, true);
|
||||||
(cia.getExpiration() != null && cia.getExpiration() < Time.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||||
import org.keycloak.models.map.storage.MapStorage;
|
import org.keycloak.models.map.storage.MapStorage;
|
||||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||||
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
||||||
import org.keycloak.models.utils.SessionExpiration;
|
|
||||||
import org.keycloak.sessions.AuthenticationSessionCompoundId;
|
import org.keycloak.sessions.AuthenticationSessionCompoundId;
|
||||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
|
@ -41,6 +40,7 @@ import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
||||||
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
||||||
import static org.keycloak.models.utils.SessionExpiration.getAuthSessionLifespan;
|
import static org.keycloak.models.utils.SessionExpiration.getAuthSessionLifespan;
|
||||||
|
@ -65,13 +65,11 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
||||||
|
|
||||||
private Function<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) {
|
private Function<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) {
|
||||||
return origEntity -> {
|
return origEntity -> {
|
||||||
//return new MapRootAuthenticationSessionAdapter(session, realm, origEntity);
|
if (isExpired(origEntity, true)) {
|
||||||
Long expiration = origEntity.getExpiration();
|
|
||||||
if (expiration == null || Time.currentTimeMillis() < origEntity.getExpiration()) {
|
|
||||||
return new MapRootAuthenticationSessionAdapter(session, realm, origEntity);
|
|
||||||
} else {
|
|
||||||
tx.delete(origEntity.getId());
|
tx.delete(origEntity.getId());
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
return new MapRootAuthenticationSessionAdapter(session, realm, origEntity);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ public interface ExpirableEntity extends AbstractEntity {
|
||||||
/**
|
/**
|
||||||
* Returns a point in the time (timestamp in milliseconds since The Epoch) when this entity expires.
|
* Returns a point in the time (timestamp in milliseconds since The Epoch) when this entity expires.
|
||||||
*
|
*
|
||||||
* @return a timestamp in milliseconds since The Epoch or {@code null} if this entity never expires.
|
* @return a timestamp in milliseconds since The Epoch or {@code null} if this entity never expires
|
||||||
|
* or expiration is not known.
|
||||||
*/
|
*/
|
||||||
Long getExpiration();
|
Long getExpiration();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
|
||||||
|
public class ExpirationUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the {@code entity} is expired
|
||||||
|
*
|
||||||
|
* @param entity to check
|
||||||
|
* @param allowInfiniteValues sets how null values are interpreted, if true entity with expiration equal
|
||||||
|
* to {@code null} is interpreted as never expiring entity, if false entities
|
||||||
|
* with {@code null} expiration are interpreted as expired entities
|
||||||
|
* @return true if the {@code entity} is expired (expiration time is in the past or now), false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isExpired(ExpirableEntity entity, boolean allowInfiniteValues) {
|
||||||
|
Long expiration = entity.getExpiration();
|
||||||
|
if (!allowInfiniteValues && expiration == null) return false;
|
||||||
|
return expiration != null && expiration <= Time.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
import static org.keycloak.models.map.events.EventUtils.modelToEntity;
|
import static org.keycloak.models.map.events.EventUtils.modelToEntity;
|
||||||
|
|
||||||
public class MapEventStoreProvider implements EventStoreProvider {
|
public class MapEventStoreProvider implements EventStoreProvider {
|
||||||
|
@ -79,9 +80,8 @@ public class MapEventStoreProvider implements EventStoreProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filterExpired(ExpirableEntity event) {
|
private boolean filterExpired(ExpirableEntity event) {
|
||||||
Long expiration = event.getExpiration();
|
|
||||||
// Check if entity is expired
|
// Check if entity is expired
|
||||||
if (expiration != null && expiration <= Time.currentTimeMillis()) {
|
if (isExpired(event, true)) {
|
||||||
// Remove entity
|
// Remove entity
|
||||||
authEventsTX.delete(event.getId());
|
authEventsTX.delete(event.getId());
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.models.map.realm;
|
package org.keycloak.models.map.realm;
|
||||||
|
|
||||||
import org.keycloak.common.util.Time;
|
|
||||||
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
||||||
import org.keycloak.models.map.annotations.IgnoreForEntityImplementationGenerator;
|
import org.keycloak.models.map.annotations.IgnoreForEntityImplementationGenerator;
|
||||||
import org.keycloak.models.map.common.AbstractEntity;
|
import org.keycloak.models.map.common.AbstractEntity;
|
||||||
|
@ -43,6 +42,8 @@ import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
|
|
||||||
@GenerateEntityImplementations(
|
@GenerateEntityImplementations(
|
||||||
inherits = "org.keycloak.models.map.realm.MapRealmEntity.AbstractRealmEntity"
|
inherits = "org.keycloak.models.map.realm.MapRealmEntity.AbstractRealmEntity"
|
||||||
)
|
)
|
||||||
|
@ -237,8 +238,7 @@ public interface MapRealmEntity extends UpdatableEntity, AbstractEntity, EntityW
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfExpired(MapClientInitialAccessEntity cia) {
|
private boolean checkIfExpired(MapClientInitialAccessEntity cia) {
|
||||||
return cia.getRemainingCount() < 1 ||
|
return cia.getRemainingCount() < 1 || isExpired(cia, true);
|
||||||
(cia.getExpiration() != null && cia.getExpiration() < Time.currentTimeMillis());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class MapSingleUseObjectAdapter extends AbstractSingleUseObjectModel<MapS
|
||||||
@Override
|
@Override
|
||||||
public int getExpiration() {
|
public int getExpiration() {
|
||||||
Long expiration = entity.getExpiration();
|
Long expiration = entity.getExpiration();
|
||||||
return expiration != null ? TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(expiration) : 0;
|
return expiration != null ? TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(TimeAdapter.fromMilliSecondsToSeconds(expiration)) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
||||||
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
||||||
|
|
||||||
|
@ -55,12 +56,11 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionTokenValueModel singleUseEntityToAdapter(MapSingleUseObjectEntity origEntity) {
|
private ActionTokenValueModel singleUseEntityToAdapter(MapSingleUseObjectEntity origEntity) {
|
||||||
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0L;
|
if (isExpired(origEntity, false)) {
|
||||||
if (Time.currentTime() < expiration) {
|
|
||||||
return new MapSingleUseObjectAdapter(session, origEntity);
|
|
||||||
} else {
|
|
||||||
actionTokenStoreTx.delete(origEntity.getId());
|
actionTokenStoreTx.delete(origEntity.getId());
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
return new MapSingleUseObjectAdapter(session, origEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,8 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
||||||
mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getUserId())
|
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_ID, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getActionId())
|
||||||
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getActionVerificationNonce().toString());
|
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, actionTokenKey.getActionVerificationNonce().toString())
|
||||||
|
.compare(ActionTokenValueModel.SearchableFields.EXPIRATION, ModelCriteriaBuilder.Operator.GT, Time.currentTimeMillis());
|
||||||
|
|
||||||
ActionTokenValueModel existing = actionTokenStoreTx.read(withCriteria(mcb))
|
ActionTokenValueModel existing = actionTokenStoreTx.read(withCriteria(mcb))
|
||||||
.findFirst().map(this::singleUseEntityToAdapter).orElse(null);
|
.findFirst().map(this::singleUseEntityToAdapter).orElse(null);
|
||||||
|
@ -85,7 +86,7 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
actionTokenStoreEntity.setUserId(actionTokenKey.getUserId());
|
actionTokenStoreEntity.setUserId(actionTokenKey.getUserId());
|
||||||
actionTokenStoreEntity.setActionId(actionTokenKey.getActionId());
|
actionTokenStoreEntity.setActionId(actionTokenKey.getActionId());
|
||||||
actionTokenStoreEntity.setActionVerificationNonce(actionTokenKey.getActionVerificationNonce().toString());
|
actionTokenStoreEntity.setActionVerificationNonce(actionTokenKey.getActionVerificationNonce().toString());
|
||||||
actionTokenStoreEntity.setExpiration(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(actionTokenKey.getExpiration()));
|
actionTokenStoreEntity.setExpiration(TimeAdapter.fromSecondsToMilliseconds(actionTokenKey.getExpiration()));
|
||||||
actionTokenStoreEntity.setNotes(notes);
|
actionTokenStoreEntity.setNotes(notes);
|
||||||
|
|
||||||
LOG.debugf("Adding used action token to actionTokens cache: %s", actionTokenKey.toString());
|
LOG.debugf("Adding used action token to actionTokens cache: %s", actionTokenKey.toString());
|
||||||
|
@ -105,7 +106,8 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
||||||
mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, key.getUserId())
|
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_ID, ModelCriteriaBuilder.Operator.EQ, key.getActionId())
|
||||||
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString());
|
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString())
|
||||||
|
.compare(ActionTokenValueModel.SearchableFields.EXPIRATION, ModelCriteriaBuilder.Operator.GT, Time.currentTimeMillis());
|
||||||
|
|
||||||
return actionTokenStoreTx.read(withCriteria(mcb))
|
return actionTokenStoreTx.read(withCriteria(mcb))
|
||||||
.findFirst().map(this::singleUseEntityToAdapter).orElse(null);
|
.findFirst().map(this::singleUseEntityToAdapter).orElse(null);
|
||||||
|
@ -122,7 +124,8 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
DefaultModelCriteria<ActionTokenValueModel> mcb = criteria();
|
||||||
mcb = mcb.compare(ActionTokenValueModel.SearchableFields.USER_ID, ModelCriteriaBuilder.Operator.EQ, key.getUserId())
|
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_ID, ModelCriteriaBuilder.Operator.EQ, key.getActionId())
|
||||||
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString());
|
.compare(ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, ModelCriteriaBuilder.Operator.EQ, key.getActionVerificationNonce().toString())
|
||||||
|
.compare(ActionTokenValueModel.SearchableFields.EXPIRATION, ModelCriteriaBuilder.Operator.GT, Time.currentTimeMillis());
|
||||||
|
|
||||||
MapSingleUseObjectEntity mapSingleUseObjectEntity = actionTokenStoreTx.read(withCriteria(mcb)).findFirst().orElse(null);
|
MapSingleUseObjectEntity mapSingleUseObjectEntity = actionTokenStoreTx.read(withCriteria(mcb)).findFirst().orElse(null);
|
||||||
if (mapSingleUseObjectEntity != null) {
|
if (mapSingleUseObjectEntity != null) {
|
||||||
|
@ -148,7 +151,7 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
|
|
||||||
singleUseEntity = new MapSingleUseObjectEntityImpl();
|
singleUseEntity = new MapSingleUseObjectEntityImpl();
|
||||||
singleUseEntity.setId(key);
|
singleUseEntity.setId(key);
|
||||||
singleUseEntity.setExpiration((long) Time.currentTime() + lifespanSeconds);
|
singleUseEntity.setExpiration(Time.currentTimeMillis() + TimeAdapter.fromSecondsToMilliseconds(lifespanSeconds));
|
||||||
singleUseEntity.setNotes(notes);
|
singleUseEntity.setNotes(notes);
|
||||||
|
|
||||||
actionTokenStoreTx.create(singleUseEntity);
|
actionTokenStoreTx.create(singleUseEntity);
|
||||||
|
@ -206,7 +209,7 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
} else {
|
} else {
|
||||||
singleUseEntity = new MapSingleUseObjectEntityImpl();
|
singleUseEntity = new MapSingleUseObjectEntityImpl();
|
||||||
singleUseEntity.setId(key);
|
singleUseEntity.setId(key);
|
||||||
singleUseEntity.setExpiration((long) Time.currentTime() + lifespanInSeconds);
|
singleUseEntity.setExpiration(Time.currentTimeMillis() + TimeAdapter.fromSecondsToMilliseconds(lifespanInSeconds));
|
||||||
|
|
||||||
actionTokenStoreTx.create(singleUseEntity);
|
actionTokenStoreTx.create(singleUseEntity);
|
||||||
|
|
||||||
|
@ -231,11 +234,11 @@ public class MapSingleUseObjectProvider implements ActionTokenStoreProvider, Sin
|
||||||
private MapSingleUseObjectEntity getWithExpiration(String key) {
|
private MapSingleUseObjectEntity getWithExpiration(String key) {
|
||||||
MapSingleUseObjectEntity singleUseEntity = actionTokenStoreTx.read(key);
|
MapSingleUseObjectEntity singleUseEntity = actionTokenStoreTx.read(key);
|
||||||
if (singleUseEntity != null) {
|
if (singleUseEntity != null) {
|
||||||
long expiration = singleUseEntity.getExpiration() != null ? singleUseEntity.getExpiration() : 0L;
|
if (isExpired(singleUseEntity, false)) {
|
||||||
if (Time.currentTime() < expiration) {
|
actionTokenStoreTx.delete(key);
|
||||||
|
} else {
|
||||||
return singleUseEntity;
|
return singleUseEntity;
|
||||||
}
|
}
|
||||||
actionTokenStoreTx.delete(key);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,6 +235,7 @@ public class MapFieldPredicates {
|
||||||
put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.USER_ID, MapSingleUseObjectEntity::getUserId);
|
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_ID, MapSingleUseObjectEntity::getActionId);
|
||||||
put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, MapSingleUseObjectEntity::getActionVerificationNonce);
|
put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE, MapSingleUseObjectEntity::getActionVerificationNonce);
|
||||||
|
put(ACTION_TOKEN_PREDICATES, ActionTokenValueModel.SearchableFields.EXPIRATION, MapSingleUseObjectEntity::getExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -29,6 +29,8 @@ import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||||
*/
|
*/
|
||||||
|
@ -72,7 +74,8 @@ public class SingleUseObjectConcurrentHashMapStorage<K, V extends AbstractEntity
|
||||||
SingleUseObjectModelCriteriaBuilder mcb = criteria.flashToModelCriteriaBuilder(createSingleUseObjectCriteriaBuilder());
|
SingleUseObjectModelCriteriaBuilder mcb = criteria.flashToModelCriteriaBuilder(createSingleUseObjectCriteriaBuilder());
|
||||||
if (mcb.isValid()) {
|
if (mcb.isValid()) {
|
||||||
MapSingleUseObjectEntity value = read(mcb.getKey());
|
MapSingleUseObjectEntity value = read(mcb.getKey());
|
||||||
return value != null ? Stream.of(value) : Stream.empty();
|
if (value == null || (mcb.checkExpiration() && isExpired(value, false))) return Stream.empty();
|
||||||
|
return Stream.of(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.read(queryParameters);
|
return super.read(queryParameters);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models.map.storage.chm;
|
package org.keycloak.models.map.storage.chm;
|
||||||
|
|
||||||
|
import org.keycloak.models.ActionTokenValueModel;
|
||||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||||
import org.keycloak.storage.SearchableModelField;
|
import org.keycloak.storage.SearchableModelField;
|
||||||
|
|
||||||
|
@ -31,13 +32,16 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder
|
||||||
|
|
||||||
private String actionVerificationNonce;
|
private String actionVerificationNonce;
|
||||||
|
|
||||||
|
private Boolean checkExpiration;
|
||||||
|
|
||||||
public SingleUseObjectModelCriteriaBuilder() {
|
public SingleUseObjectModelCriteriaBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleUseObjectModelCriteriaBuilder(String userId, String actionId, String actionVerificationNonce) {
|
public SingleUseObjectModelCriteriaBuilder(String userId, String actionId, String actionVerificationNonce, Boolean checkExpiration) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.actionId = actionId;
|
this.actionId = actionId;
|
||||||
this.actionVerificationNonce = actionVerificationNonce;
|
this.actionVerificationNonce = actionVerificationNonce;
|
||||||
|
this.checkExpiration = checkExpiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,8 +52,11 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder
|
||||||
actionId = value[0].toString();
|
actionId = value[0].toString();
|
||||||
} else if (modelField == org.keycloak.models.ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE) {
|
} else if (modelField == org.keycloak.models.ActionTokenValueModel.SearchableFields.ACTION_VERIFICATION_NONCE) {
|
||||||
actionVerificationNonce = value[0].toString();
|
actionVerificationNonce = value[0].toString();
|
||||||
|
} else if (modelField == ActionTokenValueModel.SearchableFields.EXPIRATION && op == Operator.GT) {
|
||||||
|
checkExpiration = true;
|
||||||
}
|
}
|
||||||
return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce);
|
|
||||||
|
return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce, checkExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,6 +64,7 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder
|
||||||
String userId = null;
|
String userId = null;
|
||||||
String actionId = null;
|
String actionId = null;
|
||||||
String actionVerificationNonce = null;
|
String actionVerificationNonce = null;
|
||||||
|
Boolean checkExpiration = null;
|
||||||
|
|
||||||
for (ModelCriteriaBuilder builder: builders) {
|
for (ModelCriteriaBuilder builder: builders) {
|
||||||
SingleUseObjectModelCriteriaBuilder suoMcb = (SingleUseObjectModelCriteriaBuilder) builder;
|
SingleUseObjectModelCriteriaBuilder suoMcb = (SingleUseObjectModelCriteriaBuilder) builder;
|
||||||
|
@ -69,8 +77,11 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder
|
||||||
if (suoMcb.actionVerificationNonce != null) {
|
if (suoMcb.actionVerificationNonce != null) {
|
||||||
actionVerificationNonce = suoMcb.actionVerificationNonce;
|
actionVerificationNonce = suoMcb.actionVerificationNonce;
|
||||||
}
|
}
|
||||||
|
if (suoMcb.checkExpiration != null) {
|
||||||
|
checkExpiration = suoMcb.checkExpiration;
|
||||||
}
|
}
|
||||||
return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce);
|
}
|
||||||
|
return new SingleUseObjectModelCriteriaBuilder(userId, actionId, actionVerificationNonce, checkExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,6 +98,10 @@ public class SingleUseObjectModelCriteriaBuilder implements ModelCriteriaBuilder
|
||||||
return userId != null && actionId != null && actionVerificationNonce != null;
|
return userId != null && actionId != null && actionVerificationNonce != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkExpiration() {
|
||||||
|
return checkExpiration != null && checkExpiration;
|
||||||
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return userId + ":" + actionId + ":" + actionVerificationNonce;
|
return userId + ":" + actionId + ":" + actionVerificationNonce;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import java.util.stream.Stream;
|
||||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
||||||
import static org.keycloak.models.UserSessionModel.SessionPersistenceState.TRANSIENT;
|
import static org.keycloak.models.UserSessionModel.SessionPersistenceState.TRANSIENT;
|
||||||
|
import static org.keycloak.models.map.common.ExpirationUtils.isExpired;
|
||||||
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
import static org.keycloak.models.map.storage.QueryParameters.withCriteria;
|
||||||
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.criteria;
|
||||||
import static org.keycloak.models.map.userSession.SessionExpiration.setClientSessionExpiration;
|
import static org.keycloak.models.map.userSession.SessionExpiration.setClientSessionExpiration;
|
||||||
|
@ -87,8 +88,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
||||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||||
return (origEntity) -> {
|
return (origEntity) -> {
|
||||||
if (origEntity == null) return null;
|
if (origEntity == null) return null;
|
||||||
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0L;
|
if (isExpired(origEntity, false)) {
|
||||||
if (expiration <= Time.currentTimeMillis()) {
|
|
||||||
if (TRANSIENT == origEntity.getPersistenceState()) {
|
if (TRANSIENT == origEntity.getPersistenceState()) {
|
||||||
transientUserSessions.remove(origEntity.getId());
|
transientUserSessions.remove(origEntity.getId());
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,8 +119,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
||||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||||
return origEntity -> {
|
return origEntity -> {
|
||||||
if (origEntity == null) return null;
|
if (origEntity == null) return null;
|
||||||
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0L;
|
if (isExpired(origEntity, false)) {
|
||||||
if (expiration <= Time.currentTimeMillis()) {
|
|
||||||
userSession.removeAuthenticatedClientSessions(Arrays.asList(origEntity.getClientId()));
|
userSession.removeAuthenticatedClientSessions(Arrays.asList(origEntity.getClientId()));
|
||||||
// if a client session is found among transient ones we can skip call to store
|
// if a client session is found among transient ones we can skip call to store
|
||||||
if (transientClientSessions.remove(origEntity.getId()) == null) {
|
if (transientClientSessions.remove(origEntity.getId()) == null) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ public interface ActionTokenValueModel {
|
||||||
public static final SearchableModelField<ActionTokenValueModel> USER_ID = new SearchableModelField<>("userId", String.class);
|
public static final SearchableModelField<ActionTokenValueModel> USER_ID = new SearchableModelField<>("userId", String.class);
|
||||||
public static final SearchableModelField<ActionTokenValueModel> ACTION_ID = new SearchableModelField<>("actionId", String.class);
|
public static final SearchableModelField<ActionTokenValueModel> ACTION_ID = new SearchableModelField<>("actionId", String.class);
|
||||||
public static final SearchableModelField<ActionTokenValueModel> ACTION_VERIFICATION_NONCE = new SearchableModelField<>("actionVerificationNonce", String.class);
|
public static final SearchableModelField<ActionTokenValueModel> ACTION_VERIFICATION_NONCE = new SearchableModelField<>("actionVerificationNonce", String.class);
|
||||||
|
public static final SearchableModelField<ActionTokenValueModel> EXPIRATION = new SearchableModelField<>("expiration", Long.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue