KEYCLOAK-17696 Make MapStorageFactory amphibian
This commit is contained in:
parent
e46a5484c5
commit
6d97a573e6
107 changed files with 4834 additions and 4743 deletions
|
@ -30,6 +30,6 @@ embed-server
|
|||
/subsystem=keycloak-server/spi=userSessions:add(default-provider=map)
|
||||
|
||||
/subsystem=keycloak-server/spi=mapStorage:add(default-provider=concurrenthashmap)
|
||||
/subsystem=keycloak-server/spi=mapStorage/provider=concurrenthashmap:add(properties={dir="${jboss.server.data.dir}/map"},enabled=true)
|
||||
/subsystem=keycloak-server/spi=mapStorage/provider=concurrenthashmap:add(properties={dir="${jboss.server.data.dir}/map",keyType.realms=string,keyType.authz-resource-servers=string},enabled=true)
|
||||
|
||||
quit
|
|
@ -276,6 +276,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
.map(entity -> this.wrap(realm, entity, offline));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, String clientSessionId, boolean offline) {
|
||||
return getClientSession(userSession, client, clientSessionId == null ? null : UUID.fromString(clientSessionId), offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionAdapter getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) {
|
||||
AuthenticatedClientSessionEntity entity = getClientSessionEntity(clientSessionId, offline);
|
||||
|
|
|
@ -136,4 +136,9 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel {
|
|||
provider.getLoginFailuresTx().addTask(key, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,104 +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.map.authSession;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractRootAuthenticationSessionEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
private int timestamp;
|
||||
private Map<String, MapAuthenticationSessionEntity> authenticationSessions = new ConcurrentHashMap<>();
|
||||
|
||||
protected AbstractRootAuthenticationSessionEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractRootAuthenticationSessionEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.updated |= !Objects.equals(this.timestamp, timestamp);
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Map<String, MapAuthenticationSessionEntity> getAuthenticationSessions() {
|
||||
return authenticationSessions;
|
||||
}
|
||||
|
||||
public void setAuthenticationSessions(Map<String, MapAuthenticationSessionEntity> authenticationSessions) {
|
||||
this.updated |= !Objects.equals(this.authenticationSessions, authenticationSessions);
|
||||
this.authenticationSessions = authenticationSessions;
|
||||
}
|
||||
|
||||
public MapAuthenticationSessionEntity removeAuthenticationSession(String tabId) {
|
||||
MapAuthenticationSessionEntity entity = this.authenticationSessions.remove(tabId);
|
||||
this.updated |= entity != null;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void addAuthenticationSession(String tabId, MapAuthenticationSessionEntity entity) {
|
||||
this.updated |= !Objects.equals(this.authenticationSessions.put(tabId, entity), entity);
|
||||
}
|
||||
|
||||
public void clearAuthenticationSessions() {
|
||||
this.updated |= !this.authenticationSessions.isEmpty();
|
||||
this.authenticationSessions.clear();
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ public class MapAuthenticationSessionAdapter implements AuthenticationSessionMod
|
|||
private final KeycloakSession session;
|
||||
private final MapRootAuthenticationSessionAdapter parent;
|
||||
private final String tabId;
|
||||
private MapAuthenticationSessionEntity entity;
|
||||
private final MapAuthenticationSessionEntity entity;
|
||||
|
||||
public MapAuthenticationSessionAdapter(KeycloakSession session, MapRootAuthenticationSessionAdapter parent,
|
||||
String tabId, MapAuthenticationSessionEntity entity) {
|
||||
|
|
|
@ -81,6 +81,6 @@ public class MapAuthenticationSessionAuthNoteUpdateEvent implements ClusterEvent
|
|||
@Override
|
||||
public String toString() {
|
||||
return String.format("AuthenticationSessionAuthNoteUpdateEvent [ authSessionId=%s, tabId=%s, clientUUID=%s, authNotesFragment=%s ]",
|
||||
authSessionId, clientUUID, authNotesFragment);
|
||||
authSessionId, tabId, clientUUID, authNotesFragment);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,17 +31,12 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticationSessionModel<MapRootAuthenticationSessionEntity> {
|
||||
public abstract class MapRootAuthenticationSessionAdapter<K> extends AbstractRootAuthenticationSessionModel<MapRootAuthenticationSessionEntity<K>> {
|
||||
|
||||
public MapRootAuthenticationSessionAdapter(KeycloakSession session, RealmModel realm, MapRootAuthenticationSessionEntity entity) {
|
||||
public MapRootAuthenticationSessionAdapter(KeycloakSession session, RealmModel realm, MapRootAuthenticationSessionEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return session.realms().getRealm(entity.getRealmId());
|
||||
|
@ -102,9 +97,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
|
|||
public void removeAuthenticationSessionByTabId(String tabId) {
|
||||
if (entity.removeAuthenticationSession(tabId) != null) {
|
||||
if (entity.getAuthenticationSessions().isEmpty()) {
|
||||
MapRootAuthenticationSessionProvider authenticationSessionProvider =
|
||||
(MapRootAuthenticationSessionProvider) session.authenticationSessions();
|
||||
authenticationSessionProvider.tx.delete(entity.getId());
|
||||
session.authenticationSessions().removeRootAuthenticationSession(realm, this);
|
||||
} else {
|
||||
entity.setTimestamp(Time.currentTime());
|
||||
}
|
||||
|
|
|
@ -16,18 +16,89 @@
|
|||
*/
|
||||
package org.keycloak.models.map.authSession;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapRootAuthenticationSessionEntity extends AbstractRootAuthenticationSessionEntity<UUID> {
|
||||
public class MapRootAuthenticationSessionEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
private int timestamp;
|
||||
private Map<String, MapAuthenticationSessionEntity> authenticationSessions = new ConcurrentHashMap<>();
|
||||
|
||||
protected MapRootAuthenticationSessionEntity() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapRootAuthenticationSessionEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapRootAuthenticationSessionEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.updated |= !Objects.equals(this.timestamp, timestamp);
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Map<String, MapAuthenticationSessionEntity> getAuthenticationSessions() {
|
||||
return authenticationSessions;
|
||||
}
|
||||
|
||||
public void setAuthenticationSessions(Map<String, MapAuthenticationSessionEntity> authenticationSessions) {
|
||||
this.updated |= !Objects.equals(this.authenticationSessions, authenticationSessions);
|
||||
this.authenticationSessions = authenticationSessions;
|
||||
}
|
||||
|
||||
public MapAuthenticationSessionEntity removeAuthenticationSession(String tabId) {
|
||||
MapAuthenticationSessionEntity entity = this.authenticationSessions.remove(tabId);
|
||||
this.updated |= entity != null;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void addAuthenticationSession(String tabId, MapAuthenticationSessionEntity entity) {
|
||||
this.updated |= !Objects.equals(this.authenticationSessions.put(tabId, entity), entity);
|
||||
}
|
||||
|
||||
public void clearAuthenticationSessions() {
|
||||
this.updated |= !this.authenticationSessions.isEmpty();
|
||||
this.authenticationSessions.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|||
import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
@ -45,17 +44,16 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapRootAuthenticationSessionProvider implements AuthenticationSessionProvider {
|
||||
public class MapRootAuthenticationSessionProvider<K> implements AuthenticationSessionProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapRootAuthenticationSessionProvider.class);
|
||||
private final KeycloakSession session;
|
||||
protected final MapKeycloakTransaction<UUID, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> tx;
|
||||
private final MapStorage<UUID, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> sessionStore;
|
||||
protected final MapKeycloakTransaction<K, MapRootAuthenticationSessionEntity<K>, RootAuthenticationSessionModel> tx;
|
||||
private final MapStorage<K, MapRootAuthenticationSessionEntity<K>, RootAuthenticationSessionModel> sessionStore;
|
||||
|
||||
private static final Predicate<MapRootAuthenticationSessionEntity> ALWAYS_FALSE = role -> false;
|
||||
private static final String AUTHENTICATION_SESSION_EVENTS = "AUTHENTICATION_SESSION_EVENTS";
|
||||
|
||||
public MapRootAuthenticationSessionProvider(KeycloakSession session, MapStorage<UUID, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> sessionStore) {
|
||||
public MapRootAuthenticationSessionProvider(KeycloakSession session, MapStorage<K, MapRootAuthenticationSessionEntity<K>, RootAuthenticationSessionModel> sessionStore) {
|
||||
this.session = session;
|
||||
this.sessionStore = sessionStore;
|
||||
this.tx = sessionStore.createTransaction(session);
|
||||
|
@ -63,21 +61,26 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
|||
session.getTransactionManager().enlistAfterCompletion(tx);
|
||||
}
|
||||
|
||||
private Function<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapRootAuthenticationSessionEntity<K>, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
||||
return origEntity -> new MapRootAuthenticationSessionAdapter(session, realm, registerEntityForChanges(origEntity));
|
||||
return origEntity -> new MapRootAuthenticationSessionAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return sessionStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MapRootAuthenticationSessionEntity registerEntityForChanges(MapRootAuthenticationSessionEntity origEntity) {
|
||||
MapRootAuthenticationSessionEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRootAuthenticationSessionEntity::isUpdated);
|
||||
private MapRootAuthenticationSessionEntity<K> registerEntityForChanges(MapRootAuthenticationSessionEntity<K> origEntity) {
|
||||
MapRootAuthenticationSessionEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRootAuthenticationSessionEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Predicate<MapRootAuthenticationSessionEntity> entityRealmFilter(String realmId) {
|
||||
private Predicate<MapRootAuthenticationSessionEntity<K>> entityRealmFilter(String realmId) {
|
||||
if (realmId == null) {
|
||||
return MapRootAuthenticationSessionProvider.ALWAYS_FALSE;
|
||||
return c -> false;
|
||||
}
|
||||
return entity -> Objects.equals(realmId, entity.getRealmId());
|
||||
}
|
||||
|
@ -92,12 +95,12 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
|||
public RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm, String id) {
|
||||
Objects.requireNonNull(realm, "The provided realm can't be null!");
|
||||
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? sessionStore.getKeyConvertor().yieldNewUniqueKey() : sessionStore.getKeyConvertor().fromString(id);
|
||||
|
||||
LOG.tracef("createRootAuthenticationSession(%s)%s", realm.getName(), getShortStackTrace());
|
||||
|
||||
// create map authentication session entity
|
||||
MapRootAuthenticationSessionEntity entity = new MapRootAuthenticationSessionEntity(entityId, realm.getId());
|
||||
MapRootAuthenticationSessionEntity<K> entity = new MapRootAuthenticationSessionEntity<>(entityId, realm.getId());
|
||||
entity.setRealmId(realm.getId());
|
||||
entity.setTimestamp(Time.currentTime());
|
||||
|
||||
|
@ -119,7 +122,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
|||
|
||||
LOG.tracef("getRootAuthenticationSession(%s, %s)%s", realm.getName(), authenticationSessionId, getShortStackTrace());
|
||||
|
||||
MapRootAuthenticationSessionEntity entity = tx.read(UUID.fromString(authenticationSessionId));
|
||||
MapRootAuthenticationSessionEntity<K> entity = tx.read(sessionStore.getKeyConvertor().fromStringSafe(authenticationSessionId));
|
||||
return (entity == null || !entityRealmFilter(realm.getId()).test(entity))
|
||||
? null
|
||||
: entityToAdapterFunc(realm).apply(entity);
|
||||
|
@ -128,7 +131,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
|||
@Override
|
||||
public void removeRootAuthenticationSession(RealmModel realm, RootAuthenticationSessionModel authenticationSession) {
|
||||
Objects.requireNonNull(authenticationSession, "The provided root authentication session can't be null!");
|
||||
tx.delete(UUID.fromString(authenticationSession.getId()));
|
||||
tx.delete(sessionStore.getKeyConvertor().fromString(authenticationSession.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -147,7 +150,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.TIMESTAMP, Operator.LT, expired);
|
||||
|
||||
long deletedCount = tx.delete(UUID.randomUUID(), mcb);
|
||||
long deletedCount = tx.delete(sessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
|
||||
LOG.debugf("Removed %d expired authentication sessions for realm '%s'", deletedCount, realm.getName());
|
||||
}
|
||||
|
|
|
@ -17,33 +17,30 @@
|
|||
package org.keycloak.models.map.authSession;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
import org.keycloak.sessions.AuthenticationSessionProviderFactory;
|
||||
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapRootAuthenticationSessionProviderFactory extends AbstractMapProviderFactory<AuthenticationSessionProvider>
|
||||
public class MapRootAuthenticationSessionProviderFactory<K> extends AbstractMapProviderFactory<AuthenticationSessionProvider, K, MapRootAuthenticationSessionEntity<K>, RootAuthenticationSessionModel>
|
||||
implements AuthenticationSessionProviderFactory {
|
||||
|
||||
private MapStorage<UUID, MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> store;
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("sessions", UUID.class, MapRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class);
|
||||
public MapRootAuthenticationSessionProviderFactory() {
|
||||
super(MapRootAuthenticationSessionEntity.class, RootAuthenticationSessionModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationSessionProvider create(KeycloakSession session) {
|
||||
return new MapRootAuthenticationSessionProvider(session, store);
|
||||
return new MapRootAuthenticationSessionProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Authentication session provider";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,11 +18,6 @@
|
|||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
|
@ -30,14 +25,8 @@ import org.keycloak.authorization.store.ResourceStore;
|
|||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
|
@ -51,7 +40,7 @@ public class MapAuthorizationStore implements StoreFactory {
|
|||
private final PermissionTicketStore permissionTicketStore;
|
||||
private boolean readOnly;
|
||||
|
||||
public MapAuthorizationStore(KeycloakSession session, MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, MapStorage<UUID, MapPolicyEntity, Policy> policyStore, MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore, MapStorage<UUID, MapResourceEntity, Resource> resourceStore, MapStorage<UUID, MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
|
||||
public MapAuthorizationStore(KeycloakSession session, MapStorage permissionTicketStore, MapStorage policyStore, MapStorage resourceServerStore, MapStorage resourceStore, MapStorage scopeStore, AuthorizationProvider provider) {
|
||||
this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider);
|
||||
this.policyStore = new MapPolicyStore(session, policyStore, provider);
|
||||
this.resourceServerStore = new MapResourceServerStore(session, resourceServerStore, provider);
|
||||
|
|
|
@ -26,32 +26,49 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.AuthorizationStoreFactory;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.storage.MapStorageSpi;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory {
|
||||
public class MapAuthorizationStoreFactory<K> implements AmphibianProviderFactory<StoreFactory>, AuthorizationStoreFactory {
|
||||
|
||||
private MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore;
|
||||
private MapStorage<UUID, MapPolicyEntity, Policy> policyStore;
|
||||
private MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore;
|
||||
private MapStorage<UUID, MapResourceEntity, Resource> resourceStore;
|
||||
private MapStorage<UUID, MapScopeEntity, Scope> scopeStore;
|
||||
public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID;
|
||||
|
||||
private Config.Scope storageConfigScope;
|
||||
|
||||
@Override
|
||||
public StoreFactory create(KeycloakSession session) {
|
||||
MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
|
||||
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
|
||||
final MapStorageProvider mapStorageProvider = storageProviderFactory.create(session);
|
||||
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
|
||||
|
||||
|
||||
MapStorage permissionTicketStore;
|
||||
MapStorage policyStore;
|
||||
MapStorage resourceServerStore;
|
||||
MapStorage resourceStore;
|
||||
MapStorage scopeStore;
|
||||
|
||||
permissionTicketStore = mapStorageProvider.getStorage(MapPermissionTicketEntity.class, PermissionTicket.class);
|
||||
policyStore = mapStorageProvider.getStorage(MapPolicyEntity.class, Policy.class);
|
||||
resourceServerStore = mapStorageProvider.getStorage(MapResourceServerEntity.class, ResourceServer.class);
|
||||
resourceStore = mapStorageProvider.getStorage(MapResourceEntity.class, Resource.class);
|
||||
scopeStore = mapStorageProvider.getStorage(MapScopeEntity.class, Scope.class);
|
||||
|
||||
return new MapAuthorizationStore(session,
|
||||
permissionTicketStore,
|
||||
policyStore,
|
||||
|
@ -63,21 +80,8 @@ public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
AuthorizationStoreFactory.super.postInit(factory);
|
||||
|
||||
MapStorageProviderFactory mapStorageProvider = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
permissionTicketStore = mapStorageProvider.getStorage("authzPermissionTickets", UUID.class, MapPermissionTicketEntity.class, PermissionTicket.class);
|
||||
policyStore = mapStorageProvider.getStorage("authzPolicies", UUID.class, MapPolicyEntity.class, Policy.class);
|
||||
resourceServerStore = mapStorageProvider.getStorage("authzResourceServers", String.class, MapResourceServerEntity.class, ResourceServer.class);
|
||||
resourceStore = mapStorageProvider.getStorage("authzResources", UUID.class, MapResourceEntity.class, Resource.class);
|
||||
scopeStore = mapStorageProvider.getStorage("authzScopes", UUID.class, MapScopeEntity.class, Scope.class);
|
||||
|
||||
public void init(org.keycloak.Config.Scope config) {
|
||||
this.storageConfigScope = config.scope("storage");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,6 +91,11 @@ public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory {
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "map";
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Authorization store provider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.authorization.store.ResourceStore;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
|
@ -37,11 +36,11 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -49,30 +48,35 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
|||
import static org.keycloak.utils.StreamsUtil.distinctByKey;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapPermissionTicketStore implements PermissionTicketStore {
|
||||
public class MapPermissionTicketStore<K extends Comparable<K>> implements PermissionTicketStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapPermissionTicketEntity, PermissionTicket> tx;
|
||||
private final MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore;
|
||||
final MapKeycloakTransaction<K, MapPermissionTicketEntity<K>, PermissionTicket> tx;
|
||||
private final MapStorage<K, MapPermissionTicketEntity<K>, PermissionTicket> permissionTicketStore;
|
||||
|
||||
public MapPermissionTicketStore(KeycloakSession session, MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) {
|
||||
public MapPermissionTicketStore(KeycloakSession session, MapStorage<K, MapPermissionTicketEntity<K>, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.permissionTicketStore = permissionTicketStore;
|
||||
this.tx = permissionTicketStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapPermissionTicketEntity registerEntityForChanges(MapPermissionTicketEntity origEntity) {
|
||||
final MapPermissionTicketEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPermissionTicketEntity::isUpdated);
|
||||
private MapPermissionTicketEntity<K> registerEntityForChanges(MapPermissionTicketEntity<K> origEntity) {
|
||||
final MapPermissionTicketEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPermissionTicketEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) {
|
||||
private PermissionTicket entityToAdapter(MapPermissionTicketEntity<K> origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapPermissionTicketAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
return new MapPermissionTicketAdapter<K>(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return permissionTicketStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<PermissionTicket> forResourceServer(String resourceServerId) {
|
||||
|
@ -116,13 +120,14 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
+ ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists.");
|
||||
}
|
||||
|
||||
MapPermissionTicketEntity entity = new MapPermissionTicketEntity(UUID.randomUUID());
|
||||
entity.setResourceId(UUID.fromString(resourceId));
|
||||
final K newId = permissionTicketStore.getKeyConvertor().yieldNewUniqueKey();
|
||||
MapPermissionTicketEntity<K> entity = new MapPermissionTicketEntity<>(newId);
|
||||
entity.setResourceId(resourceId);
|
||||
entity.setRequester(requester);
|
||||
entity.setCreatedTimestamp(System.currentTimeMillis());
|
||||
|
||||
if (scopeId != null) {
|
||||
entity.setScopeId(UUID.fromString(scopeId));
|
||||
entity.setScopeId(scopeId);
|
||||
}
|
||||
|
||||
entity.setOwner(owner);
|
||||
|
@ -136,7 +141,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(permissionTicketStore.getKeyConvertor().fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -204,7 +209,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
if (r == null || r.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.IN, r.stream().map(Resource::getId).collect(Collectors.toList()));
|
||||
mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.IN, r.stream().map(Resource::getId));
|
||||
}
|
||||
|
||||
mcb = mcb.and(
|
||||
|
@ -213,8 +218,9 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
.toArray(ModelCriteriaBuilder[]::new)
|
||||
);
|
||||
|
||||
Comparator<? super MapPermissionTicketEntity<K>> c = Comparator.comparing(MapPermissionTicketEntity::getId);
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_ID), firstResult, maxResult)
|
||||
.sorted(c), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -278,14 +284,14 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
.compare(SearchableFields.REQUESTER, Operator.EQ, requester)
|
||||
.compare(SearchableFields.GRANTED_TIMESTAMP, Operator.EXISTS);
|
||||
|
||||
Function<MapPermissionTicketEntity, Resource> ticketResourceMapper;
|
||||
Function<MapPermissionTicketEntity<K>, Resource> ticketResourceMapper;
|
||||
|
||||
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
|
||||
if (name != null) {
|
||||
ticketResourceMapper = ticket -> {
|
||||
Map<Resource.FilterOption, String[]> filterOptionMap = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId().toString()});
|
||||
filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId()});
|
||||
filterOptionMap.put(Resource.FilterOption.NAME, new String[] {name});
|
||||
|
||||
List<Resource> resource = resourceStore.findByResourceServer(filterOptionMap, ticket.getResourceServerId(), -1, 1);
|
||||
|
@ -294,11 +300,11 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
};
|
||||
} else {
|
||||
ticketResourceMapper = ticket -> resourceStore
|
||||
.findById(ticket.getResourceId().toString(), ticket.getResourceServerId());
|
||||
.findById(ticket.getResourceId(), ticket.getResourceServerId());
|
||||
}
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId))
|
||||
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId))
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID)
|
||||
.map(ticketResourceMapper)
|
||||
.filter(Objects::nonNull), first, max)
|
||||
|
@ -311,10 +317,10 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
|
|||
.compare(SearchableFields.OWNER, Operator.EQ, owner);
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId))
|
||||
.filter(distinctByKey(MapPermissionTicketEntity::getResourceId))
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID), first, max)
|
||||
.map(ticket -> authorizationProvider.getStoreFactory().getResourceStore()
|
||||
.findById(ticket.getResourceId().toString(), ticket.getResourceServerId()))
|
||||
.findById(ticket.getResourceId(), ticket.getResourceServerId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.keycloak.authorization.store.PolicyStore;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
|
@ -38,37 +37,41 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapPolicyStore implements PolicyStore {
|
||||
public class MapPolicyStore<K> implements PolicyStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapPolicyStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapPolicyEntity, Policy> tx;
|
||||
private final MapStorage<UUID, MapPolicyEntity, Policy> policyStore;
|
||||
final MapKeycloakTransaction<K, MapPolicyEntity<K>, Policy> tx;
|
||||
private final MapStorage<K, MapPolicyEntity<K>, Policy> policyStore;
|
||||
|
||||
public MapPolicyStore(KeycloakSession session, MapStorage<UUID, MapPolicyEntity, Policy> policyStore, AuthorizationProvider provider) {
|
||||
public MapPolicyStore(KeycloakSession session, MapStorage<K, MapPolicyEntity<K>, Policy> policyStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.policyStore = policyStore;
|
||||
this.tx = policyStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapPolicyEntity registerEntityForChanges(MapPolicyEntity origEntity) {
|
||||
final MapPolicyEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPolicyEntity::isUpdated);
|
||||
private MapPolicyEntity<K> registerEntityForChanges(MapPolicyEntity<K> origEntity) {
|
||||
final MapPolicyEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPolicyEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Policy entityToAdapter(MapPolicyEntity origEntity) {
|
||||
private Policy entityToAdapter(MapPolicyEntity<K> origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapPolicyAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
return new MapPolicyAdapter<K>(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return policyStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Policy> forResourceServer(String resourceServerId) {
|
||||
|
@ -92,8 +95,8 @@ public class MapPolicyStore implements PolicyStore {
|
|||
throw new ModelDuplicateException("Policy with name '" + representation.getName() + "' for " + resourceServer.getId() + " already exists");
|
||||
}
|
||||
|
||||
UUID uid = representation.getId() == null ? UUID.randomUUID() : UUID.fromString(representation.getId());
|
||||
MapPolicyEntity entity = new MapPolicyEntity(uid);
|
||||
K uid = representation.getId() == null ? policyStore.getKeyConvertor().yieldNewUniqueKey() : policyStore.getKeyConvertor().fromString(representation.getId());
|
||||
MapPolicyEntity<K> entity = new MapPolicyEntity<>(uid);
|
||||
entity.setType(representation.getType());
|
||||
entity.setName(representation.getName());
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
@ -106,7 +109,7 @@ public class MapPolicyStore implements PolicyStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(policyStore.getKeyConvertor().fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,9 +158,9 @@ public class MapPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(AbstractPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.map(MapPolicyEntity::getId)
|
||||
.map(UUID::toString)
|
||||
.sorted(MapPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.map(MapPolicyEntity<K>::getId)
|
||||
.map(K::toString)
|
||||
.map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)) // We need to go through cache
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
|
@ -41,30 +41,35 @@ import org.keycloak.storage.StorageId;
|
|||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
|
||||
public class MapResourceServerStore implements ResourceServerStore {
|
||||
public class MapResourceServerStore<K> implements ResourceServerStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapResourceServerStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<String, MapResourceServerEntity, ResourceServer> tx;
|
||||
private final MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore;
|
||||
final MapKeycloakTransaction<K, MapResourceServerEntity<K>, ResourceServer> tx;
|
||||
private final MapStorage<K, MapResourceServerEntity<K>, ResourceServer> resourceServerStore;
|
||||
|
||||
public MapResourceServerStore(KeycloakSession session, MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore, AuthorizationProvider provider) {
|
||||
public MapResourceServerStore(KeycloakSession session, MapStorage<K, MapResourceServerEntity<K>, ResourceServer> resourceServerStore, AuthorizationProvider provider) {
|
||||
this.resourceServerStore = resourceServerStore;
|
||||
this.tx = resourceServerStore.createTransaction(session);
|
||||
this.authorizationProvider = provider;
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapResourceServerEntity registerEntityForChanges(MapResourceServerEntity origEntity) {
|
||||
final MapResourceServerEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceServerEntity::isUpdated);
|
||||
private MapResourceServerEntity<K> registerEntityForChanges(MapResourceServerEntity<K> origEntity) {
|
||||
final MapResourceServerEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceServerEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) {
|
||||
private ResourceServer entityToAdapter(MapResourceServerEntity<K> origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapResourceServerAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
return new MapResourceServerAdapter<K>(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return resourceServerStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,11 +82,11 @@ public class MapResourceServerStore implements ResourceServerStore {
|
|||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
|
||||
if (tx.read(clientId) != null) {
|
||||
if (tx.read(resourceServerStore.getKeyConvertor().fromString(clientId)) != null) {
|
||||
throw new ModelDuplicateException("Resource server already exists: " + clientId);
|
||||
}
|
||||
|
||||
MapResourceServerEntity entity = new MapResourceServerEntity(clientId);
|
||||
MapResourceServerEntity<K> entity = new MapResourceServerEntity<>(resourceServerStore.getKeyConvertor().fromString(clientId));
|
||||
|
||||
tx.create(entity.getId(), entity);
|
||||
|
||||
|
@ -113,7 +118,7 @@ public class MapResourceServerStore implements ResourceServerStore {
|
|||
.map(Scope::getId)
|
||||
.forEach(scopeStore::delete);
|
||||
|
||||
tx.delete(id);
|
||||
tx.delete(resourceServerStore.getKeyConvertor().fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,7 +130,7 @@ public class MapResourceServerStore implements ResourceServerStore {
|
|||
}
|
||||
|
||||
|
||||
MapResourceServerEntity entity = tx.read(id);
|
||||
MapResourceServerEntity<K> entity = tx.read(resourceServerStore.getKeyConvertor().fromStringSafe(id));
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.keycloak.authorization.store.ResourceStore;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapResourceAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
|
@ -35,40 +34,45 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapResourceStore implements ResourceStore {
|
||||
public class MapResourceStore<K extends Comparable<K>> implements ResourceStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapResourceStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapResourceEntity, Resource> tx;
|
||||
private final MapStorage<UUID, MapResourceEntity, Resource> resourceStore;
|
||||
final MapKeycloakTransaction<K, MapResourceEntity<K>, Resource> tx;
|
||||
private final MapStorage<K, MapResourceEntity<K>, Resource> resourceStore;
|
||||
|
||||
public MapResourceStore(KeycloakSession session, MapStorage<UUID, MapResourceEntity, Resource> resourceStore, AuthorizationProvider provider) {
|
||||
public MapResourceStore(KeycloakSession session, MapStorage<K, MapResourceEntity<K>, Resource> resourceStore, AuthorizationProvider provider) {
|
||||
this.resourceStore = resourceStore;
|
||||
this.tx = resourceStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
authorizationProvider = provider;
|
||||
}
|
||||
|
||||
private MapResourceEntity registerEntityForChanges(MapResourceEntity origEntity) {
|
||||
final MapResourceEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceEntity::isUpdated);
|
||||
private MapResourceEntity<K> registerEntityForChanges(MapResourceEntity<K> origEntity) {
|
||||
final MapResourceEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Resource entityToAdapter(MapResourceEntity origEntity) {
|
||||
private Resource entityToAdapter(MapResourceEntity<K> origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapResourceAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
return new MapResourceAdapter<K>(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return resourceStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Resource> forResourceServer(String resourceServerId) {
|
||||
|
@ -92,8 +96,8 @@ public class MapResourceStore implements ResourceStore {
|
|||
throw new ModelDuplicateException("Resource with name '" + name + "' for " + resourceServer.getId() + " already exists for request owner " + owner);
|
||||
}
|
||||
|
||||
UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
MapResourceEntity entity = new MapResourceEntity(uid);
|
||||
K uid = id == null ? resourceStore.getKeyConvertor().yieldNewUniqueKey(): resourceStore.getKeyConvertor().fromString(id);
|
||||
MapResourceEntity<K> entity = new MapResourceEntity<>(uid);
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
@ -108,7 +112,7 @@ public class MapResourceStore implements ResourceStore {
|
|||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(resourceStore.getKeyConvertor().fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,9 +133,10 @@ public class MapResourceStore implements ResourceStore {
|
|||
|
||||
private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer<Resource> consumer, int firstResult, int maxResult) {
|
||||
LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace());
|
||||
Comparator<? super MapResourceEntity<K>> c = Comparator.comparing(MapResourceEntity::getId);
|
||||
paginatedStream(tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, ownerId))
|
||||
.sorted(MapResourceEntity.COMPARE_BY_ID), firstResult, maxResult)
|
||||
.sorted(c), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
@ -174,7 +179,7 @@ public class MapResourceStore implements ResourceStore {
|
|||
);
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(AbstractResourceEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.sorted(MapResourceEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
|
@ -36,36 +36,40 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapScopeStore implements ScopeStore {
|
||||
public class MapScopeStore<K> implements ScopeStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapScopeStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapScopeEntity, Scope> tx;
|
||||
private final MapStorage<UUID, MapScopeEntity, Scope> scopeStore;
|
||||
final MapKeycloakTransaction<K, MapScopeEntity<K>, Scope> tx;
|
||||
private final MapStorage<K, MapScopeEntity<K>, Scope> scopeStore;
|
||||
|
||||
public MapScopeStore(KeycloakSession session, MapStorage<UUID, MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
|
||||
public MapScopeStore(KeycloakSession session, MapStorage<K, MapScopeEntity<K>, Scope> scopeStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.scopeStore = scopeStore;
|
||||
this.tx = scopeStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapScopeEntity registerEntityForChanges(MapScopeEntity origEntity) {
|
||||
final MapScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapScopeEntity::isUpdated);
|
||||
private MapScopeEntity<K> registerEntityForChanges(MapScopeEntity<K> origEntity) {
|
||||
final MapScopeEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapScopeEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Scope entityToAdapter(MapScopeEntity origEntity) {
|
||||
private Scope entityToAdapter(MapScopeEntity<K> origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapScopeAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
return new MapScopeAdapter<K>(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory()) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return scopeStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Scope> forResourceServer(String resourceServerId) {
|
||||
|
@ -90,8 +94,8 @@ public class MapScopeStore implements ScopeStore {
|
|||
throw new ModelDuplicateException("Scope with name '" + name + "' for " + resourceServer.getId() + " already exists");
|
||||
}
|
||||
|
||||
UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
MapScopeEntity entity = new MapScopeEntity(uid);
|
||||
K uid = id == null ? scopeStore.getKeyConvertor().yieldNewUniqueKey(): scopeStore.getKeyConvertor().fromString(id);
|
||||
MapScopeEntity<K> entity = new MapScopeEntity<>(uid);
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
@ -104,7 +108,7 @@ public class MapScopeStore implements ScopeStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(scopeStore.getKeyConvertor().fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.keycloak.models.map.common.AbstractEntity;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractResourceModel<E extends AbstractEntity> extends AbstractAuthorizationModel implements Resource {
|
||||
public abstract class AbstractResourceModel<E extends AbstractEntity<?>> extends AbstractAuthorizationModel implements Resource {
|
||||
|
||||
protected final E entity;
|
||||
|
||||
|
|
|
@ -23,23 +23,17 @@ import org.keycloak.authorization.model.Resource;
|
|||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
|
||||
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy;
|
||||
|
||||
public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<MapPermissionTicketEntity> {
|
||||
public abstract class MapPermissionTicketAdapter<K extends Comparable<K>> extends AbstractPermissionTicketModel<MapPermissionTicketEntity<K>> {
|
||||
|
||||
public MapPermissionTicketAdapter(MapPermissionTicketEntity entity, StoreFactory storeFactory) {
|
||||
public MapPermissionTicketAdapter(MapPermissionTicketEntity<K> entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return entity.getOwner();
|
||||
|
@ -52,13 +46,13 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
|
|||
|
||||
@Override
|
||||
public Resource getResource() {
|
||||
return storeFactory.getResourceStore().findById(entity.getResourceId().toString(), entity.getResourceServerId());
|
||||
return storeFactory.getResourceStore().findById(entity.getResourceId(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope getScope() {
|
||||
if (entity.getScopeId() == null) return null;
|
||||
return storeFactory.getScopeStore().findById(entity.getScopeId().toString(), entity.getResourceServerId());
|
||||
return storeFactory.getScopeStore().findById(entity.getScopeId(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,13 +84,13 @@ public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<Ma
|
|||
@Override
|
||||
public Policy getPolicy() {
|
||||
if (entity.getPolicyId() == null) return null;
|
||||
return storeFactory.getPolicyStore().findById(entity.getPolicyId().toString(), entity.getResourceServerId());
|
||||
return storeFactory.getPolicyStore().findById(entity.getPolicyId(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolicy(Policy policy) {
|
||||
if (policy != null) {
|
||||
entity.setPolicyId(UUID.fromString(policy.getId()));
|
||||
entity.setPolicyId(policy.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,20 +28,14 @@ import org.keycloak.representations.idm.authorization.Logic;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
||||
public abstract class MapPolicyAdapter<K> extends AbstractPolicyModel<MapPolicyEntity<K>> {
|
||||
|
||||
public MapPolicyAdapter(MapPolicyEntity entity, StoreFactory storeFactory) {
|
||||
public MapPolicyAdapter(MapPolicyEntity<K> entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return entity.getType();
|
||||
|
@ -123,7 +117,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
|||
public Set<Policy> getAssociatedPolicies() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getAssociatedPoliciesIds().stream()
|
||||
.map(policyId -> storeFactory.getPolicyStore().findById(policyId.toString(), resourceServerId))
|
||||
.map(policyId -> storeFactory.getPolicyStore().findById(policyId, resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
@ -131,7 +125,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
|||
public Set<Resource> getResources() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getResourceIds().stream()
|
||||
.map(resourceId -> storeFactory.getResourceStore().findById(resourceId.toString(), resourceServerId))
|
||||
.map(resourceId -> storeFactory.getResourceStore().findById(resourceId, resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
@ -139,7 +133,7 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
|||
public Set<Scope> getScopes() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getScopeIds().stream()
|
||||
.map(scopeId -> storeFactory.getScopeStore().findById(scopeId.toString(), resourceServerId))
|
||||
.map(scopeId -> storeFactory.getScopeStore().findById(scopeId, resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
@ -157,37 +151,37 @@ public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
|||
@Override
|
||||
public void addScope(Scope scope) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addScope(UUID.fromString(scope.getId()));
|
||||
entity.addScope(scope.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScope(Scope scope) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeScope(UUID.fromString(scope.getId()));
|
||||
entity.removeScope(scope.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAssociatedPolicy(Policy associatedPolicy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addAssociatedPolicy(UUID.fromString(associatedPolicy.getId()));
|
||||
entity.addAssociatedPolicy(associatedPolicy.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAssociatedPolicy(Policy associatedPolicy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeAssociatedPolicy(UUID.fromString(associatedPolicy.getId()));
|
||||
entity.removeAssociatedPolicy(associatedPolicy.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResource(Resource resource) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addResource(UUID.fromString(resource.getId()));
|
||||
entity.addResource(resource.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResource(Resource resource) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeResource(UUID.fromString(resource.getId()));
|
||||
entity.removeResource(resource.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,27 +19,21 @@ package org.keycloak.models.map.authorization.adapter;
|
|||
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity> {
|
||||
public abstract class MapResourceAdapter<K> extends AbstractResourceModel<MapResourceEntity<K>> {
|
||||
|
||||
public MapResourceAdapter(MapResourceEntity entity, StoreFactory storeFactory) {
|
||||
public MapResourceAdapter(MapResourceEntity<K> entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
|
@ -88,7 +82,7 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
|
|||
public List<Scope> getScopes() {
|
||||
return entity.getScopeIds().stream()
|
||||
.map(id -> storeFactory
|
||||
.getScopeStore().findById(id.toString(), entity.getResourceServerId()))
|
||||
.getScopeStore().findById(id, entity.getResourceServerId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@ -127,7 +121,7 @@ public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity>
|
|||
@Override
|
||||
public void updateScopes(Set<Scope> scopes) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setScopeIds(scopes.stream().map(Scope::getId).map(UUID::fromString).collect(Collectors.toSet()));
|
||||
entity.setScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,17 +23,12 @@ import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
|||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
public class MapResourceServerAdapter extends AbstractResourceServerModel<MapResourceServerEntity> {
|
||||
public abstract class MapResourceServerAdapter<K> extends AbstractResourceServerModel<MapResourceServerEntity<K>> {
|
||||
|
||||
public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) {
|
||||
public MapResourceServerAdapter(MapResourceServerEntity<K> entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return entity.isAllowRemoteResourceManagement();
|
||||
|
|
|
@ -22,17 +22,12 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
|
||||
public class MapScopeAdapter extends AbstractScopeModel<MapScopeEntity> {
|
||||
public abstract class MapScopeAdapter<K> extends AbstractScopeModel<MapScopeEntity<K>> {
|
||||
|
||||
public MapScopeAdapter(MapScopeEntity entity, StoreFactory storeFactory) {
|
||||
public MapScopeAdapter(MapScopeEntity<K> entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPermissionTicketEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private String owner;
|
||||
private String requester;
|
||||
private Long createdTimestamp;
|
||||
private Long grantedTimestamp;
|
||||
private K resourceId;
|
||||
private K scopeId;
|
||||
private String resourceServerId;
|
||||
private K policyId;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractPermissionTicketEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractPermissionTicketEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public void setRequester(String requester) {
|
||||
this.updated |= !Objects.equals(this.requester, requester);
|
||||
this.requester = requester;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public Long getGrantedTimestamp() {
|
||||
return grantedTimestamp;
|
||||
}
|
||||
|
||||
public void setGrantedTimestamp(Long grantedTimestamp) {
|
||||
this.updated |= !Objects.equals(this.grantedTimestamp, grantedTimestamp);
|
||||
this.grantedTimestamp = grantedTimestamp;
|
||||
}
|
||||
|
||||
public K getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(K resourceId) {
|
||||
this.updated |= !Objects.equals(this.resourceId, resourceId);
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public K getScopeId() {
|
||||
return scopeId;
|
||||
}
|
||||
|
||||
public void setScopeId(K scopeId) {
|
||||
this.updated |= !Objects.equals(this.scopeId, scopeId);
|
||||
this.scopeId = scopeId;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public K getPolicyId() {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
public void setPolicyId(K policyId) {
|
||||
this.updated |= !Objects.equals(this.policyId, policyId);
|
||||
this.policyId = policyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPolicyEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<AbstractPolicyEntity<?>> COMPARE_BY_NAME = Comparator.comparing(AbstractPolicyEntity::getName);
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
private Logic logic = Logic.POSITIVE;
|
||||
private final Map<String, String> config = new HashMap<>();
|
||||
private String resourceServerId;
|
||||
private final Set<K> associatedPoliciesIds = new HashSet<>();
|
||||
private final Set<K> resourceIds = new HashSet<>();
|
||||
private final Set<K> scopeIds = new HashSet<>();
|
||||
private String owner;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractPolicyEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractPolicyEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= !Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
public Logic getLogic() {
|
||||
return logic;
|
||||
}
|
||||
|
||||
public void setLogic(Logic logic) {
|
||||
this.updated |= !Objects.equals(this.logic, logic);
|
||||
this.logic = logic;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public String getConfigValue(String name) {
|
||||
return config.get(name);
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
if (Objects.equals(this.config, config)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.config.clear();
|
||||
if (config != null) {
|
||||
this.config.putAll(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConfig(String name) {
|
||||
this.updated |= this.config.remove(name) != null;
|
||||
}
|
||||
|
||||
public void putConfig(String name, String value) {
|
||||
this.updated |= !Objects.equals(value, this.config.put(name, value));
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<K> getAssociatedPoliciesIds() {
|
||||
return associatedPoliciesIds;
|
||||
}
|
||||
|
||||
public void addAssociatedPolicy(K policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.add(policyId);
|
||||
}
|
||||
|
||||
public void removeAssociatedPolicy(K policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.remove(policyId);
|
||||
}
|
||||
|
||||
public Set<K> getResourceIds() {
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
public void addResource(K resourceId) {
|
||||
this.updated |= this.resourceIds.add(resourceId);
|
||||
}
|
||||
|
||||
public void removeResource(K resourceId) {
|
||||
this.updated |= this.resourceIds.remove(resourceId);
|
||||
}
|
||||
|
||||
public Set<K> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void addScope(K scopeId) {
|
||||
this.updated |= this.scopeIds.add(scopeId);
|
||||
}
|
||||
|
||||
public void removeScope(K scopeId) {
|
||||
this.updated |= this.scopeIds.remove(scopeId);
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractResourceEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<AbstractResourceEntity<?>> COMPARE_BY_NAME = Comparator.comparing(AbstractResourceEntity::getName);
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private final Set<String> uris = new HashSet<>();
|
||||
private String type;
|
||||
private String iconUri;
|
||||
private String owner;
|
||||
private boolean ownerManagedAccess;
|
||||
private String resourceServerId;
|
||||
private final Set<K> scopeIds = new HashSet<>();
|
||||
private final Set<K> policyIds = new HashSet<>();
|
||||
private final Map<String, List<String>> attributes = new HashMap<>();
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractResourceEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractResourceEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public Set<String> getUris() {
|
||||
return uris;
|
||||
}
|
||||
|
||||
public void setUris(Set<String> uris) {
|
||||
if (Objects.equals(this.uris, uris)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.uris.clear();
|
||||
|
||||
if (uris != null) {
|
||||
this.uris.addAll(uris);
|
||||
}
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public boolean isOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||
this.updated |= this.ownerManagedAccess != ownerManagedAccess;
|
||||
this.ownerManagedAccess = ownerManagedAccess;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<K> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void setScopeIds(Set<K> scopeIds) {
|
||||
if (Objects.equals(this.scopeIds, scopeIds)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.scopeIds.clear();
|
||||
if (scopeIds != null) {
|
||||
this.scopeIds.addAll(scopeIds);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<K> getPolicyIds() {
|
||||
return policyIds;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
public String getSingleAttribute(String name) {
|
||||
List<String> attributeValues = attributes.get(name);
|
||||
return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractResourceServerEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private boolean updated = false;
|
||||
|
||||
private boolean allowRemoteResourceManagement;
|
||||
private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
|
||||
protected AbstractResourceServerEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractResourceServerEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
||||
this.updated |= this.allowRemoteResourceManagement != allowRemoteResourceManagement;
|
||||
this.allowRemoteResourceManagement = allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public PolicyEnforcementMode getPolicyEnforcementMode() {
|
||||
return policyEnforcementMode;
|
||||
}
|
||||
|
||||
public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
|
||||
this.updated |= !Objects.equals(this.policyEnforcementMode, policyEnforcementMode);
|
||||
this.policyEnforcementMode = policyEnforcementMode;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractScopeEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private String iconUri;
|
||||
private String resourceServerId;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractScopeEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractScopeEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -17,21 +17,114 @@
|
|||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MapPermissionTicketEntity extends AbstractPermissionTicketEntity<UUID> {
|
||||
public class MapPermissionTicketEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<AbstractPermissionTicketEntity<UUID>> COMPARE_BY_ID = Comparator.comparing(AbstractPermissionTicketEntity::getId);
|
||||
public static final Comparator<AbstractPermissionTicketEntity<UUID>> COMPARE_BY_RESOURCE_ID = Comparator.comparing(AbstractPermissionTicketEntity::getResourceId);
|
||||
public static final Comparator<MapPermissionTicketEntity<?>> COMPARE_BY_RESOURCE_ID = Comparator.comparing(MapPermissionTicketEntity::getResourceId);
|
||||
|
||||
private final K id;
|
||||
private String owner;
|
||||
private String requester;
|
||||
private Long createdTimestamp;
|
||||
private Long grantedTimestamp;
|
||||
private String resourceId;
|
||||
private String scopeId;
|
||||
private String resourceServerId;
|
||||
private String policyId;
|
||||
private boolean updated = false;
|
||||
|
||||
protected MapPermissionTicketEntity() {
|
||||
super();
|
||||
public MapPermissionTicketEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MapPermissionTicketEntity(UUID id) {
|
||||
super(id);
|
||||
public MapPermissionTicketEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public void setRequester(String requester) {
|
||||
this.updated |= !Objects.equals(this.requester, requester);
|
||||
this.requester = requester;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public Long getGrantedTimestamp() {
|
||||
return grantedTimestamp;
|
||||
}
|
||||
|
||||
public void setGrantedTimestamp(Long grantedTimestamp) {
|
||||
this.updated |= !Objects.equals(this.grantedTimestamp, grantedTimestamp);
|
||||
this.grantedTimestamp = grantedTimestamp;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(String resourceId) {
|
||||
this.updated |= !Objects.equals(this.resourceId, resourceId);
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getScopeId() {
|
||||
return scopeId;
|
||||
}
|
||||
|
||||
public void setScopeId(String scopeId) {
|
||||
this.updated |= !Objects.equals(this.scopeId, scopeId);
|
||||
this.scopeId = scopeId;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public String getPolicyId() {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
public void setPolicyId(String policyId) {
|
||||
this.updated |= !Objects.equals(this.policyId, policyId);
|
||||
this.policyId = policyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,15 +17,176 @@
|
|||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
public class MapPolicyEntity extends AbstractPolicyEntity<UUID> {
|
||||
protected MapPolicyEntity() {
|
||||
super();
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MapPolicyEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<MapPolicyEntity<?>> COMPARE_BY_NAME = Comparator.comparing(MapPolicyEntity::getName);
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
private Logic logic = Logic.POSITIVE;
|
||||
private final Map<String, String> config = new HashMap<>();
|
||||
private String resourceServerId;
|
||||
private final Set<String> associatedPoliciesIds = new HashSet<>();
|
||||
private final Set<String> resourceIds = new HashSet<>();
|
||||
private final Set<String> scopeIds = new HashSet<>();
|
||||
private String owner;
|
||||
private boolean updated = false;
|
||||
|
||||
public MapPolicyEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MapPolicyEntity(UUID id) {
|
||||
super(id);
|
||||
public MapPolicyEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= !Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
public Logic getLogic() {
|
||||
return logic;
|
||||
}
|
||||
|
||||
public void setLogic(Logic logic) {
|
||||
this.updated |= !Objects.equals(this.logic, logic);
|
||||
this.logic = logic;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public String getConfigValue(String name) {
|
||||
return config.get(name);
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
if (Objects.equals(this.config, config)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.config.clear();
|
||||
if (config != null) {
|
||||
this.config.putAll(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConfig(String name) {
|
||||
this.updated |= this.config.remove(name) != null;
|
||||
}
|
||||
|
||||
public void putConfig(String name, String value) {
|
||||
this.updated |= !Objects.equals(value, this.config.put(name, value));
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<String> getAssociatedPoliciesIds() {
|
||||
return associatedPoliciesIds;
|
||||
}
|
||||
|
||||
public void addAssociatedPolicy(String policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.add(policyId);
|
||||
}
|
||||
|
||||
public void removeAssociatedPolicy(String policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.remove(policyId);
|
||||
}
|
||||
|
||||
public Set<String> getResourceIds() {
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
public void addResource(String resourceId) {
|
||||
this.updated |= this.resourceIds.add(resourceId);
|
||||
}
|
||||
|
||||
public void removeResource(String resourceId) {
|
||||
this.updated |= this.resourceIds.remove(resourceId);
|
||||
}
|
||||
|
||||
public Set<String> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void addScope(String scopeId) {
|
||||
this.updated |= this.scopeIds.add(scopeId);
|
||||
}
|
||||
|
||||
public void removeScope(String scopeId) {
|
||||
this.updated |= this.scopeIds.remove(scopeId);
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,18 +17,167 @@
|
|||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class MapResourceEntity extends AbstractResourceEntity<UUID> {
|
||||
public static final Comparator<AbstractResourceEntity<UUID>> COMPARE_BY_ID = Comparator.comparing(AbstractResourceEntity::getId);
|
||||
public class MapResourceEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<MapResourceEntity<?>> COMPARE_BY_NAME = Comparator.comparing(MapResourceEntity::getName);
|
||||
|
||||
protected MapResourceEntity() {
|
||||
super();
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private final Set<String> uris = new HashSet<>();
|
||||
private String type;
|
||||
private String iconUri;
|
||||
private String owner;
|
||||
private boolean ownerManagedAccess;
|
||||
private String resourceServerId;
|
||||
private final Set<String> scopeIds = new HashSet<>();
|
||||
private final Set<String> policyIds = new HashSet<>();
|
||||
private final Map<String, List<String>> attributes = new HashMap<>();
|
||||
private boolean updated = false;
|
||||
|
||||
public MapResourceEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MapResourceEntity(UUID id) {
|
||||
super(id);
|
||||
public MapResourceEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public Set<String> getUris() {
|
||||
return uris;
|
||||
}
|
||||
|
||||
public void setUris(Set<String> uris) {
|
||||
if (Objects.equals(this.uris, uris)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.uris.clear();
|
||||
|
||||
if (uris != null) {
|
||||
this.uris.addAll(uris);
|
||||
}
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public boolean isOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||
this.updated |= this.ownerManagedAccess != ownerManagedAccess;
|
||||
this.ownerManagedAccess = ownerManagedAccess;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<String> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void setScopeIds(Set<String> scopeIds) {
|
||||
if (Objects.equals(this.scopeIds, scopeIds)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.scopeIds.clear();
|
||||
if (scopeIds != null) {
|
||||
this.scopeIds.addAll(scopeIds);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getPolicyIds() {
|
||||
return policyIds;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
public String getSingleAttribute(String name) {
|
||||
List<String> attributeValues = attributes.get(name);
|
||||
return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,13 +17,64 @@
|
|||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
public class MapResourceServerEntity extends AbstractResourceServerEntity<String> {
|
||||
protected MapResourceServerEntity() {
|
||||
super();
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class MapResourceServerEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private boolean updated = false;
|
||||
|
||||
private boolean allowRemoteResourceManagement;
|
||||
private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
|
||||
public MapResourceServerEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MapResourceServerEntity(String id) {
|
||||
super(id);
|
||||
public MapResourceServerEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
||||
this.updated |= this.allowRemoteResourceManagement != allowRemoteResourceManagement;
|
||||
this.allowRemoteResourceManagement = allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public PolicyEnforcementMode getPolicyEnforcementMode() {
|
||||
return policyEnforcementMode;
|
||||
}
|
||||
|
||||
public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
|
||||
this.updated |= !Objects.equals(this.policyEnforcementMode, policyEnforcementMode);
|
||||
this.policyEnforcementMode = policyEnforcementMode;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,15 +17,71 @@
|
|||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public class MapScopeEntity extends AbstractScopeEntity<UUID> {
|
||||
protected MapScopeEntity() {
|
||||
super();
|
||||
import java.util.Objects;
|
||||
|
||||
public class MapScopeEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private String iconUri;
|
||||
private String resourceServerId;
|
||||
private boolean updated = false;
|
||||
|
||||
public MapScopeEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MapScopeEntity(UUID id) {
|
||||
super(id);
|
||||
public MapScopeEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,482 +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.map.client;
|
||||
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public abstract class AbstractClientEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
private String clientId;
|
||||
private String name;
|
||||
private String description;
|
||||
private Set<String> redirectUris = new HashSet<>();
|
||||
private boolean enabled;
|
||||
private boolean alwaysDisplayInConsole;
|
||||
private String clientAuthenticatorType;
|
||||
private String secret;
|
||||
private String registrationToken;
|
||||
private String protocol;
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
private Map<String, String> authFlowBindings = new HashMap<>();
|
||||
private boolean publicClient;
|
||||
private boolean fullScopeAllowed;
|
||||
private boolean frontchannelLogout;
|
||||
private int notBefore;
|
||||
private Set<String> scope = new HashSet<>();
|
||||
private Set<String> webOrigins = new HashSet<>();
|
||||
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
|
||||
private Map<String, Boolean> clientScopes = new HashMap<>();
|
||||
private Set<String> scopeMappings = new LinkedHashSet<>();
|
||||
private boolean surrogateAuthRequired;
|
||||
private String managementUrl;
|
||||
private String rootUrl;
|
||||
private String baseUrl;
|
||||
private boolean bearerOnly;
|
||||
private boolean consentRequired;
|
||||
private boolean standardFlowEnabled;
|
||||
private boolean implicitFlowEnabled;
|
||||
private boolean directAccessGrantsEnabled;
|
||||
private boolean serviceAccountsEnabled;
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected AbstractClientEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractClientEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= ! Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Set<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
||||
public void setRedirectUris(Set<String> redirectUris) {
|
||||
this.updated |= ! Objects.equals(this.redirectUris, redirectUris);
|
||||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.updated |= ! Objects.equals(this.enabled, enabled);
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isAlwaysDisplayInConsole() {
|
||||
return alwaysDisplayInConsole;
|
||||
}
|
||||
|
||||
public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) {
|
||||
this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole);
|
||||
this.alwaysDisplayInConsole = alwaysDisplayInConsole;
|
||||
}
|
||||
|
||||
public String getClientAuthenticatorType() {
|
||||
return clientAuthenticatorType;
|
||||
}
|
||||
|
||||
public void setClientAuthenticatorType(String clientAuthenticatorType) {
|
||||
this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType);
|
||||
this.clientAuthenticatorType = clientAuthenticatorType;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.updated |= ! Objects.equals(this.secret, secret);
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getRegistrationToken() {
|
||||
return registrationToken;
|
||||
}
|
||||
|
||||
public void setRegistrationToken(String registrationToken) {
|
||||
this.updated |= ! Objects.equals(this.registrationToken, registrationToken);
|
||||
this.registrationToken = registrationToken;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.updated |= ! Objects.equals(this.protocol, protocol);
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthFlowBindings() {
|
||||
return authFlowBindings;
|
||||
}
|
||||
|
||||
public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
|
||||
this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings);
|
||||
this.authFlowBindings = authFlowBindings;
|
||||
}
|
||||
|
||||
public boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(boolean publicClient) {
|
||||
this.updated |= ! Objects.equals(this.publicClient, publicClient);
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(boolean fullScopeAllowed) {
|
||||
this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed);
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
public boolean isFrontchannelLogout() {
|
||||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public void setFrontchannelLogout(boolean frontchannelLogout) {
|
||||
this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout);
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.updated |= ! Objects.equals(this.notBefore, notBefore);
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public Set<String> getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(Set<String> scope) {
|
||||
this.updated |= ! Objects.equals(this.scope, scope);
|
||||
this.scope.clear();
|
||||
this.scope.addAll(scope);
|
||||
}
|
||||
|
||||
public Set<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
||||
public void setWebOrigins(Set<String> webOrigins) {
|
||||
this.updated |= ! Objects.equals(this.webOrigins, webOrigins);
|
||||
this.webOrigins.clear();
|
||||
this.webOrigins.addAll(webOrigins);
|
||||
}
|
||||
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
Objects.requireNonNull(model.getId(), "protocolMapper.id");
|
||||
updated = true;
|
||||
this.protocolMappers.put(model.getId(), model);
|
||||
return model;
|
||||
}
|
||||
|
||||
public Collection<ProtocolMapperModel> getProtocolMappers() {
|
||||
return protocolMappers.values();
|
||||
}
|
||||
|
||||
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
|
||||
updated = true;
|
||||
protocolMappers.put(id, mapping);
|
||||
}
|
||||
|
||||
public void removeProtocolMapper(String id) {
|
||||
updated |= protocolMappers.remove(id) != null;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
|
||||
this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
|
||||
this.protocolMappers.clear();
|
||||
this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
|
||||
}
|
||||
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return id == null ? null : protocolMappers.get(id);
|
||||
}
|
||||
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired);
|
||||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public String getManagementUrl() {
|
||||
return managementUrl;
|
||||
}
|
||||
|
||||
public void setManagementUrl(String managementUrl) {
|
||||
this.updated |= ! Objects.equals(this.managementUrl, managementUrl);
|
||||
this.managementUrl = managementUrl;
|
||||
}
|
||||
|
||||
public String getRootUrl() {
|
||||
return rootUrl;
|
||||
}
|
||||
|
||||
public void setRootUrl(String rootUrl) {
|
||||
this.updated |= ! Objects.equals(this.rootUrl, rootUrl);
|
||||
this.rootUrl = rootUrl;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.updated |= ! Objects.equals(this.baseUrl, baseUrl);
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
||||
public void setBearerOnly(boolean bearerOnly) {
|
||||
this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly);
|
||||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
public boolean isConsentRequired() {
|
||||
return consentRequired;
|
||||
}
|
||||
|
||||
public void setConsentRequired(boolean consentRequired) {
|
||||
this.updated |= ! Objects.equals(this.consentRequired, consentRequired);
|
||||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public boolean isStandardFlowEnabled() {
|
||||
return standardFlowEnabled;
|
||||
}
|
||||
|
||||
public void setStandardFlowEnabled(boolean standardFlowEnabled) {
|
||||
this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled);
|
||||
this.standardFlowEnabled = standardFlowEnabled;
|
||||
}
|
||||
|
||||
public boolean isImplicitFlowEnabled() {
|
||||
return implicitFlowEnabled;
|
||||
}
|
||||
|
||||
public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
|
||||
this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled);
|
||||
this.implicitFlowEnabled = implicitFlowEnabled;
|
||||
}
|
||||
|
||||
public boolean isDirectAccessGrantsEnabled() {
|
||||
return directAccessGrantsEnabled;
|
||||
}
|
||||
|
||||
public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
|
||||
this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled);
|
||||
this.directAccessGrantsEnabled = directAccessGrantsEnabled;
|
||||
}
|
||||
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled);
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) {
|
||||
this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout);
|
||||
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void addWebOrigin(String webOrigin) {
|
||||
updated = true;
|
||||
this.webOrigins.add(webOrigin);
|
||||
}
|
||||
|
||||
public void removeWebOrigin(String webOrigin) {
|
||||
updated |= this.webOrigins.remove(webOrigin);
|
||||
}
|
||||
|
||||
public void addRedirectUri(String redirectUri) {
|
||||
this.updated |= ! this.redirectUris.contains(redirectUri);
|
||||
this.redirectUris.add(redirectUri);
|
||||
}
|
||||
|
||||
public void removeRedirectUri(String redirectUri) {
|
||||
updated |= this.redirectUris.remove(redirectUri);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getAuthenticationFlowBindingOverride(String binding) {
|
||||
return this.authFlowBindings.get(binding);
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthenticationFlowBindingOverrides() {
|
||||
return this.authFlowBindings;
|
||||
}
|
||||
|
||||
public void removeAuthenticationFlowBindingOverride(String binding) {
|
||||
updated |= this.authFlowBindings.remove(binding) != null;
|
||||
}
|
||||
|
||||
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
|
||||
this.updated = true;
|
||||
this.authFlowBindings.put(binding, flowId);
|
||||
}
|
||||
|
||||
public Collection<String> getScopeMappings() {
|
||||
return scopeMappings;
|
||||
}
|
||||
|
||||
public void addScopeMapping(String id) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
scopeMappings.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteScopeMapping(String id) {
|
||||
updated |= scopeMappings.remove(id);
|
||||
}
|
||||
|
||||
public void addClientScope(String id, boolean defaultScope) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
this.clientScopes.put(id, defaultScope);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeClientScope(String id) {
|
||||
if (id != null) {
|
||||
updated |= clientScopes.remove(id) != null;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<String> getClientScopes(boolean defaultScope) {
|
||||
return this.clientScopes.entrySet().stream()
|
||||
.filter(me -> Objects.equals(me.getValue(), defaultScope))
|
||||
.map(Entry::getKey);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
}
|
|
@ -33,17 +33,12 @@ import java.util.stream.Stream;
|
|||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public abstract class MapClientAdapter extends AbstractClientModel<MapClientEntity> implements ClientModel {
|
||||
public abstract class MapClientAdapter<K> extends AbstractClientModel<MapClientEntity<K>> implements ClientModel {
|
||||
|
||||
public MapClientAdapter(KeycloakSession session, RealmModel realm, MapClientEntity entity) {
|
||||
public MapClientAdapter(KeycloakSession session, RealmModel realm, MapClientEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return entity.getClientId();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
|
@ -16,20 +16,467 @@
|
|||
*/
|
||||
package org.keycloak.models.map.client;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class MapClientEntity extends AbstractClientEntity<UUID> {
|
||||
public class MapClientEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
private String clientId;
|
||||
private String name;
|
||||
private String description;
|
||||
private Set<String> redirectUris = new HashSet<>();
|
||||
private boolean enabled;
|
||||
private boolean alwaysDisplayInConsole;
|
||||
private String clientAuthenticatorType;
|
||||
private String secret;
|
||||
private String registrationToken;
|
||||
private String protocol;
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
private Map<String, String> authFlowBindings = new HashMap<>();
|
||||
private boolean publicClient;
|
||||
private boolean fullScopeAllowed;
|
||||
private boolean frontchannelLogout;
|
||||
private int notBefore;
|
||||
private Set<String> scope = new HashSet<>();
|
||||
private Set<String> webOrigins = new HashSet<>();
|
||||
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
|
||||
private Map<String, Boolean> clientScopes = new HashMap<>();
|
||||
private Set<String> scopeMappings = new LinkedHashSet<>();
|
||||
private boolean surrogateAuthRequired;
|
||||
private String managementUrl;
|
||||
private String rootUrl;
|
||||
private String baseUrl;
|
||||
private boolean bearerOnly;
|
||||
private boolean consentRequired;
|
||||
private boolean standardFlowEnabled;
|
||||
private boolean implicitFlowEnabled;
|
||||
private boolean directAccessGrantsEnabled;
|
||||
private boolean serviceAccountsEnabled;
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected MapClientEntity() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapClientEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapClientEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= ! Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Set<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
||||
public void setRedirectUris(Set<String> redirectUris) {
|
||||
this.updated |= ! Objects.equals(this.redirectUris, redirectUris);
|
||||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.updated |= ! Objects.equals(this.enabled, enabled);
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isAlwaysDisplayInConsole() {
|
||||
return alwaysDisplayInConsole;
|
||||
}
|
||||
|
||||
public void setAlwaysDisplayInConsole(boolean alwaysDisplayInConsole) {
|
||||
this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole);
|
||||
this.alwaysDisplayInConsole = alwaysDisplayInConsole;
|
||||
}
|
||||
|
||||
public String getClientAuthenticatorType() {
|
||||
return clientAuthenticatorType;
|
||||
}
|
||||
|
||||
public void setClientAuthenticatorType(String clientAuthenticatorType) {
|
||||
this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType);
|
||||
this.clientAuthenticatorType = clientAuthenticatorType;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.updated |= ! Objects.equals(this.secret, secret);
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getRegistrationToken() {
|
||||
return registrationToken;
|
||||
}
|
||||
|
||||
public void setRegistrationToken(String registrationToken) {
|
||||
this.updated |= ! Objects.equals(this.registrationToken, registrationToken);
|
||||
this.registrationToken = registrationToken;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.updated |= ! Objects.equals(this.protocol, protocol);
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthFlowBindings() {
|
||||
return authFlowBindings;
|
||||
}
|
||||
|
||||
public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
|
||||
this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings);
|
||||
this.authFlowBindings = authFlowBindings;
|
||||
}
|
||||
|
||||
public boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(boolean publicClient) {
|
||||
this.updated |= ! Objects.equals(this.publicClient, publicClient);
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(boolean fullScopeAllowed) {
|
||||
this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed);
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
public boolean isFrontchannelLogout() {
|
||||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public void setFrontchannelLogout(boolean frontchannelLogout) {
|
||||
this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout);
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.updated |= ! Objects.equals(this.notBefore, notBefore);
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public Set<String> getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(Set<String> scope) {
|
||||
this.updated |= ! Objects.equals(this.scope, scope);
|
||||
this.scope.clear();
|
||||
this.scope.addAll(scope);
|
||||
}
|
||||
|
||||
public Set<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
||||
public void setWebOrigins(Set<String> webOrigins) {
|
||||
this.updated |= ! Objects.equals(this.webOrigins, webOrigins);
|
||||
this.webOrigins.clear();
|
||||
this.webOrigins.addAll(webOrigins);
|
||||
}
|
||||
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
Objects.requireNonNull(model.getId(), "protocolMapper.id");
|
||||
updated = true;
|
||||
this.protocolMappers.put(model.getId(), model);
|
||||
return model;
|
||||
}
|
||||
|
||||
public Collection<ProtocolMapperModel> getProtocolMappers() {
|
||||
return protocolMappers.values();
|
||||
}
|
||||
|
||||
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
|
||||
updated = true;
|
||||
protocolMappers.put(id, mapping);
|
||||
}
|
||||
|
||||
public void removeProtocolMapper(String id) {
|
||||
updated |= protocolMappers.remove(id) != null;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
|
||||
this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
|
||||
this.protocolMappers.clear();
|
||||
this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
|
||||
}
|
||||
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return id == null ? null : protocolMappers.get(id);
|
||||
}
|
||||
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
|
||||
this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired);
|
||||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public String getManagementUrl() {
|
||||
return managementUrl;
|
||||
}
|
||||
|
||||
public void setManagementUrl(String managementUrl) {
|
||||
this.updated |= ! Objects.equals(this.managementUrl, managementUrl);
|
||||
this.managementUrl = managementUrl;
|
||||
}
|
||||
|
||||
public String getRootUrl() {
|
||||
return rootUrl;
|
||||
}
|
||||
|
||||
public void setRootUrl(String rootUrl) {
|
||||
this.updated |= ! Objects.equals(this.rootUrl, rootUrl);
|
||||
this.rootUrl = rootUrl;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.updated |= ! Objects.equals(this.baseUrl, baseUrl);
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
||||
public void setBearerOnly(boolean bearerOnly) {
|
||||
this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly);
|
||||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
public boolean isConsentRequired() {
|
||||
return consentRequired;
|
||||
}
|
||||
|
||||
public void setConsentRequired(boolean consentRequired) {
|
||||
this.updated |= ! Objects.equals(this.consentRequired, consentRequired);
|
||||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public boolean isStandardFlowEnabled() {
|
||||
return standardFlowEnabled;
|
||||
}
|
||||
|
||||
public void setStandardFlowEnabled(boolean standardFlowEnabled) {
|
||||
this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled);
|
||||
this.standardFlowEnabled = standardFlowEnabled;
|
||||
}
|
||||
|
||||
public boolean isImplicitFlowEnabled() {
|
||||
return implicitFlowEnabled;
|
||||
}
|
||||
|
||||
public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
|
||||
this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled);
|
||||
this.implicitFlowEnabled = implicitFlowEnabled;
|
||||
}
|
||||
|
||||
public boolean isDirectAccessGrantsEnabled() {
|
||||
return directAccessGrantsEnabled;
|
||||
}
|
||||
|
||||
public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
|
||||
this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled);
|
||||
this.directAccessGrantsEnabled = directAccessGrantsEnabled;
|
||||
}
|
||||
|
||||
public boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
|
||||
this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled);
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) {
|
||||
this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout);
|
||||
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void addWebOrigin(String webOrigin) {
|
||||
updated = true;
|
||||
this.webOrigins.add(webOrigin);
|
||||
}
|
||||
|
||||
public void removeWebOrigin(String webOrigin) {
|
||||
updated |= this.webOrigins.remove(webOrigin);
|
||||
}
|
||||
|
||||
public void addRedirectUri(String redirectUri) {
|
||||
this.updated |= ! this.redirectUris.contains(redirectUri);
|
||||
this.redirectUris.add(redirectUri);
|
||||
}
|
||||
|
||||
public void removeRedirectUri(String redirectUri) {
|
||||
updated |= this.redirectUris.remove(redirectUri);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getAuthenticationFlowBindingOverride(String binding) {
|
||||
return this.authFlowBindings.get(binding);
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthenticationFlowBindingOverrides() {
|
||||
return this.authFlowBindings;
|
||||
}
|
||||
|
||||
public void removeAuthenticationFlowBindingOverride(String binding) {
|
||||
updated |= this.authFlowBindings.remove(binding) != null;
|
||||
}
|
||||
|
||||
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
|
||||
this.updated = true;
|
||||
this.authFlowBindings.put(binding, flowId);
|
||||
}
|
||||
|
||||
public Collection<String> getScopeMappings() {
|
||||
return scopeMappings;
|
||||
}
|
||||
|
||||
public void addScopeMapping(String id) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
scopeMappings.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteScopeMapping(String id) {
|
||||
updated |= scopeMappings.remove(id);
|
||||
}
|
||||
|
||||
public void addClientScope(String id, boolean defaultScope) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
this.clientScopes.put(id, defaultScope);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeClientScope(String id) {
|
||||
if (id != null) {
|
||||
updated |= clientScopes.remove(id) != null;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<String> getClientScopes(boolean defaultScope) {
|
||||
return this.clientScopes.entrySet().stream()
|
||||
.filter(me -> Objects.equals(me.getValue(), defaultScope))
|
||||
.map(Entry::getKey);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,11 +30,9 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
@ -50,18 +48,17 @@ import org.keycloak.models.ClientScopeModel;
|
|||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapClientProvider implements ClientProvider {
|
||||
public class MapClientProvider<K> implements ClientProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapClientProvider.class);
|
||||
private static final Predicate<MapClientEntity> ALWAYS_FALSE = c -> { return false; };
|
||||
private final KeycloakSession session;
|
||||
final MapKeycloakTransaction<UUID, MapClientEntity, ClientModel> tx;
|
||||
private final MapStorage<UUID, MapClientEntity, ClientModel> clientStore;
|
||||
private final ConcurrentMap<UUID, ConcurrentMap<String, Integer>> clientRegisteredNodesStore;
|
||||
final MapKeycloakTransaction<K, MapClientEntity<K>, ClientModel> tx;
|
||||
private final MapStorage<K, MapClientEntity<K>, ClientModel> clientStore;
|
||||
private final ConcurrentMap<K, ConcurrentMap<String, Integer>> clientRegisteredNodesStore;
|
||||
|
||||
private static final Comparator<MapClientEntity> COMPARE_BY_CLIENT_ID = Comparator.comparing(MapClientEntity::getClientId);
|
||||
|
||||
public MapClientProvider(KeycloakSession session, MapStorage<UUID, MapClientEntity, ClientModel> clientStore, ConcurrentMap<UUID, ConcurrentMap<String, Integer>> clientRegisteredNodesStore) {
|
||||
public MapClientProvider(KeycloakSession session, MapStorage<K, MapClientEntity<K>, ClientModel> clientStore, ConcurrentMap<K, ConcurrentMap<String, Integer>> clientRegisteredNodesStore) {
|
||||
this.session = session;
|
||||
this.clientStore = clientStore;
|
||||
this.clientRegisteredNodesStore = clientRegisteredNodesStore;
|
||||
|
@ -83,16 +80,21 @@ public class MapClientProvider implements ClientProvider {
|
|||
};
|
||||
}
|
||||
|
||||
private MapClientEntity registerEntityForChanges(MapClientEntity origEntity) {
|
||||
final MapClientEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapClientEntity::isUpdated);
|
||||
private MapClientEntity<K> registerEntityForChanges(MapClientEntity<K> origEntity) {
|
||||
final MapClientEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapClientEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Function<MapClientEntity, ClientModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapClientEntity<K>, ClientModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
||||
return origEntity -> new MapClientAdapter(session, realm, registerEntityForChanges(origEntity)) {
|
||||
return origEntity -> new MapClientAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return clientStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateClient() {
|
||||
LOG.tracef("updateClient(%s)%s", realm, origEntity.getId(), getShortStackTrace());
|
||||
|
@ -119,9 +121,9 @@ public class MapClientProvider implements ClientProvider {
|
|||
};
|
||||
}
|
||||
|
||||
private Predicate<MapClientEntity> entityRealmFilter(RealmModel realm) {
|
||||
private Predicate<MapClientEntity<K>> entityRealmFilter(RealmModel realm) {
|
||||
if (realm == null || realm.getId() == null) {
|
||||
return MapClientProvider.ALWAYS_FALSE;
|
||||
return c -> false;
|
||||
}
|
||||
String realmId = realm.getId();
|
||||
return entity -> Objects.equals(realmId, entity.getRealmId());
|
||||
|
@ -145,7 +147,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? clientStore.getKeyConvertor().yieldNewUniqueKey() : clientStore.getKeyConvertor().fromString(id);
|
||||
|
||||
if (clientId == null) {
|
||||
clientId = entityId.toString();
|
||||
|
@ -153,7 +155,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace());
|
||||
|
||||
MapClientEntity entity = new MapClientEntity(entityId, realm.getId());
|
||||
MapClientEntity<K> entity = new MapClientEntity<>(entityId, realm.getId());
|
||||
entity.setClientId(clientId);
|
||||
entity.setEnabled(true);
|
||||
entity.setStandardFlowEnabled(true);
|
||||
|
@ -213,7 +215,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
});
|
||||
// TODO: ^^^^^^^ Up to here
|
||||
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(clientStore.getKeyConvertor().fromString(id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -234,7 +236,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
LOG.tracef("getClientById(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
MapClientEntity entity = tx.read(UUID.fromString(id));
|
||||
MapClientEntity<K> entity = tx.read(clientStore.getKeyConvertor().fromStringSafe(id));
|
||||
return (entity == null || ! entityRealmFilter(realm).test(entity))
|
||||
? null
|
||||
: entityToAdapterFunc(realm).apply(entity);
|
||||
|
@ -268,7 +270,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.CLIENT_ID, Operator.ILIKE, "%" + clientId + "%");
|
||||
|
||||
Stream<MapClientEntity> s = tx.getUpdatedNotRemoved(mcb)
|
||||
Stream<MapClientEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(COMPARE_BY_CLIENT_ID);
|
||||
|
||||
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
|
||||
|
@ -276,7 +278,8 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
@Override
|
||||
public void addClientScopes(RealmModel realm, ClientModel client, Set<ClientScopeModel> clientScopes, boolean defaultScope) {
|
||||
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
|
||||
final String id = client.getId();
|
||||
MapClientEntity<K> entity = tx.read(clientStore.getKeyConvertor().fromString(id));
|
||||
|
||||
if (entity == null) return;
|
||||
|
||||
|
@ -295,7 +298,8 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
@Override
|
||||
public void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope) {
|
||||
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
|
||||
final String id = client.getId();
|
||||
MapClientEntity<K> entity = tx.read(clientStore.getKeyConvertor().fromString(id));
|
||||
|
||||
if (entity == null) return;
|
||||
|
||||
|
@ -306,7 +310,8 @@ public class MapClientProvider implements ClientProvider {
|
|||
|
||||
@Override
|
||||
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScopes) {
|
||||
MapClientEntity entity = tx.read(UUID.fromString(client.getId()));
|
||||
final String id = client.getId();
|
||||
MapClientEntity<K> entity = tx.read(clientStore.getKeyConvertor().fromString(id));
|
||||
|
||||
if (entity == null) return null;
|
||||
|
||||
|
@ -326,7 +331,7 @@ public class MapClientProvider implements ClientProvider {
|
|||
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.SCOPE_MAPPING_ROLE, Operator.EQ, role.getId());
|
||||
try (Stream<MapClientEntity> toRemove = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapClientEntity<K>> toRemove = tx.getUpdatedNotRemoved(mcb)) {
|
||||
toRemove
|
||||
.map(clientEntity -> session.clients().getClientById(realm, clientEntity.getId().toString()))
|
||||
.filter(Objects::nonNull)
|
||||
|
|
|
@ -26,40 +26,34 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class MapClientProviderFactory extends AbstractMapProviderFactory<ClientProvider> implements ClientProviderFactory, ProviderEventListener {
|
||||
public class MapClientProviderFactory<K> extends AbstractMapProviderFactory<ClientProvider, K, MapClientEntity<K>, ClientModel> implements ClientProviderFactory, ProviderEventListener {
|
||||
|
||||
private final ConcurrentHashMap<UUID, ConcurrentMap<String, Integer>> REGISTERED_NODES_STORE = new ConcurrentHashMap<>();
|
||||
|
||||
private MapStorage<UUID, MapClientEntity, ClientModel> store;
|
||||
private final ConcurrentHashMap<K, ConcurrentMap<String, Integer>> REGISTERED_NODES_STORE = new ConcurrentHashMap<>();
|
||||
|
||||
private Runnable onClose;
|
||||
|
||||
public MapClientProviderFactory() {
|
||||
super(MapClientEntity.class, ClientModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("clients", UUID.class, MapClientEntity.class, ClientModel.class);
|
||||
|
||||
factory.register(this);
|
||||
onClose = () -> factory.unregister(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MapClientProvider create(KeycloakSession session) {
|
||||
return new MapClientProvider(session, store, REGISTERED_NODES_STORE);
|
||||
return new MapClientProvider<>(session, getStorage(session), REGISTERED_NODES_STORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,6 +62,11 @@ public class MapClientProviderFactory extends AbstractMapProviderFactory<ClientP
|
|||
onClose.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Client provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
if (event instanceof RoleContainerModel.RoleRemovedEvent) {
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.clientscope;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public abstract class AbstractClientScopeEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String name;
|
||||
private String protocol;
|
||||
private String description;
|
||||
|
||||
private final Set<String> scopeMappings = new LinkedHashSet<>();
|
||||
private final Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected AbstractClientScopeEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractClientScopeEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.updated |= ! Objects.equals(this.protocol, protocol);
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes.clear();
|
||||
this.attributes.putAll(attributes);
|
||||
}
|
||||
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
Objects.requireNonNull(model.getId(), "protocolMapper.id");
|
||||
updated = true;
|
||||
this.protocolMappers.put(model.getId(), model);
|
||||
return model;
|
||||
}
|
||||
|
||||
public Stream<ProtocolMapperModel> getProtocolMappers() {
|
||||
return protocolMappers.values().stream();
|
||||
}
|
||||
|
||||
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
|
||||
updated = true;
|
||||
protocolMappers.put(id, mapping);
|
||||
}
|
||||
|
||||
public void removeProtocolMapper(String id) {
|
||||
updated |= protocolMappers.remove(id) != null;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
|
||||
this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
|
||||
this.protocolMappers.clear();
|
||||
this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
|
||||
}
|
||||
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return id == null ? null : protocolMappers.get(id);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
public Stream<String> getScopeMappings() {
|
||||
return scopeMappings.stream();
|
||||
}
|
||||
|
||||
public void addScopeMapping(String id) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
scopeMappings.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteScopeMapping(String id) {
|
||||
updated |= scopeMappings.remove(id);
|
||||
}
|
||||
}
|
|
@ -28,17 +28,12 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
|
||||
public class MapClientScopeAdapter extends AbstractClientScopeModel<MapClientScopeEntity> implements ClientScopeModel {
|
||||
public abstract class MapClientScopeAdapter<K> extends AbstractClientScopeModel<MapClientScopeEntity<K>> implements ClientScopeModel {
|
||||
|
||||
public MapClientScopeAdapter(KeycloakSession session, RealmModel realm, MapClientScopeEntity entity) {
|
||||
public MapClientScopeAdapter(KeycloakSession session, RealmModel realm, MapClientScopeEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* Copyright 2021 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.
|
||||
|
@ -16,16 +16,155 @@
|
|||
*/
|
||||
package org.keycloak.models.map.clientscope;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public class MapClientScopeEntity extends AbstractClientScopeEntity<UUID> {
|
||||
public class MapClientScopeEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private MapClientScopeEntity() {
|
||||
super();
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String name;
|
||||
private String protocol;
|
||||
private String description;
|
||||
|
||||
private final Set<String> scopeMappings = new LinkedHashSet<>();
|
||||
private final Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected MapClientScopeEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapClientScopeEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapClientScopeEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.updated |= ! Objects.equals(this.protocol, protocol);
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes.clear();
|
||||
this.attributes.putAll(attributes);
|
||||
}
|
||||
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
Objects.requireNonNull(model.getId(), "protocolMapper.id");
|
||||
updated = true;
|
||||
this.protocolMappers.put(model.getId(), model);
|
||||
return model;
|
||||
}
|
||||
|
||||
public Stream<ProtocolMapperModel> getProtocolMappers() {
|
||||
return protocolMappers.values().stream();
|
||||
}
|
||||
|
||||
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
|
||||
updated = true;
|
||||
protocolMappers.put(id, mapping);
|
||||
}
|
||||
|
||||
public void removeProtocolMapper(String id) {
|
||||
updated |= protocolMappers.remove(id) != null;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
|
||||
this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
|
||||
this.protocolMappers.clear();
|
||||
this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
|
||||
}
|
||||
|
||||
public ProtocolMapperModel getProtocolMapperById(String id) {
|
||||
return id == null ? null : protocolMappers.get(id);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
public Stream<String> getScopeMappings() {
|
||||
return scopeMappings.stream();
|
||||
}
|
||||
|
||||
public void addScopeMapping(String id) {
|
||||
if (id != null) {
|
||||
updated = true;
|
||||
scopeMappings.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteScopeMapping(String id) {
|
||||
updated |= scopeMappings.remove(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.models.map.clientscope;
|
|||
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -40,38 +39,42 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
public class MapClientScopeProvider implements ClientScopeProvider {
|
||||
public class MapClientScopeProvider<K> implements ClientScopeProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapClientScopeProvider.class);
|
||||
private static final Predicate<MapClientScopeEntity> ALWAYS_FALSE = c -> { return false; };
|
||||
private final KeycloakSession session;
|
||||
private final MapKeycloakTransaction<UUID, MapClientScopeEntity, ClientScopeModel> tx;
|
||||
private final MapStorage<UUID, MapClientScopeEntity, ClientScopeModel> clientScopeStore;
|
||||
private final MapKeycloakTransaction<K, MapClientScopeEntity<K>, ClientScopeModel> tx;
|
||||
private final MapStorage<K, MapClientScopeEntity<K>, ClientScopeModel> clientScopeStore;
|
||||
|
||||
private static final Comparator<MapClientScopeEntity> COMPARE_BY_NAME = Comparator.comparing(MapClientScopeEntity::getName);
|
||||
|
||||
public MapClientScopeProvider(KeycloakSession session, MapStorage<UUID, MapClientScopeEntity, ClientScopeModel> clientScopeStore) {
|
||||
public MapClientScopeProvider(KeycloakSession session, MapStorage<K, MapClientScopeEntity<K>, ClientScopeModel> clientScopeStore) {
|
||||
this.session = session;
|
||||
this.clientScopeStore = clientScopeStore;
|
||||
this.tx = clientScopeStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapClientScopeEntity registerEntityForChanges(MapClientScopeEntity origEntity) {
|
||||
final MapClientScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapClientScopeEntity::isUpdated);
|
||||
private MapClientScopeEntity<K> registerEntityForChanges(MapClientScopeEntity<K> origEntity) {
|
||||
final MapClientScopeEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapClientScopeEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Function<MapClientScopeEntity, ClientScopeModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapClientScopeEntity<K>, ClientScopeModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
||||
return origEntity -> new MapClientScopeAdapter(session, realm, registerEntityForChanges(origEntity));
|
||||
return origEntity -> new MapClientScopeAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return clientScopeStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Predicate<MapClientScopeEntity> entityRealmFilter(RealmModel realm) {
|
||||
private Predicate<MapClientScopeEntity<K>> entityRealmFilter(RealmModel realm) {
|
||||
if (realm == null || realm.getId() == null) {
|
||||
return MapClientScopeProvider.ALWAYS_FALSE;
|
||||
return c -> false;
|
||||
}
|
||||
String realmId = realm.getId();
|
||||
return entity -> Objects.equals(realmId, entity.getRealmId());
|
||||
|
@ -98,11 +101,11 @@ public class MapClientScopeProvider implements ClientScopeProvider {
|
|||
throw new ModelDuplicateException("Client scope with name '" + name + "' in realm " + realm.getName());
|
||||
}
|
||||
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? clientScopeStore.getKeyConvertor().yieldNewUniqueKey() : clientScopeStore.getKeyConvertor().fromString(id);
|
||||
|
||||
LOG.tracef("addClientScope(%s, %s, %s)%s", realm, id, name, getShortStackTrace());
|
||||
|
||||
MapClientScopeEntity entity = new MapClientScopeEntity(entityId, realm.getId());
|
||||
MapClientScopeEntity<K> entity = new MapClientScopeEntity<>(entityId, realm.getId());
|
||||
entity.setName(KeycloakModelUtils.convertClientScopeName(name));
|
||||
if (tx.read(entity.getId()) != null) {
|
||||
throw new ModelDuplicateException("Client scope exists: " + id);
|
||||
|
@ -137,7 +140,7 @@ public class MapClientScopeProvider implements ClientScopeProvider {
|
|||
}
|
||||
});
|
||||
|
||||
tx.delete(UUID.fromString(id));
|
||||
tx.delete(clientScopeStore.getKeyConvertor().fromString(id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -159,14 +162,14 @@ public class MapClientScopeProvider implements ClientScopeProvider {
|
|||
|
||||
LOG.tracef("getClientScopeById(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
UUID uuid;
|
||||
K uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(id);
|
||||
uuid = clientScopeStore.getKeyConvertor().fromStringSafe(id);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapClientScopeEntity entity = tx.read(uuid);
|
||||
MapClientScopeEntity<K> entity = tx.read(uuid);
|
||||
return (entity == null || ! entityRealmFilter(realm).test(entity))
|
||||
? null
|
||||
: entityToAdapterFunc(realm).apply(entity);
|
||||
|
|
|
@ -16,28 +16,25 @@
|
|||
*/
|
||||
package org.keycloak.models.map.clientscope;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.ClientScopeProvider;
|
||||
import org.keycloak.models.ClientScopeProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
|
||||
public class MapClientScopeProviderFactory extends AbstractMapProviderFactory<ClientScopeProvider> implements ClientScopeProviderFactory {
|
||||
public class MapClientScopeProviderFactory<K> extends AbstractMapProviderFactory<ClientScopeProvider, K, MapClientScopeEntity<K>, ClientScopeModel> implements ClientScopeProviderFactory {
|
||||
|
||||
private MapStorage<UUID, MapClientScopeEntity, ClientScopeModel> store;
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProvider sp = (MapStorageProvider) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("clientscope", UUID.class, MapClientScopeEntity.class, ClientScopeModel.class);
|
||||
public MapClientScopeProviderFactory() {
|
||||
super(MapClientScopeEntity.class, ClientScopeModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientScopeProvider create(KeycloakSession session) {
|
||||
return new MapClientScopeProvider(session, store);
|
||||
return new MapClientScopeProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Client scope provider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,23 +17,52 @@
|
|||
package org.keycloak.models.map.common;
|
||||
|
||||
import org.keycloak.Config.Scope;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorageSpi;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.jboss.logging.Logger;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public abstract class AbstractMapProviderFactory<T extends Provider> implements ProviderFactory<T> {
|
||||
public abstract class AbstractMapProviderFactory<T extends Provider, K, V extends AbstractEntity<K>, M> implements AmphibianProviderFactory<T> {
|
||||
|
||||
public static final String PROVIDER_ID = "map";
|
||||
|
||||
public static final String CONFIG_STORAGE = "storage";
|
||||
|
||||
protected final Logger LOG = Logger.getLogger(getClass());
|
||||
|
||||
protected final Class<M> modelType;
|
||||
|
||||
protected final Class<V> entityType;
|
||||
|
||||
private Scope storageConfigScope;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected AbstractMapProviderFactory(Class<? extends AbstractEntity> entityType, Class<M> modelType) {
|
||||
this.modelType = modelType;
|
||||
this.entityType = (Class<V>) entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Scope config) {
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
protected MapStorage<K, V, M> getStorage(KeycloakSession session) {
|
||||
MapStorageProviderFactory storageProviderFactory = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
|
||||
MapStorageProvider.class, storageConfigScope, MapStorageSpi.NAME);
|
||||
final MapStorageProvider factory = storageProviderFactory.create(session);
|
||||
|
||||
return factory.getStorage(entityType, modelType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,11 +70,8 @@ public abstract class AbstractMapProviderFactory<T extends Provider> implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
public void init(Scope config) {
|
||||
// Implementation of the map storage SPI
|
||||
this.storageConfigScope = config.scope(CONFIG_STORAGE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ package org.keycloak.models.map.common;
|
|||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -49,7 +52,12 @@ public class Serialization {
|
|||
public static final ConcurrentHashMap<Class<?>, ObjectReader> READERS = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentHashMap<Class<?>, ObjectWriter> WRITERS = new ConcurrentHashMap<>();
|
||||
|
||||
abstract class IgnoreUpdatedMixIn { @JsonIgnore public abstract boolean isUpdated(); }
|
||||
abstract class IgnoreUpdatedMixIn {
|
||||
@JsonIgnore public abstract boolean isUpdated();
|
||||
|
||||
@JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY)
|
||||
abstract Object getId();
|
||||
}
|
||||
|
||||
static {
|
||||
JavaType type = TypeFactory.unknownType();
|
||||
|
|
|
@ -1,134 +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.map.group;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public abstract class AbstractGroupEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String name;
|
||||
private String parentId;
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
private Set<String> grantedRoles = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected AbstractGroupEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractGroupEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.updated |= !Objects.equals(this.parentId, parentId);
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !this.attributes.containsKey(name) || !this.attributes.get(name).equals(value);
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
public Set<String> getGrantedRoles() {
|
||||
return grantedRoles;
|
||||
}
|
||||
|
||||
public void setGrantedRoles(Set<String> grantedRoles) {
|
||||
this.updated |= !Objects.equals(this.grantedRoles, grantedRoles);
|
||||
this.grantedRoles = grantedRoles;
|
||||
}
|
||||
|
||||
public void removeRole(String role) {
|
||||
this.updated |= this.grantedRoles.contains(role);
|
||||
this.grantedRoles.remove(role);
|
||||
}
|
||||
|
||||
public void addGrantedRole(String role) {
|
||||
this.updated |= !this.grantedRoles.contains(role);
|
||||
this.grantedRoles.add(role);
|
||||
}
|
||||
}
|
|
@ -29,16 +29,11 @@ import java.util.Map;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public class MapGroupAdapter extends AbstractGroupModel<MapGroupEntity> {
|
||||
public MapGroupAdapter(KeycloakSession session, RealmModel realm, MapGroupEntity entity) {
|
||||
public abstract class MapGroupAdapter<K> extends AbstractGroupModel<MapGroupEntity<K>> {
|
||||
public MapGroupAdapter(KeycloakSession session, RealmModel realm, MapGroupEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
|
@ -17,15 +17,118 @@
|
|||
|
||||
package org.keycloak.models.map.group;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public class MapGroupEntity extends AbstractGroupEntity<UUID> {
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapGroupEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String name;
|
||||
private String parentId;
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
private Set<String> grantedRoles = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected MapGroupEntity() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapGroupEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapGroupEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.updated |= !Objects.equals(this.parentId, parentId);
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !this.attributes.containsKey(name) || !this.attributes.get(name).equals(value);
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return this.realmId;
|
||||
}
|
||||
|
||||
public Set<String> getGrantedRoles() {
|
||||
return grantedRoles;
|
||||
}
|
||||
|
||||
public void setGrantedRoles(Set<String> grantedRoles) {
|
||||
this.updated |= !Objects.equals(this.grantedRoles, grantedRoles);
|
||||
this.grantedRoles = grantedRoles;
|
||||
}
|
||||
|
||||
public void removeRole(String role) {
|
||||
this.updated |= this.grantedRoles.contains(role);
|
||||
this.grantedRoles.remove(role);
|
||||
}
|
||||
|
||||
public void addGrantedRole(String role) {
|
||||
this.updated |= !this.grantedRoles.contains(role);
|
||||
this.grantedRoles.add(role);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -41,29 +40,34 @@ import java.util.stream.Stream;
|
|||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapGroupProvider implements GroupProvider {
|
||||
public class MapGroupProvider<K> implements GroupProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapGroupProvider.class);
|
||||
private final KeycloakSession session;
|
||||
final MapKeycloakTransaction<UUID, MapGroupEntity, GroupModel> tx;
|
||||
private final MapStorage<UUID, MapGroupEntity, GroupModel> groupStore;
|
||||
final MapKeycloakTransaction<K, MapGroupEntity<K>, GroupModel> tx;
|
||||
private final MapStorage<K, MapGroupEntity<K>, GroupModel> groupStore;
|
||||
|
||||
public MapGroupProvider(KeycloakSession session, MapStorage<UUID, MapGroupEntity, GroupModel> groupStore) {
|
||||
public MapGroupProvider(KeycloakSession session, MapStorage<K, MapGroupEntity<K>, GroupModel> groupStore) {
|
||||
this.session = session;
|
||||
this.groupStore = groupStore;
|
||||
this.tx = groupStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapGroupEntity registerEntityForChanges(MapGroupEntity origEntity) {
|
||||
final MapGroupEntity res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapGroupEntity::isUpdated);
|
||||
private MapGroupEntity<K> registerEntityForChanges(MapGroupEntity<K> origEntity) {
|
||||
final MapGroupEntity<K> res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapGroupEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Function<MapGroupEntity, GroupModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapGroupEntity<K>, GroupModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return origEntity -> new MapGroupAdapter(session, realm, registerEntityForChanges(origEntity));
|
||||
return origEntity -> new MapGroupAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return groupStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,15 +78,14 @@ public class MapGroupProvider implements GroupProvider {
|
|||
|
||||
LOG.tracef("getGroupById(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
|
||||
UUID uid;
|
||||
K uid;
|
||||
try {
|
||||
uid = UUID.fromString(id);
|
||||
uid = groupStore.getKeyConvertor().fromStringSafe(id);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapGroupEntity entity = tx.read(uid);
|
||||
MapGroupEntity<K> entity = tx.read(uid);
|
||||
String realmId = realm.getId();
|
||||
return (entity == null || ! Objects.equals(realmId, entity.getRealmId()))
|
||||
? null
|
||||
|
@ -112,7 +115,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
@Override
|
||||
public Stream<GroupModel> getGroupsStream(RealmModel realm, Stream<String> ids, String search, Integer first, Integer max) {
|
||||
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.ID, Operator.IN, ids.map(UUID::fromString))
|
||||
.compare(SearchableFields.ID, Operator.IN, ids.map(groupStore.getKeyConvertor()::fromString))
|
||||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
|
||||
|
||||
if (search != null) {
|
||||
|
@ -188,7 +191,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
@Override
|
||||
public GroupModel createGroup(RealmModel realm, String id, String name, GroupModel toParent) {
|
||||
LOG.tracef("createGroup(%s, %s, %s, %s)%s", realm, id, name, toParent, getShortStackTrace());
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? groupStore.getKeyConvertor().yieldNewUniqueKey() : groupStore.getKeyConvertor().fromString(id);
|
||||
|
||||
// Check Db constraint: uniqueConstraints = { @UniqueConstraint(columnNames = {"REALM_ID", "PARENT_GROUP", "NAME"})}
|
||||
String parentId = toParent == null ? null : toParent.getId();
|
||||
|
@ -201,7 +204,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
throw new ModelDuplicateException("Group with name '" + name + "' in realm " + realm.getName() + " already exists for requested parent" );
|
||||
}
|
||||
|
||||
MapGroupEntity entity = new MapGroupEntity(entityId, realm.getId());
|
||||
MapGroupEntity<K> entity = new MapGroupEntity<K>(entityId, realm.getId());
|
||||
entity.setName(name);
|
||||
entity.setParentId(toParent == null ? null : toParent.getId());
|
||||
if (tx.read(entity.getId()) != null) {
|
||||
|
@ -243,7 +246,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
|
||||
// TODO: ^^^^^^^ Up to here
|
||||
|
||||
tx.delete(UUID.fromString(group.getId()));
|
||||
tx.delete(groupStore.getKeyConvertor().fromString(group.getId()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -264,7 +267,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
.compare(SearchableFields.PARENT_ID, Operator.EQ, parentId)
|
||||
.compare(SearchableFields.NAME, Operator.EQ, group.getName());
|
||||
|
||||
try (Stream<MapGroupEntity> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
|
||||
if (possibleSiblings.findAny().isPresent()) {
|
||||
throw new ModelDuplicateException("Parent already contains subgroup named '" + group.getName() + "'");
|
||||
}
|
||||
|
@ -286,7 +289,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
.compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null)
|
||||
.compare(SearchableFields.NAME, Operator.EQ, subGroup.getName());
|
||||
|
||||
try (Stream<MapGroupEntity> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
|
||||
if (possibleSiblings.findAny().isPresent()) {
|
||||
throw new ModelDuplicateException("There is already a top level group named '" + subGroup.getName() + "'");
|
||||
}
|
||||
|
@ -300,7 +303,7 @@ public class MapGroupProvider implements GroupProvider {
|
|||
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId());
|
||||
try (Stream<MapGroupEntity> toRemove = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapGroupEntity<K>> toRemove = tx.getUpdatedNotRemoved(mcb)) {
|
||||
toRemove
|
||||
.map(groupEntity -> session.groups().getGroupById(realm, groupEntity.getId().toString()))
|
||||
.forEach(groupModel -> groupModel.deleteRoleMapping(role));
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.group;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -27,38 +26,32 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapGroupProviderFactory extends AbstractMapProviderFactory<GroupProvider> implements GroupProviderFactory, ProviderEventListener {
|
||||
|
||||
private MapStorage<UUID, MapGroupEntity, GroupModel> store;
|
||||
public class MapGroupProviderFactory<K> extends AbstractMapProviderFactory<GroupProvider, K, MapGroupEntity<K>, GroupModel> implements GroupProviderFactory, ProviderEventListener {
|
||||
|
||||
private Runnable onClose;
|
||||
|
||||
public MapGroupProviderFactory() {
|
||||
super(MapGroupEntity.class, GroupModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("groups", UUID.class, MapGroupEntity.class, GroupModel.class);
|
||||
|
||||
factory.register(this);
|
||||
onClose = () -> factory.unregister(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MapGroupProvider create(KeycloakSession session) {
|
||||
return new MapGroupProvider(session, store);
|
||||
return new MapGroupProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -67,6 +60,11 @@ public class MapGroupProviderFactory extends AbstractMapProviderFactory<GroupPro
|
|||
onClose.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Group provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
if (event instanceof RoleContainerModel.RoleRemovedEvent) {
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.loginFailure;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractUserLoginFailureEntity<K> implements AbstractEntity<K> {
|
||||
private K id;
|
||||
private String realmId;
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private int failedLoginNotBefore;
|
||||
private int numFailures;
|
||||
private long lastFailure;
|
||||
private String lastIPFailure;
|
||||
|
||||
public AbstractUserLoginFailureEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
this.userId = null;
|
||||
}
|
||||
|
||||
public AbstractUserLoginFailureEntity(K id, String realmId, String userId) {
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.updated |= !Objects.equals(this.userId, userId);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public int getFailedLoginNotBefore() {
|
||||
return failedLoginNotBefore;
|
||||
}
|
||||
|
||||
public void setFailedLoginNotBefore(int failedLoginNotBefore) {
|
||||
this.updated |= this.failedLoginNotBefore != failedLoginNotBefore;
|
||||
this.failedLoginNotBefore = failedLoginNotBefore;
|
||||
}
|
||||
|
||||
public int getNumFailures() {
|
||||
return numFailures;
|
||||
}
|
||||
|
||||
public void setNumFailures(int numFailures) {
|
||||
this.updated |= this.numFailures != numFailures;
|
||||
this.numFailures = numFailures;
|
||||
}
|
||||
|
||||
public long getLastFailure() {
|
||||
return lastFailure;
|
||||
}
|
||||
|
||||
public void setLastFailure(long lastFailure) {
|
||||
this.updated |= this.lastFailure != lastFailure;
|
||||
this.lastFailure = lastFailure;
|
||||
}
|
||||
|
||||
public String getLastIPFailure() {
|
||||
return lastIPFailure;
|
||||
}
|
||||
|
||||
public void setLastIPFailure(String lastIPFailure) {
|
||||
this.updated |= !Objects.equals(this.lastIPFailure, lastIPFailure);
|
||||
this.lastIPFailure = lastIPFailure;
|
||||
}
|
||||
|
||||
public void clearFailures() {
|
||||
this.updated |= this.failedLoginNotBefore != 0 || this.numFailures != 0 ||
|
||||
this.lastFailure != 0l || this.lastIPFailure != null;
|
||||
this.failedLoginNotBefore = this.numFailures = 0;
|
||||
this.lastFailure = 0l;
|
||||
this.lastIPFailure = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
|
@ -22,8 +22,8 @@ import org.keycloak.models.RealmModel;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserLoginFailureAdapter extends AbstractUserLoginFailureModel<MapUserLoginFailureEntity> {
|
||||
public MapUserLoginFailureAdapter(KeycloakSession session, RealmModel realm, MapUserLoginFailureEntity entity) {
|
||||
public abstract class MapUserLoginFailureAdapter<K> extends AbstractUserLoginFailureModel<MapUserLoginFailureEntity<K>> {
|
||||
public MapUserLoginFailureAdapter(KeycloakSession session, RealmModel realm, MapUserLoginFailureEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,17 +16,114 @@
|
|||
*/
|
||||
package org.keycloak.models.map.loginFailure;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserLoginFailureEntity extends AbstractUserLoginFailureEntity<UUID> {
|
||||
protected MapUserLoginFailureEntity() {
|
||||
super();
|
||||
public class MapUserLoginFailureEntity<K> implements AbstractEntity<K> {
|
||||
private K id;
|
||||
private String realmId;
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private int failedLoginNotBefore;
|
||||
private int numFailures;
|
||||
private long lastFailure;
|
||||
private String lastIPFailure;
|
||||
|
||||
public MapUserLoginFailureEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
this.userId = null;
|
||||
}
|
||||
|
||||
public MapUserLoginFailureEntity(UUID id, String realmId, String userId) {
|
||||
super(id, realmId, userId);
|
||||
public MapUserLoginFailureEntity(K id, String realmId, String userId) {
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.updated |= !Objects.equals(this.userId, userId);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public int getFailedLoginNotBefore() {
|
||||
return failedLoginNotBefore;
|
||||
}
|
||||
|
||||
public void setFailedLoginNotBefore(int failedLoginNotBefore) {
|
||||
this.updated |= this.failedLoginNotBefore != failedLoginNotBefore;
|
||||
this.failedLoginNotBefore = failedLoginNotBefore;
|
||||
}
|
||||
|
||||
public int getNumFailures() {
|
||||
return numFailures;
|
||||
}
|
||||
|
||||
public void setNumFailures(int numFailures) {
|
||||
this.updated |= this.numFailures != numFailures;
|
||||
this.numFailures = numFailures;
|
||||
}
|
||||
|
||||
public long getLastFailure() {
|
||||
return lastFailure;
|
||||
}
|
||||
|
||||
public void setLastFailure(long lastFailure) {
|
||||
this.updated |= this.lastFailure != lastFailure;
|
||||
this.lastFailure = lastFailure;
|
||||
}
|
||||
|
||||
public String getLastIPFailure() {
|
||||
return lastIPFailure;
|
||||
}
|
||||
|
||||
public void setLastIPFailure(String lastIPFailure) {
|
||||
this.updated |= !Objects.equals(this.lastIPFailure, lastIPFailure);
|
||||
this.lastIPFailure = lastIPFailure;
|
||||
}
|
||||
|
||||
public void clearFailures() {
|
||||
this.updated |= this.failedLoginNotBefore != 0 || this.numFailures != 0 ||
|
||||
this.lastFailure != 0l || this.lastIPFailure != null;
|
||||
this.failedLoginNotBefore = this.numFailures = 0;
|
||||
this.lastFailure = 0l;
|
||||
this.lastIPFailure = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
|||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
|
@ -34,14 +33,14 @@ import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserLoginFailureProvider implements UserLoginFailureProvider {
|
||||
public class MapUserLoginFailureProvider<K> implements UserLoginFailureProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapUserLoginFailureProvider.class);
|
||||
private final KeycloakSession session;
|
||||
protected final MapKeycloakTransaction<UUID, MapUserLoginFailureEntity, UserLoginFailureModel> userLoginFailureTx;
|
||||
private final MapStorage<UUID, MapUserLoginFailureEntity, UserLoginFailureModel> userLoginFailureStore;
|
||||
protected final MapKeycloakTransaction<K, MapUserLoginFailureEntity<K>, UserLoginFailureModel> userLoginFailureTx;
|
||||
private final MapStorage<K, MapUserLoginFailureEntity<K>, UserLoginFailureModel> userLoginFailureStore;
|
||||
|
||||
public MapUserLoginFailureProvider(KeycloakSession session, MapStorage<UUID, MapUserLoginFailureEntity, UserLoginFailureModel> userLoginFailureStore) {
|
||||
public MapUserLoginFailureProvider(KeycloakSession session, MapStorage<K, MapUserLoginFailureEntity<K>, UserLoginFailureModel> userLoginFailureStore) {
|
||||
this.session = session;
|
||||
this.userLoginFailureStore = userLoginFailureStore;
|
||||
|
||||
|
@ -49,14 +48,19 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider {
|
|||
session.getTransactionManager().enlistAfterCompletion(userLoginFailureTx);
|
||||
}
|
||||
|
||||
private Function<MapUserLoginFailureEntity, UserLoginFailureModel> userLoginFailureEntityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapUserLoginFailureEntity<K>, UserLoginFailureModel> userLoginFailureEntityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return origEntity -> new MapUserLoginFailureAdapter(session, realm, registerEntityForChanges(origEntity));
|
||||
return origEntity -> new MapUserLoginFailureAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return userLoginFailureStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MapUserLoginFailureEntity registerEntityForChanges(MapUserLoginFailureEntity origEntity) {
|
||||
MapUserLoginFailureEntity res = userLoginFailureTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
userLoginFailureTx.updateIfChanged(origEntity.getId(), res, MapUserLoginFailureEntity::isUpdated);
|
||||
private MapUserLoginFailureEntity<K> registerEntityForChanges(MapUserLoginFailureEntity<K> origEntity) {
|
||||
MapUserLoginFailureEntity<K> res = userLoginFailureTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
userLoginFailureTx.updateIfChanged(origEntity.getId(), res, MapUserLoginFailureEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -82,10 +86,10 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider {
|
|||
|
||||
LOG.tracef("addUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
|
||||
|
||||
MapUserLoginFailureEntity userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
|
||||
MapUserLoginFailureEntity<K> userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
|
||||
|
||||
if (userLoginFailureEntity == null) {
|
||||
userLoginFailureEntity = new MapUserLoginFailureEntity(UUID.randomUUID(), realm.getId(), userId);
|
||||
userLoginFailureEntity = new MapUserLoginFailureEntity<>(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), realm.getId(), userId);
|
||||
|
||||
userLoginFailureTx.create(userLoginFailureEntity.getId(), userLoginFailureEntity);
|
||||
}
|
||||
|
@ -101,7 +105,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider {
|
|||
|
||||
LOG.tracef("removeUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
|
||||
|
||||
userLoginFailureTx.delete(UUID.randomUUID(), mcb);
|
||||
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,7 +115,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider {
|
|||
|
||||
LOG.tracef("removeAllUserLoginFailures(%s)%s", realm, getShortStackTrace());
|
||||
|
||||
userLoginFailureTx.delete(UUID.randomUUID(), mcb);
|
||||
userLoginFailureTx.delete(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,46 +23,58 @@ import org.keycloak.models.UserLoginFailureProvider;
|
|||
import org.keycloak.models.UserLoginFailureProviderFactory;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserLoginFailureProviderFactory extends AbstractMapProviderFactory<UserLoginFailureProvider>
|
||||
implements UserLoginFailureProviderFactory {
|
||||
public class MapUserLoginFailureProviderFactory<K> extends AbstractMapProviderFactory<UserLoginFailureProvider, K, MapUserLoginFailureEntity<K>, UserLoginFailureModel>
|
||||
implements UserLoginFailureProviderFactory, ProviderEventListener {
|
||||
|
||||
private MapStorage<UUID, MapUserLoginFailureEntity, UserLoginFailureModel> userLoginFailureStore;
|
||||
private Runnable onClose;
|
||||
|
||||
public MapUserLoginFailureProviderFactory() {
|
||||
super(MapUserLoginFailureEntity.class, UserLoginFailureModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProvider sp = (MapStorageProvider) factory.getProviderFactory(MapStorageProvider.class);
|
||||
userLoginFailureStore = sp.getStorage("userLoginFailures", UUID.class, MapUserLoginFailureEntity.class, UserLoginFailureModel.class);
|
||||
|
||||
factory.register(event -> {
|
||||
if (event instanceof UserModel.UserRemovedEvent) {
|
||||
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
||||
|
||||
MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
|
||||
provider.removeUserLoginFailure(userRemovedEvent.getRealm(), userRemovedEvent.getUser().getId());
|
||||
}
|
||||
});
|
||||
|
||||
factory.register(event -> {
|
||||
if (event instanceof RealmModel.RealmRemovedEvent) {
|
||||
RealmModel.RealmRemovedEvent realmRemovedEvent = (RealmModel.RealmRemovedEvent) event;
|
||||
|
||||
MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(realmRemovedEvent.getKeycloakSession());
|
||||
provider.removeAllUserLoginFailures(realmRemovedEvent.getRealm());
|
||||
}
|
||||
});
|
||||
factory.register(this);
|
||||
onClose = () -> factory.unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapUserLoginFailureProvider create(KeycloakSession session) {
|
||||
return new MapUserLoginFailureProvider(session, userLoginFailureStore);
|
||||
public void close() {
|
||||
super.close();
|
||||
onClose.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapUserLoginFailureProvider<K> create(KeycloakSession session) {
|
||||
return new MapUserLoginFailureProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "User login failure provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
if (event instanceof UserModel.UserRemovedEvent) {
|
||||
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
||||
|
||||
MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
|
||||
provider.removeUserLoginFailure(userRemovedEvent.getRealm(), userRemovedEvent.getUser().getId());
|
||||
} else if (event instanceof RealmModel.RealmRemovedEvent) {
|
||||
RealmModel.RealmRemovedEvent realmRemovedEvent = (RealmModel.RealmRemovedEvent) event;
|
||||
|
||||
MapUserLoginFailureProvider provider = MapUserLoginFailureProviderFactory.this.create(realmRemovedEvent.getKeycloakSession());
|
||||
provider.removeAllUserLoginFailures(realmRemovedEvent.getRealm());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -60,7 +60,7 @@ import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity;
|
|||
import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity;
|
||||
import org.keycloak.models.utils.ComponentUtil;
|
||||
|
||||
public class MapRealmAdapter extends AbstractRealmModel<MapRealmEntity> implements RealmModel {
|
||||
public abstract class MapRealmAdapter<K> extends AbstractRealmModel<MapRealmEntity<K>> implements RealmModel {
|
||||
|
||||
private static final String ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN = "actionTokenGeneratedByUserLifespan";
|
||||
private static final String DEFAULT_SIGNATURE_ALGORITHM = "defaultSignatureAlgorithm";
|
||||
|
@ -75,15 +75,10 @@ public class MapRealmAdapter extends AbstractRealmModel<MapRealmEntity> implemen
|
|||
|
||||
private PasswordPolicy passwordPolicy;
|
||||
|
||||
public MapRealmAdapter(KeycloakSession session, MapRealmEntity entity) {
|
||||
public MapRealmAdapter(KeycloakSession session, MapRealmEntity<K> entity) {
|
||||
super(session, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -39,29 +39,34 @@ import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
public class MapRealmProvider implements RealmProvider {
|
||||
public class MapRealmProvider<K> implements RealmProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapRealmProvider.class);
|
||||
private final KeycloakSession session;
|
||||
final MapKeycloakTransaction<String, MapRealmEntity, RealmModel> tx;
|
||||
private final MapStorage<String, MapRealmEntity, RealmModel> realmStore;
|
||||
final MapKeycloakTransaction<K, MapRealmEntity<K>, RealmModel> tx;
|
||||
private final MapStorage<K, MapRealmEntity<K>, RealmModel> realmStore;
|
||||
|
||||
public MapRealmProvider(KeycloakSession session, MapStorage<String, MapRealmEntity, RealmModel> realmStore) {
|
||||
public MapRealmProvider(KeycloakSession session, MapStorage<K, MapRealmEntity<K>, RealmModel> realmStore) {
|
||||
this.session = session;
|
||||
this.realmStore = realmStore;
|
||||
this.tx = realmStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private RealmModel entityToAdapter(MapRealmEntity entity) {
|
||||
private RealmModel entityToAdapter(MapRealmEntity<K> entity) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
||||
return new MapRealmAdapter(session, registerEntityForChanges(entity));
|
||||
return new MapRealmAdapter<K>(session, registerEntityForChanges(entity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return realmStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MapRealmEntity registerEntityForChanges(MapRealmEntity origEntity) {
|
||||
final MapRealmEntity res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRealmEntity::isUpdated);
|
||||
private MapRealmEntity<K> registerEntityForChanges(MapRealmEntity<K> origEntity) {
|
||||
final MapRealmEntity<K> res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRealmEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -76,20 +81,21 @@ public class MapRealmProvider implements RealmProvider {
|
|||
throw new ModelDuplicateException("Realm with given name exists: " + name);
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
if (tx.read(id) != null) {
|
||||
throw new ModelDuplicateException("Realm exists: " + id);
|
||||
K kId = id == null ? null : realmStore.getKeyConvertor().fromString(id);
|
||||
if (kId != null) {
|
||||
if (tx.read(kId) != null) {
|
||||
throw new ModelDuplicateException("Realm exists: " + kId);
|
||||
}
|
||||
} else {
|
||||
id = KeycloakModelUtils.generateId();
|
||||
kId = realmStore.getKeyConvertor().yieldNewUniqueKey();
|
||||
}
|
||||
|
||||
LOG.tracef("createRealm(%s, %s)%s", id, name, getShortStackTrace());
|
||||
LOG.tracef("createRealm(%s, %s)%s", kId, name, getShortStackTrace());
|
||||
|
||||
MapRealmEntity entity = new MapRealmEntity(id);
|
||||
MapRealmEntity<K> entity = new MapRealmEntity<>(kId);
|
||||
entity.setName(name);
|
||||
|
||||
tx.create(id, entity);
|
||||
tx.create(kId, entity);
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
|
@ -99,7 +105,7 @@ public class MapRealmProvider implements RealmProvider {
|
|||
|
||||
LOG.tracef("getRealm(%s)%s", id, getShortStackTrace());
|
||||
|
||||
MapRealmEntity entity = tx.read(id);
|
||||
MapRealmEntity<K> entity = tx.read(realmStore.getKeyConvertor().fromStringSafe(id));
|
||||
return entity == null ? null : entityToAdapter(entity);
|
||||
}
|
||||
|
||||
|
@ -112,12 +118,12 @@ public class MapRealmProvider implements RealmProvider {
|
|||
ModelCriteriaBuilder<RealmModel> mcb = realmStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.NAME, Operator.EQ, name);
|
||||
|
||||
String realmId = tx.getUpdatedNotRemoved(mcb)
|
||||
K realmId = tx.getUpdatedNotRemoved(mcb)
|
||||
.findFirst()
|
||||
.map(MapRealmEntity::getId)
|
||||
.map(MapRealmEntity<K>::getId)
|
||||
.orElse(null);
|
||||
//we need to go via session.realms() not to bypass cache
|
||||
return realmId == null ? null : session.realms().getRealm(realmId);
|
||||
return realmId == null ? null : session.realms().getRealm(realmStore.getKeyConvertor().keyToString(realmId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,7 +173,7 @@ public class MapRealmProvider implements RealmProvider {
|
|||
});
|
||||
// TODO: ^^^^^^^ Up to here
|
||||
|
||||
tx.delete(id);
|
||||
tx.delete(realmStore.getKeyConvertor().fromString(id));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -178,7 +184,7 @@ public class MapRealmProvider implements RealmProvider {
|
|||
|
||||
tx.getUpdatedNotRemoved(mcb)
|
||||
.map(this::registerEntityForChanges)
|
||||
.forEach(MapRealmEntity::removeExpiredClientInitialAccesses);
|
||||
.forEach(MapRealmEntity<K>::removeExpiredClientInitialAccesses);
|
||||
}
|
||||
|
||||
//TODO move the following method to adapter
|
||||
|
|
|
@ -18,27 +18,23 @@ package org.keycloak.models.map.realm;
|
|||
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RealmProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
|
||||
public class MapRealmProviderFactory extends AbstractMapProviderFactory<RealmProvider> implements RealmProviderFactory {
|
||||
public class MapRealmProviderFactory<K> extends AbstractMapProviderFactory<RealmProvider, K, MapRealmEntity<K>, RealmModel> implements RealmProviderFactory {
|
||||
|
||||
//for now we have to support String ids
|
||||
private MapStorage<String, MapRealmEntity, RealmModel> store;
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("realm", String.class, MapRealmEntity.class, RealmModel.class);
|
||||
public MapRealmProviderFactory() {
|
||||
super(MapRealmEntity.class, RealmModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider create(KeycloakSession session) {
|
||||
return new MapRealmProvider(session, store);
|
||||
return new MapRealmProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Realm provider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,150 +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.map.role;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public abstract class AbstractRoleEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private boolean clientRole;
|
||||
private String clientId;
|
||||
private Set<String> compositeRoles = new HashSet<>();
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected AbstractRoleEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractRoleEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= ! Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public boolean isClientRole() {
|
||||
return clientRole;
|
||||
}
|
||||
|
||||
public void setClientRole(boolean clientRole) {
|
||||
this.updated |= ! Objects.equals(this.clientRole, clientRole);
|
||||
this.clientRole = clientRole;
|
||||
}
|
||||
|
||||
public boolean isComposite() {
|
||||
return ! (compositeRoles == null || compositeRoles.isEmpty());
|
||||
}
|
||||
|
||||
public Set<String> getCompositeRoles() {
|
||||
return compositeRoles;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= ! Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public void setCompositeRoles(Set<String> compositeRoles) {
|
||||
this.updated |= ! Objects.equals(this.compositeRoles, compositeRoles);
|
||||
this.compositeRoles.clear();
|
||||
this.compositeRoles.addAll(compositeRoles);
|
||||
}
|
||||
|
||||
public void addCompositeRole(String roleId) {
|
||||
this.updated |= this.compositeRoles.add(roleId);
|
||||
}
|
||||
|
||||
public void removeCompositeRole(String roleId) {
|
||||
this.updated |= this.compositeRoles.remove(roleId);
|
||||
}
|
||||
}
|
|
@ -30,11 +30,11 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
public class MapRoleAdapter extends AbstractRoleModel<MapRoleEntity> implements RoleModel {
|
||||
public abstract class MapRoleAdapter<K> extends AbstractRoleModel<MapRoleEntity<K>> implements RoleModel {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapRoleAdapter.class);
|
||||
|
||||
public MapRoleAdapter(KeycloakSession session, RealmModel realm, MapRoleEntity entity) {
|
||||
public MapRoleAdapter(KeycloakSession session, RealmModel realm, MapRoleEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
|
@ -53,11 +53,6 @@ public class MapRoleAdapter extends AbstractRoleModel<MapRoleEntity> implements
|
|||
entity.setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
entity.setName(name);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
|
@ -16,16 +16,135 @@
|
|||
*/
|
||||
package org.keycloak.models.map.role;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
public class MapRoleEntity extends AbstractRoleEntity<UUID> {
|
||||
public class MapRoleEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String realmId;
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private boolean clientRole;
|
||||
private String clientId;
|
||||
private Set<String> compositeRoles = new HashSet<>();
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected MapRoleEntity() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapRoleEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapRoleEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= ! Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= ! Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= ! Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public boolean isClientRole() {
|
||||
return clientRole;
|
||||
}
|
||||
|
||||
public void setClientRole(boolean clientRole) {
|
||||
this.updated |= ! Objects.equals(this.clientRole, clientRole);
|
||||
this.clientRole = clientRole;
|
||||
}
|
||||
|
||||
public boolean isComposite() {
|
||||
return ! (compositeRoles == null || compositeRoles.isEmpty());
|
||||
}
|
||||
|
||||
public Set<String> getCompositeRoles() {
|
||||
return compositeRoles;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= ! Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public void setCompositeRoles(Set<String> compositeRoles) {
|
||||
this.updated |= ! Objects.equals(this.compositeRoles, compositeRoles);
|
||||
this.compositeRoles.clear();
|
||||
this.compositeRoles.addAll(compositeRoles);
|
||||
}
|
||||
|
||||
public void addCompositeRole(String roleId) {
|
||||
this.updated |= this.compositeRoles.add(roleId);
|
||||
}
|
||||
|
||||
public void removeCompositeRole(String roleId) {
|
||||
this.updated |= this.compositeRoles.remove(roleId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
|||
import org.keycloak.models.map.common.Serialization;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -43,16 +42,16 @@ import org.keycloak.models.map.common.StreamUtils;
|
|||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
public class MapRoleProvider implements RoleProvider {
|
||||
public class MapRoleProvider<K> implements RoleProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapRoleProvider.class);
|
||||
private final KeycloakSession session;
|
||||
final MapKeycloakTransaction<UUID, MapRoleEntity, RoleModel> tx;
|
||||
private final MapStorage<UUID, MapRoleEntity, RoleModel> roleStore;
|
||||
final MapKeycloakTransaction<K, MapRoleEntity<K>, RoleModel> tx;
|
||||
private final MapStorage<K, MapRoleEntity<K>, RoleModel> roleStore;
|
||||
|
||||
private static final Comparator<MapRoleEntity> COMPARE_BY_NAME = new Comparator<MapRoleEntity>() {
|
||||
private static final Comparator<MapRoleEntity<?>> COMPARE_BY_NAME = new Comparator<MapRoleEntity<?>>() {
|
||||
@Override
|
||||
public int compare(MapRoleEntity o1, MapRoleEntity o2) {
|
||||
public int compare(MapRoleEntity<?> o1, MapRoleEntity<?> o2) {
|
||||
String r1 = o1 == null ? null : o1.getName();
|
||||
String r2 = o2 == null ? null : o2.getName();
|
||||
return r1 == r2 ? 0
|
||||
|
@ -63,22 +62,27 @@ public class MapRoleProvider implements RoleProvider {
|
|||
}
|
||||
};
|
||||
|
||||
public MapRoleProvider(KeycloakSession session, MapStorage<UUID, MapRoleEntity, RoleModel> roleStore) {
|
||||
public MapRoleProvider(KeycloakSession session, MapStorage<K, MapRoleEntity<K>, RoleModel> roleStore) {
|
||||
this.session = session;
|
||||
this.roleStore = roleStore;
|
||||
this.tx = roleStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private Function<MapRoleEntity, RoleModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapRoleEntity<K>, RoleModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
||||
return origEntity -> new MapRoleAdapter(session, realm, registerEntityForChanges(origEntity));
|
||||
return origEntity -> new MapRoleAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return roleStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MapRoleEntity registerEntityForChanges(MapRoleEntity origEntity) {
|
||||
final MapRoleEntity res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRoleEntity::isUpdated);
|
||||
private MapRoleEntity<K> registerEntityForChanges(MapRoleEntity<K> origEntity) {
|
||||
final MapRoleEntity<K> res = Serialization.from(origEntity);
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapRoleEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -88,11 +92,11 @@ public class MapRoleProvider implements RoleProvider {
|
|||
throw new ModelDuplicateException("Role exists: " + id);
|
||||
}
|
||||
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? roleStore.getKeyConvertor().yieldNewUniqueKey() : roleStore.getKeyConvertor().fromString(id);
|
||||
|
||||
LOG.tracef("addRealmRole(%s, %s, %s)%s", realm, id, name, getShortStackTrace());
|
||||
|
||||
MapRoleEntity entity = new MapRoleEntity(entityId, realm.getId());
|
||||
MapRoleEntity<K> entity = new MapRoleEntity<K>(entityId, realm.getId());
|
||||
entity.setName(name);
|
||||
entity.setRealmId(realm.getId());
|
||||
if (tx.read(entity.getId()) != null) {
|
||||
|
@ -124,11 +128,11 @@ public class MapRoleProvider implements RoleProvider {
|
|||
throw new ModelDuplicateException("Role exists: " + id);
|
||||
}
|
||||
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? roleStore.getKeyConvertor().yieldNewUniqueKey() : roleStore.getKeyConvertor().fromString(id);
|
||||
|
||||
LOG.tracef("addClientRole(%s, %s, %s)%s", client, id, name, getShortStackTrace());
|
||||
|
||||
MapRoleEntity entity = new MapRoleEntity(entityId, client.getRealm().getId());
|
||||
MapRoleEntity<K> entity = new MapRoleEntity<K>(entityId, client.getRealm().getId());
|
||||
entity.setName(name);
|
||||
entity.setClientRole(true);
|
||||
entity.setClientId(client.getId());
|
||||
|
@ -168,13 +172,13 @@ public class MapRoleProvider implements RoleProvider {
|
|||
.compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false);
|
||||
|
||||
//remove role from realm-roles composites
|
||||
try (Stream<MapRoleEntity> baseStream = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapRoleEntity<K>> baseStream = tx.getUpdatedNotRemoved(mcb)) {
|
||||
|
||||
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles)
|
||||
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity<K>::getCompositeRoles)
|
||||
.filter(pair -> role.getId().equals(pair.getV()))
|
||||
.collect(Collectors.toSet())
|
||||
.forEach(pair -> {
|
||||
MapRoleEntity origEntity = pair.getK();
|
||||
MapRoleEntity<K> origEntity = pair.getK();
|
||||
|
||||
//
|
||||
// TODO: Investigate what this is for - the return value is ignored
|
||||
|
@ -192,13 +196,13 @@ public class MapRoleProvider implements RoleProvider {
|
|||
.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId())
|
||||
.compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false);
|
||||
|
||||
try (Stream<MapRoleEntity> baseStream = tx.getUpdatedNotRemoved(mcbClient)) {
|
||||
try (Stream<MapRoleEntity<K>> baseStream = tx.getUpdatedNotRemoved(mcbClient)) {
|
||||
|
||||
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles)
|
||||
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity<K>::getCompositeRoles)
|
||||
.filter(pair -> role.getId().equals(pair.getV()))
|
||||
.collect(Collectors.toSet())
|
||||
.forEach(pair -> {
|
||||
MapRoleEntity origEntity = pair.getK();
|
||||
MapRoleEntity<K> origEntity = pair.getK();
|
||||
|
||||
//
|
||||
// TODO: Investigate what this is for - the return value is ignored
|
||||
|
@ -223,7 +227,7 @@ public class MapRoleProvider implements RoleProvider {
|
|||
});
|
||||
// TODO: ^^^^^^^ Up to here
|
||||
|
||||
tx.delete(UUID.fromString(role.getId()));
|
||||
tx.delete(roleStore.getKeyConvertor().fromString(role.getId()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -287,7 +291,7 @@ public class MapRoleProvider implements RoleProvider {
|
|||
|
||||
LOG.tracef("getRoleById(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
MapRoleEntity entity = tx.read(UUID.fromString(id));
|
||||
MapRoleEntity<K> entity = tx.read(roleStore.getKeyConvertor().fromStringSafe(id));
|
||||
String realmId = realm.getId();
|
||||
return (entity == null || ! Objects.equals(realmId, entity.getRealmId()))
|
||||
? null
|
||||
|
@ -306,7 +310,7 @@ public class MapRoleProvider implements RoleProvider {
|
|||
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
|
||||
);
|
||||
|
||||
Stream<MapRoleEntity> s = tx.getUpdatedNotRemoved(mcb)
|
||||
Stream<MapRoleEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(COMPARE_BY_NAME);
|
||||
|
||||
return paginatedStream(s.map(entityToAdapterFunc(realm)), first, max);
|
||||
|
@ -324,7 +328,7 @@ public class MapRoleProvider implements RoleProvider {
|
|||
roleStore.createCriteriaBuilder().compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"),
|
||||
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
|
||||
);
|
||||
Stream<MapRoleEntity> s = tx.getUpdatedNotRemoved(mcb)
|
||||
Stream<MapRoleEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(COMPARE_BY_NAME);
|
||||
|
||||
return paginatedStream(s,first, max).map(entityToAdapterFunc(client.getRealm()));
|
||||
|
|
|
@ -16,30 +16,25 @@
|
|||
*/
|
||||
package org.keycloak.models.map.role;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.RoleProvider;
|
||||
import org.keycloak.models.RoleProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
|
||||
public class MapRoleProviderFactory extends AbstractMapProviderFactory<RoleProvider> implements RoleProviderFactory {
|
||||
public class MapRoleProviderFactory<K> extends AbstractMapProviderFactory<RoleProvider, K, MapRoleEntity<K>, RoleModel> implements RoleProviderFactory {
|
||||
|
||||
private MapStorage<UUID, MapRoleEntity, RoleModel> store;
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("roles", UUID.class, MapRoleEntity.class, RoleModel.class);
|
||||
public MapRoleProviderFactory() {
|
||||
super(MapRoleEntity.class, RoleModel.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RoleProvider create(KeycloakSession session) {
|
||||
return new MapRoleProvider(session, store);
|
||||
return new MapRoleProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Role provider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,14 +27,21 @@ import org.keycloak.common.util.RandomString;
|
|||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ServerInfoProvider;
|
||||
import org.keycloak.models.ServerInfoProviderFactory;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
|
||||
public class MapServerInfoProviderFactory extends AbstractMapProviderFactory<ServerInfoProvider> implements ServerInfoProviderFactory {
|
||||
public class MapServerInfoProviderFactory implements ServerInfoProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "map";
|
||||
|
||||
private static final String RESOURCES_VERSION_SEED = "resourcesVersionSeed";
|
||||
|
||||
@Override
|
||||
public ServerInfoProvider create(KeycloakSession session) {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String seed = config.get(RESOURCES_VERSION_SEED);
|
||||
|
@ -53,8 +60,16 @@ public class MapServerInfoProviderFactory extends AbstractMapProviderFactory<Ser
|
|||
}
|
||||
|
||||
@Override
|
||||
public ServerInfoProvider create(KeycloakSession session) {
|
||||
return INSTANCE;
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
private static final ServerInfoProvider INSTANCE = new ServerInfoProvider() {
|
||||
|
|
|
@ -30,28 +30,28 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.authSession.AbstractRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractScopeEntity;
|
||||
import org.keycloak.models.map.client.AbstractClientEntity;
|
||||
import org.keycloak.models.map.clientscope.AbstractClientScopeEntity;
|
||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.client.MapClientEntity;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.group.AbstractGroupEntity;
|
||||
import org.keycloak.models.map.realm.AbstractRealmEntity;
|
||||
import org.keycloak.models.map.role.AbstractRoleEntity;
|
||||
import org.keycloak.models.map.userSession.AbstractAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.loginFailure.AbstractUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.userSession.AbstractUserSessionEntity;
|
||||
import org.keycloak.models.map.group.MapGroupEntity;
|
||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||
import org.keycloak.models.map.role.MapRoleEntity;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.keycloak.models.map.storage.MapModelCriteriaBuilder.UpdatePredicatesFunc;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import org.keycloak.models.map.user.AbstractUserEntity;
|
||||
import org.keycloak.models.map.user.MapUserEntity;
|
||||
import org.keycloak.models.map.user.UserConsentEntity;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import java.util.Arrays;
|
||||
|
@ -69,57 +69,57 @@ import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
|||
*/
|
||||
public class MapFieldPredicates {
|
||||
|
||||
public static final Map<SearchableModelField<RealmModel>, UpdatePredicatesFunc<Object, AbstractRealmEntity<Object>, RealmModel>> REALM_PREDICATES = basePredicates(RealmModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ClientModel>, UpdatePredicatesFunc<Object, AbstractClientEntity<Object>, ClientModel>> CLIENT_PREDICATES = basePredicates(ClientModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ClientScopeModel>, UpdatePredicatesFunc<Object, AbstractClientScopeEntity<Object>, ClientScopeModel>> CLIENT_SCOPE_PREDICATES = basePredicates(ClientScopeModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<GroupModel>, UpdatePredicatesFunc<Object, AbstractGroupEntity<Object>, GroupModel>> GROUP_PREDICATES = basePredicates(GroupModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RoleModel>, UpdatePredicatesFunc<Object, AbstractRoleEntity<Object>, RoleModel>> ROLE_PREDICATES = basePredicates(RoleModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserModel>, UpdatePredicatesFunc<Object, AbstractUserEntity<Object>, UserModel>> USER_PREDICATES = basePredicates(UserModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RootAuthenticationSessionModel>, UpdatePredicatesFunc<Object, AbstractRootAuthenticationSessionEntity<Object>, RootAuthenticationSessionModel>> AUTHENTICATION_SESSION_PREDICATES = basePredicates(RootAuthenticationSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ResourceServer>, UpdatePredicatesFunc<Object, AbstractResourceServerEntity<Object>, ResourceServer>> AUTHZ_RESOURCE_SERVER_PREDICATES = basePredicates(ResourceServer.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Resource>, UpdatePredicatesFunc<Object, AbstractResourceEntity<Object>, Resource>> AUTHZ_RESOURCE_PREDICATES = basePredicates(Resource.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Scope>, UpdatePredicatesFunc<Object, AbstractScopeEntity<Object>, Scope>> AUTHZ_SCOPE_PREDICATES = basePredicates(Scope.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<PermissionTicket>, UpdatePredicatesFunc<Object, AbstractPermissionTicketEntity<Object>, PermissionTicket>> AUTHZ_PERMISSION_TICKET_PREDICATES = basePredicates(PermissionTicket.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Policy>, UpdatePredicatesFunc<Object, AbstractPolicyEntity<Object>, Policy>> AUTHZ_POLICY_PREDICATES = basePredicates(Policy.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserSessionModel>, UpdatePredicatesFunc<Object, AbstractUserSessionEntity<Object>, UserSessionModel>> USER_SESSION_PREDICATES = basePredicates(UserSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<AuthenticatedClientSessionModel>, UpdatePredicatesFunc<Object, AbstractAuthenticatedClientSessionEntity<Object>, AuthenticatedClientSessionModel>> CLIENT_SESSION_PREDICATES = basePredicates(AuthenticatedClientSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserLoginFailureModel>, UpdatePredicatesFunc<Object, AbstractUserLoginFailureEntity<Object>, UserLoginFailureModel>> USER_LOGIN_FAILURE_PREDICATES = basePredicates(UserLoginFailureModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<AuthenticatedClientSessionModel>, UpdatePredicatesFunc<Object, MapAuthenticatedClientSessionEntity<Object>, AuthenticatedClientSessionModel>> CLIENT_SESSION_PREDICATES = basePredicates(AuthenticatedClientSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ClientModel>, UpdatePredicatesFunc<Object, MapClientEntity<Object>, ClientModel>> CLIENT_PREDICATES = basePredicates(ClientModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ClientScopeModel>, UpdatePredicatesFunc<Object, MapClientScopeEntity<Object>, ClientScopeModel>> CLIENT_SCOPE_PREDICATES = basePredicates(ClientScopeModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<GroupModel>, UpdatePredicatesFunc<Object, MapGroupEntity<Object>, GroupModel>> GROUP_PREDICATES = basePredicates(GroupModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RoleModel>, UpdatePredicatesFunc<Object, MapRoleEntity<Object>, RoleModel>> ROLE_PREDICATES = basePredicates(RoleModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RootAuthenticationSessionModel>, UpdatePredicatesFunc<Object, MapRootAuthenticationSessionEntity<Object>, RootAuthenticationSessionModel>> AUTHENTICATION_SESSION_PREDICATES = basePredicates(RootAuthenticationSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RealmModel>, UpdatePredicatesFunc<Object, MapRealmEntity<Object>, RealmModel>> REALM_PREDICATES = basePredicates(RealmModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ResourceServer>, UpdatePredicatesFunc<Object, MapResourceServerEntity<Object>, ResourceServer>> AUTHZ_RESOURCE_SERVER_PREDICATES = basePredicates(ResourceServer.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Resource>, UpdatePredicatesFunc<Object, MapResourceEntity<Object>, Resource>> AUTHZ_RESOURCE_PREDICATES = basePredicates(Resource.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Scope>, UpdatePredicatesFunc<Object, MapScopeEntity<Object>, Scope>> AUTHZ_SCOPE_PREDICATES = basePredicates(Scope.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<PermissionTicket>, UpdatePredicatesFunc<Object, MapPermissionTicketEntity<Object>, PermissionTicket>> AUTHZ_PERMISSION_TICKET_PREDICATES = basePredicates(PermissionTicket.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Policy>, UpdatePredicatesFunc<Object, MapPolicyEntity<Object>, Policy>> AUTHZ_POLICY_PREDICATES = basePredicates(Policy.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserLoginFailureModel>, UpdatePredicatesFunc<Object, MapUserLoginFailureEntity<Object>, UserLoginFailureModel>> USER_LOGIN_FAILURE_PREDICATES = basePredicates(UserLoginFailureModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserModel>, UpdatePredicatesFunc<Object, MapUserEntity<Object>, UserModel>> USER_PREDICATES = basePredicates(UserModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserSessionModel>, UpdatePredicatesFunc<Object, MapUserSessionEntity<Object>, UserSessionModel>> USER_SESSION_PREDICATES = basePredicates(UserSessionModel.SearchableFields.ID);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final Map<Class<?>, Map> PREDICATES = new HashMap<>();
|
||||
|
||||
static {
|
||||
put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, AbstractRealmEntity::getName);
|
||||
put(REALM_PREDICATES, RealmModel.SearchableFields.NAME, MapRealmEntity::getName);
|
||||
put(REALM_PREDICATES, RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, MapFieldPredicates::checkRealmsWithClientInitialAccess);
|
||||
put(REALM_PREDICATES, RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, MapFieldPredicates::checkRealmsWithComponentType);
|
||||
|
||||
put(CLIENT_PREDICATES, ClientModel.SearchableFields.REALM_ID, AbstractClientEntity::getRealmId);
|
||||
put(CLIENT_PREDICATES, ClientModel.SearchableFields.CLIENT_ID, AbstractClientEntity::getClientId);
|
||||
put(CLIENT_PREDICATES, ClientModel.SearchableFields.REALM_ID, MapClientEntity::getRealmId);
|
||||
put(CLIENT_PREDICATES, ClientModel.SearchableFields.CLIENT_ID, MapClientEntity::getClientId);
|
||||
put(CLIENT_PREDICATES, ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, MapFieldPredicates::checkScopeMappingRole);
|
||||
|
||||
put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.REALM_ID, AbstractClientScopeEntity::getRealmId);
|
||||
put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.NAME, AbstractClientScopeEntity::getName);
|
||||
put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.REALM_ID, MapClientScopeEntity::getRealmId);
|
||||
put(CLIENT_SCOPE_PREDICATES, ClientScopeModel.SearchableFields.NAME, MapClientScopeEntity::getName);
|
||||
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.REALM_ID, AbstractGroupEntity::getRealmId);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.NAME, AbstractGroupEntity::getName);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.PARENT_ID, AbstractGroupEntity::getParentId);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.REALM_ID, MapGroupEntity::getRealmId);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.NAME, MapGroupEntity::getName);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.PARENT_ID, MapGroupEntity::getParentId);
|
||||
put(GROUP_PREDICATES, GroupModel.SearchableFields.ASSIGNED_ROLE, MapFieldPredicates::checkGrantedGroupRole);
|
||||
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.REALM_ID, AbstractRoleEntity::getRealmId);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.CLIENT_ID, AbstractRoleEntity::getClientId);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.DESCRIPTION, AbstractRoleEntity::getDescription);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.NAME, AbstractRoleEntity::getName);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_CLIENT_ROLE, AbstractRoleEntity::isClientRole);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_COMPOSITE_ROLE, AbstractRoleEntity::isComposite);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.REALM_ID, MapRoleEntity::getRealmId);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.CLIENT_ID, MapRoleEntity::getClientId);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.DESCRIPTION, MapRoleEntity::getDescription);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.NAME, MapRoleEntity::getName);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_CLIENT_ROLE, MapRoleEntity::isClientRole);
|
||||
put(ROLE_PREDICATES, RoleModel.SearchableFields.IS_COMPOSITE_ROLE, MapRoleEntity::isComposite);
|
||||
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.REALM_ID, AbstractUserEntity::getRealmId);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.USERNAME, AbstractUserEntity::getUsername);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.FIRST_NAME, AbstractUserEntity::getFirstName);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.LAST_NAME, AbstractUserEntity::getLastName);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.EMAIL, AbstractUserEntity::getEmail);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.ENABLED, AbstractUserEntity::isEnabled);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.EMAIL_VERIFIED, AbstractUserEntity::isEmailVerified);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.FEDERATION_LINK, AbstractUserEntity::getFederationLink);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.REALM_ID, MapUserEntity::getRealmId);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.USERNAME, MapUserEntity::getUsername);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.FIRST_NAME, MapUserEntity::getFirstName);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.LAST_NAME, MapUserEntity::getLastName);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.EMAIL, MapUserEntity::getEmail);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.ENABLED, MapUserEntity::isEnabled);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.EMAIL_VERIFIED, MapUserEntity::isEmailVerified);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.FEDERATION_LINK, MapUserEntity::getFederationLink);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.ATTRIBUTE, MapFieldPredicates::checkUserAttributes);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.IDP_AND_USER, MapFieldPredicates::getUserIdpAliasAtIdentityProviderPredicate);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.ASSIGNED_ROLE, MapFieldPredicates::checkGrantedUserRole);
|
||||
|
@ -127,62 +127,62 @@ public class MapFieldPredicates {
|
|||
put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_FOR_CLIENT, MapFieldPredicates::checkUserClientConsent);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_WITH_CLIENT_SCOPE, MapFieldPredicates::checkUserConsentsWithClientScope);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, MapFieldPredicates::getUserConsentClientFederationLink);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, AbstractUserEntity::getServiceAccountClientLink);
|
||||
put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, MapUserEntity::getServiceAccountClientLink);
|
||||
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, AbstractRootAuthenticationSessionEntity::getRealmId);
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, AbstractRootAuthenticationSessionEntity::getTimestamp);
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, MapRootAuthenticationSessionEntity::getRealmId);
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, MapRootAuthenticationSessionEntity::getTimestamp);
|
||||
|
||||
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, AbstractResourceServerEntity::getId);
|
||||
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, MapResourceServerEntity::getId);
|
||||
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(AbstractResourceEntity::getId));
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, AbstractResourceEntity::getName);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.RESOURCE_SERVER_ID, AbstractResourceEntity::getResourceServerId);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER, AbstractResourceEntity::getOwner);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.TYPE, AbstractResourceEntity::getType);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(MapResourceEntity::getId));
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, MapResourceEntity::getName);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.RESOURCE_SERVER_ID, MapResourceEntity::getResourceServerId);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER, MapResourceEntity::getOwner);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.TYPE, MapResourceEntity::getType);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.URI, MapFieldPredicates::checkResourceUri);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.SCOPE_ID, MapFieldPredicates::checkResourceScopes);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, AbstractResourceEntity::isOwnerManagedAccess);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, MapResourceEntity::isOwnerManagedAccess);
|
||||
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(AbstractScopeEntity::getId));
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, AbstractScopeEntity::getResourceServerId);
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, AbstractScopeEntity::getName);
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(MapScopeEntity::getId));
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, MapScopeEntity::getResourceServerId);
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, MapScopeEntity::getName);
|
||||
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(AbstractPermissionTicketEntity::getId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, AbstractPermissionTicketEntity::getOwner);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REQUESTER, AbstractPermissionTicketEntity::getRequester);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_SERVER_ID, AbstractPermissionTicketEntity::getResourceServerId);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getResourceId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getScopeId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(AbstractPermissionTicketEntity::getPolicyId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, AbstractPermissionTicketEntity::getGrantedTimestamp);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(MapPermissionTicketEntity::getId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, MapPermissionTicketEntity::getOwner);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REQUESTER, MapPermissionTicketEntity::getRequester);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_SERVER_ID, MapPermissionTicketEntity::getResourceServerId);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_ID, predicateForKeyField(MapPermissionTicketEntity::getResourceId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(MapPermissionTicketEntity::getScopeId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(MapPermissionTicketEntity::getPolicyId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, MapPermissionTicketEntity::getGrantedTimestamp);
|
||||
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(AbstractPolicyEntity::getId));
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, AbstractPolicyEntity::getName);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, AbstractPolicyEntity::getOwner);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, AbstractPolicyEntity::getType);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, AbstractPolicyEntity::getResourceServerId);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(MapPolicyEntity::getId));
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, MapPolicyEntity::getName);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, MapPolicyEntity::getOwner);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, MapPolicyEntity::getType);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, MapPolicyEntity::getResourceServerId);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ASSOCIATED_POLICY_ID, MapFieldPredicates::checkAssociatedPolicy);
|
||||
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, use -> use.getNote(CORRESPONDING_SESSION_ID));
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.REALM_ID, AbstractUserSessionEntity::getRealmId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.USER_ID, AbstractUserSessionEntity::getUserId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.REALM_ID, MapUserSessionEntity::getRealmId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.USER_ID, MapUserSessionEntity::getUserId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.CLIENT_ID, MapFieldPredicates::checkUserSessionContainsAuthenticatedClientSession);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_SESSION_ID, AbstractUserSessionEntity::getBrokerSessionId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_USER_ID, AbstractUserSessionEntity::getBrokerUserId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.IS_OFFLINE, AbstractUserSessionEntity::isOffline);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.LAST_SESSION_REFRESH, AbstractUserSessionEntity::getLastSessionRefresh);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_SESSION_ID, MapUserSessionEntity::getBrokerSessionId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.BROKER_USER_ID, MapUserSessionEntity::getBrokerUserId);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.IS_OFFLINE, MapUserSessionEntity::isOffline);
|
||||
put(USER_SESSION_PREDICATES, UserSessionModel.SearchableFields.LAST_SESSION_REFRESH, MapUserSessionEntity::getLastSessionRefresh);
|
||||
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.REALM_ID, AbstractAuthenticatedClientSessionEntity::getRealmId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, AbstractAuthenticatedClientSessionEntity::getClientId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, AbstractAuthenticatedClientSessionEntity::getUserSessionId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, AbstractAuthenticatedClientSessionEntity::isOffline);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.TIMESTAMP, AbstractAuthenticatedClientSessionEntity::getTimestamp);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.REALM_ID, MapAuthenticatedClientSessionEntity::getRealmId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, MapAuthenticatedClientSessionEntity::getClientId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, MapAuthenticatedClientSessionEntity::getUserSessionId);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, MapAuthenticatedClientSessionEntity::isOffline);
|
||||
put(CLIENT_SESSION_PREDICATES, AuthenticatedClientSessionModel.SearchableFields.TIMESTAMP, MapAuthenticatedClientSessionEntity::getTimestamp);
|
||||
|
||||
put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.REALM_ID, AbstractUserLoginFailureEntity::getRealmId);
|
||||
put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.USER_ID, AbstractUserLoginFailureEntity::getUserId);
|
||||
put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.REALM_ID, MapUserLoginFailureEntity::getRealmId);
|
||||
put(USER_LOGIN_FAILURE_PREDICATES, UserLoginFailureModel.SearchableFields.USER_ID, MapUserLoginFailureEntity::getUserId);
|
||||
}
|
||||
|
||||
static {
|
||||
|
@ -242,30 +242,30 @@ public class MapFieldPredicates {
|
|||
return expectedType.cast(ob);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractClientEntity<Object>, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder<Object, AbstractClientEntity<Object>, ClientModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapClientEntity<Object>, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder<Object, MapClientEntity<Object>, ClientModel> mcb, Operator op, Object[] values) {
|
||||
String roleIdS = ensureEqSingleValue(ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, "role_id", op, values);
|
||||
Function<AbstractClientEntity<Object>, ?> getter;
|
||||
Function<MapClientEntity<Object>, ?> getter;
|
||||
getter = ce -> ce.getScopeMappings().contains(roleIdS);
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractGroupEntity<Object>, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder<Object, AbstractGroupEntity<Object>, GroupModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapGroupEntity<Object>, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder<Object, MapGroupEntity<Object>, GroupModel> mcb, Operator op, Object[] values) {
|
||||
String roleIdS = ensureEqSingleValue(GroupModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values);
|
||||
Function<AbstractGroupEntity<Object>, ?> getter;
|
||||
Function<MapGroupEntity<Object>, ?> getter;
|
||||
getter = ge -> ge.getGrantedRoles().contains(roleIdS);
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> getUserConsentClientFederationLink(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> getUserConsentClientFederationLink(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
String providerId = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, "provider_id", op, values);
|
||||
String providerIdS = new StorageId((String) providerId, "").getId();
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
getter = ue -> ue.getUserConsents().map(UserConsentEntity::getClientId).anyMatch(v -> v != null && v.startsWith(providerIdS));
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkUserAttributes(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkUserAttributes(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
if (values == null || values.length <= 1) {
|
||||
throw new CriterionNotSupportedException(UserModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (attribute_name, ...), got: " + Arrays.toString(values));
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ public class MapFieldPredicates {
|
|||
throw new CriterionNotSupportedException(UserModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (String attribute_name), got: " + Arrays.toString(values));
|
||||
}
|
||||
String attrNameS = (String) attrName;
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
Object[] realValues = new Object[values.length - 1];
|
||||
System.arraycopy(values, 1, realValues, 0, values.length - 1);
|
||||
Predicate<Object> valueComparator = CriteriaOperator.predicateFor(op, realValues);
|
||||
|
@ -287,16 +287,16 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
String roleIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values);
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
getter = ue -> ue.getRolesMembership().contains(roleIdS);
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> checkResourceUri(MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractResourceEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapResourceEntity<Object>, Resource> checkResourceUri(MapModelCriteriaBuilder<Object, MapResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<MapResourceEntity<Object>, ?> getter;
|
||||
|
||||
if (Operator.EXISTS.equals(op)) {
|
||||
getter = re -> re.getUris() != null && !re.getUris().isEmpty();
|
||||
|
@ -311,8 +311,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> checkResourceScopes(MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractResourceEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapResourceEntity<Object>, Resource> checkResourceScopes(MapModelCriteriaBuilder<Object, MapResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<MapResourceEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
|
@ -325,8 +325,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyResources(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> checkPolicyResources(MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<MapPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.NOT_EXISTS) {
|
||||
getter = re -> re.getResourceIds().isEmpty();
|
||||
|
@ -341,8 +341,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyScopes(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> checkPolicyScopes(MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<MapPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
|
@ -355,8 +355,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyConfig(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> checkPolicyConfig(MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<MapPolicyEntity<Object>, ?> getter;
|
||||
|
||||
final Object attrName = values[0];
|
||||
if (!(attrName instanceof String)) {
|
||||
|
@ -375,8 +375,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkAssociatedPolicy(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> checkAssociatedPolicy(MapModelCriteriaBuilder<Object, MapPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<MapPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
|
@ -389,8 +389,8 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkUserGroup(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkUserGroup(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = ue -> ue.getGroupsMembership().stream().anyMatch(c::contains);
|
||||
|
@ -402,23 +402,23 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkUserClientConsent(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkUserClientConsent(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
String clientIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_id", op, values);
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
getter = ue -> ue.getUserConsent(clientIdS);
|
||||
|
||||
return mcb.fieldCompare(Operator.EXISTS, getter, null);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
String clientScopeIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_scope_id", op, values);
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
getter = ue -> ue.getUserConsents().anyMatch(consent -> consent.getGrantedClientScopesIds().contains(clientScopeIdS));
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> getUserIdpAliasAtIdentityProviderPredicate(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> getUserIdpAliasAtIdentityProviderPredicate(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
if (op != Operator.EQ) {
|
||||
throw new CriterionNotSupportedException(UserModel.SearchableFields.IDP_AND_USER, op);
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ public class MapFieldPredicates {
|
|||
}
|
||||
|
||||
final Object idpAlias = values[0];
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
Function<MapUserEntity<Object>, ?> getter;
|
||||
if (values.length == 1) {
|
||||
getter = ue -> ue.getFederatedIdentities()
|
||||
.anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider()));
|
||||
|
@ -444,25 +444,23 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractRealmEntity<Object>, RealmModel> checkRealmsWithClientInitialAccess(MapModelCriteriaBuilder<Object, AbstractRealmEntity<Object>, RealmModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapRealmEntity<Object>, RealmModel> checkRealmsWithClientInitialAccess(MapModelCriteriaBuilder<Object, MapRealmEntity<Object>, RealmModel> mcb, Operator op, Object[] values) {
|
||||
if (op != Operator.EXISTS) {
|
||||
throw new CriterionNotSupportedException(RealmModel.SearchableFields.CLIENT_INITIAL_ACCESS, op);
|
||||
}
|
||||
Function<AbstractRealmEntity<Object>, ?> getter = AbstractRealmEntity::hasClientInitialAccess;
|
||||
Function<MapRealmEntity<Object>, ?> getter = MapRealmEntity::hasClientInitialAccess;
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractRealmEntity<Object>, RealmModel> checkRealmsWithComponentType(MapModelCriteriaBuilder<Object, AbstractRealmEntity<Object>, RealmModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapRealmEntity<Object>, RealmModel> checkRealmsWithComponentType(MapModelCriteriaBuilder<Object, MapRealmEntity<Object>, RealmModel> mcb, Operator op, Object[] values) {
|
||||
String providerType = ensureEqSingleValue(RealmModel.SearchableFields.COMPONENT_PROVIDER_TYPE, "component_provider_type", op, values);
|
||||
Function<AbstractRealmEntity<Object>, ?> getter = realmEntity -> realmEntity.getComponents().anyMatch(component -> component.getProviderType().equals(providerType));
|
||||
Function<MapRealmEntity<Object>, ?> getter = realmEntity -> realmEntity.getComponents().anyMatch(component -> component.getProviderType().equals(providerType));
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserSessionEntity<Object>, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder<Object, AbstractUserSessionEntity<Object>, UserSessionModel> mcb, Operator op, Object[] values) {
|
||||
private static MapModelCriteriaBuilder<Object, MapUserSessionEntity<Object>, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder<Object, MapUserSessionEntity<Object>, UserSessionModel> mcb, Operator op, Object[] values) {
|
||||
String clientId = ensureEqSingleValue(UserSessionModel.SearchableFields.CLIENT_ID, "client_id", op, values);
|
||||
Function<AbstractUserSessionEntity<Object>, ?> getter;
|
||||
getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId));
|
||||
|
||||
Function<MapUserSessionEntity<Object>, ?> getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId));
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class MapKeycloakTransaction<K, V extends AbstractEntity<K>, M> implement
|
|||
|
||||
@Override
|
||||
public void commit() {
|
||||
log.trace("Commit");
|
||||
log.tracef("Commit - %s", map);
|
||||
|
||||
if (rollback) {
|
||||
throw new RuntimeException("Rollback only!");
|
||||
|
@ -97,7 +97,11 @@ public class MapKeycloakTransaction<K, V extends AbstractEntity<K>, M> implement
|
|||
|
||||
// This is for possibility to lookup for session by id, which was created in this transaction
|
||||
public V read(K key) {
|
||||
return read(key, map::read);
|
||||
try { // TODO: Consider using Optional rather than handling NPE
|
||||
return read(key, map::read);
|
||||
} catch (NullPointerException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public V read(K key, Function<K, V> defaultValueFunc) {
|
||||
|
@ -127,8 +131,6 @@ public class MapKeycloakTransaction<K, V extends AbstractEntity<K>, M> implement
|
|||
* Returns the stream of records that match given criteria and includes changes made in this transaction, i.e.
|
||||
* the result contains updates and excludes records that have been deleted in this transaction.
|
||||
*
|
||||
* Note that returned stream might not reflect on the bulk delete. This is known limitation that can be fixed if necessary.
|
||||
*
|
||||
* @param mcb
|
||||
* @return
|
||||
*/
|
||||
|
|
|
@ -49,6 +49,8 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
|
|||
|
||||
/**
|
||||
* Returns object with the given {@code key} from the storage or {@code null} if object does not exist.
|
||||
* <br>
|
||||
* TODO: Consider returning {@code Optional<V>} instead.
|
||||
* @param key Key of the object. Must not be {@code null}.
|
||||
* @return See description
|
||||
* @throws NullPointerException if the {@code key} is {@code null}
|
||||
|
@ -115,7 +117,7 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
|
|||
* If possible, do <i>not</i> delay filtering after the models are reconstructed from
|
||||
* storage entities, in most cases this would be highly inefficient.
|
||||
*
|
||||
* @return See description
|
||||
* @return See description. Never returns {@code null}
|
||||
*/
|
||||
ModelCriteriaBuilder<M> createCriteriaBuilder();
|
||||
|
||||
|
@ -126,8 +128,16 @@ public interface MapStorage<K, V extends AbstractEntity<K>, M> {
|
|||
* shared same across storages accessing the same database within the same session; in other cases
|
||||
* (e.g. plain map) a separate transaction handler might be created per each storage.
|
||||
*
|
||||
* @return See description.
|
||||
* @return See description. Never returns {@code null}
|
||||
*/
|
||||
public MapKeycloakTransaction<K, V, M> createTransaction(KeycloakSession session);
|
||||
|
||||
/**
|
||||
* Returns a {@link StringKeyConvertor} that is used to convert primary keys
|
||||
* from {@link String} to internal representation and vice versa.
|
||||
*
|
||||
* @return See above. Never returns {@code null}.
|
||||
*/
|
||||
public StringKeyConvertor<K> getKeyConvertor();
|
||||
|
||||
}
|
||||
|
|
|
@ -27,12 +27,13 @@ import org.keycloak.provider.Provider;
|
|||
public interface MapStorageProvider extends Provider {
|
||||
|
||||
/**
|
||||
* Returns a key-value storage
|
||||
* Returns a key-value storage implementation for the particular types.
|
||||
* @param <K> type of the primary key
|
||||
* @param <V> type of the value
|
||||
* @param name Name of the storage
|
||||
* @param flags
|
||||
* @return
|
||||
* @throws IllegalArgumentException If some of the types is not supported by the underlying implementation.
|
||||
*/
|
||||
<K, V extends AbstractEntity<K>, M> MapStorage<K, V, M> getStorage(String name, Class<K> keyType, Class<V> valueType, Class<M> modelType, Flag... flags);
|
||||
<K, V extends AbstractEntity<K>, M> MapStorage<K, V, M> getStorage(Class<V> valueType, Class<M> modelType, Flag... flags);
|
||||
}
|
||||
|
|
|
@ -16,27 +16,17 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public interface MapStorageProviderFactory extends ProviderFactory<MapStorageProvider> {
|
||||
public interface MapStorageProviderFactory extends ProviderFactory<MapStorageProvider>, ComponentFactory<MapStorageProvider, MapStorageProvider> {
|
||||
|
||||
public enum Flag {
|
||||
INITIALIZE_EMPTY,
|
||||
LOCAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key-value storage
|
||||
* @param <K> type of the primary key
|
||||
* @param <V> type of the value
|
||||
* @param name Name of the storage
|
||||
* @param flags
|
||||
* @return
|
||||
*/
|
||||
<K, V extends AbstractEntity<K>, M> MapStorage<K, V, M> getStorage(String name, Class<K> keyType, Class<V> valueType, Class<M> modelType, Flag... flags);
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ public interface ModelCriteriaBuilder<M> {
|
|||
* Creates and returns a new instance of {@code ModelCriteriaBuilder} that
|
||||
* combines the given builders with the Boolean OR operator.
|
||||
* <p>
|
||||
* Predicate coming out of {@code and} on an empty array of {@code builders}
|
||||
* Predicate coming out of {@code or} on an empty array of {@code builders}
|
||||
* (i.e. empty disjunction) is always {@code false}.
|
||||
*
|
||||
* <pre>
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright 2021 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.storage;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Converts given storage key from and to {@code String} representation.
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public interface StringKeyConvertor<K> {
|
||||
|
||||
/**
|
||||
* Returns String representation of the key from native representation
|
||||
* @param key
|
||||
* @throws IllegalArgumentException if the string format is not recognized
|
||||
* @throws NullPointerException if the parameter is {@code null}
|
||||
* @return See above
|
||||
*/
|
||||
default String keyToString(K key) { return key == null ? null : key.toString(); }
|
||||
|
||||
/**
|
||||
* Returns a new unique primary key for the storage that
|
||||
* this {@code StringKeyConvertor} belongs to. The uniqueness
|
||||
* needs to be guaranteed by e.g. using database sequences or
|
||||
* using a random value that is proved sufficiently improbable
|
||||
* to be repeated.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
K yieldNewUniqueKey();
|
||||
|
||||
/**
|
||||
* Returns native representation of the key from String representation
|
||||
* @param key
|
||||
* @throws IllegalArgumentException if the string format is not recognized
|
||||
* @throws NullPointerException if the parameter is {@code null}
|
||||
* @return See above
|
||||
*/
|
||||
K fromString(String key);
|
||||
|
||||
/**
|
||||
* Exception-free variant of {@link #fromString} method.
|
||||
* Returns native representation of the key from String representation,
|
||||
* or {@code null} if the {@code key} is either {@code null} or invalid.
|
||||
* @param key
|
||||
* @return See above
|
||||
*/
|
||||
default K fromStringSafe(String key) {
|
||||
try {
|
||||
return fromString(key);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UUIDKey implements StringKeyConvertor<UUID> {
|
||||
|
||||
public static final UUIDKey INSTANCE = new UUIDKey();
|
||||
|
||||
@Override
|
||||
public UUID yieldNewUniqueKey() {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID fromString(String key) {
|
||||
return UUID.fromString(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringKey implements StringKeyConvertor<String> {
|
||||
|
||||
public static final StringKey INSTANCE = new StringKey();
|
||||
|
||||
@Override
|
||||
public String fromString(String key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String yieldNewUniqueKey() {
|
||||
return fromString(UUID.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ULongKey implements StringKeyConvertor<Long> {
|
||||
|
||||
public static final ULongKey INSTANCE = new ULongKey();
|
||||
|
||||
/*
|
||||
* The random number generator used by this class to create random
|
||||
* based UUIDs. In a holder class to defer initialization until needed.
|
||||
*/
|
||||
private static class Holder {
|
||||
static final SecureRandom numberGenerator = new SecureRandom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long fromString(String key) {
|
||||
return key == null ? null : Long.parseUnsignedLong(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long yieldNewUniqueKey() {
|
||||
return Holder.numberGenerator.nextLong();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Stream;
|
||||
import org.keycloak.models.map.storage.MapModelCriteriaBuilder.UpdatePredicatesFunc;
|
||||
import org.keycloak.models.map.storage.StringKeyConvertor;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -43,10 +44,12 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
|
|||
private final ConcurrentMap<K, V> store = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<SearchableModelField<M>, UpdatePredicatesFunc<K, V, M>> fieldPredicates;
|
||||
private final StringKeyConvertor<K> keyConvertor;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConcurrentHashMapStorage(Class<M> modelClass) {
|
||||
public ConcurrentHashMapStorage(Class<M> modelClass, StringKeyConvertor<K> keyConvertor) {
|
||||
this.fieldPredicates = MapFieldPredicates.getPredicates(modelClass);
|
||||
this.keyConvertor = keyConvertor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,6 +111,10 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity<K>, M> impleme
|
|||
return sessionTransaction == null ? new MapKeycloakTransaction<>(this) : (MapKeycloakTransaction<K, V, M>) sessionTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringKeyConvertor<K> getKeyConvertor() {
|
||||
return keyConvertor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<V> read(ModelCriteriaBuilder<M> criteria) {
|
||||
|
|
|
@ -37,8 +37,8 @@ public class ConcurrentHashMapStorageProvider implements MapStorageProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> getStorage(String name,
|
||||
Class<K> keyType, Class<V> valueType, Class<M> modelType, Flag... flags) {
|
||||
return factory.getStorage(name, keyType, valueType, modelType, flags);
|
||||
public <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> getStorage(
|
||||
Class<V> valueType, Class<M> modelType, Flag... flags) {
|
||||
return factory.getStorage(valueType, modelType, flags);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,23 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage.chm;
|
||||
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.Config.Scope;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.component.ComponentModelScope;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
|
@ -35,13 +48,18 @@ import org.keycloak.models.map.storage.MapStorageProvider;
|
|||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.storage.StringKeyConvertor;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class ConcurrentHashMapStorageProviderFactory implements MapStorageProviderFactory {
|
||||
public class ConcurrentHashMapStorageProviderFactory implements AmphibianProviderFactory<MapStorageProvider>,MapStorageProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "concurrenthashmap";
|
||||
|
||||
|
@ -49,22 +67,69 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
|
|||
|
||||
private final ConcurrentHashMap<String, ConcurrentHashMapStorage<?,?,?>> storages = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, StringKeyConvertor> keyConvertors = new HashMap<>();
|
||||
|
||||
private File storageDirectory;
|
||||
|
||||
private String suffix;
|
||||
|
||||
private StringKeyConvertor defaultKeyConvertor;
|
||||
|
||||
public static final Map<Class<?>, String> MODEL_TO_NAME = new HashMap<>();
|
||||
static {
|
||||
MODEL_TO_NAME.put(AuthenticatedClientSessionModel.class, "client-sessions");
|
||||
MODEL_TO_NAME.put(ClientScopeModel.class, "client-scopes");
|
||||
MODEL_TO_NAME.put(ClientModel.class, "clients");
|
||||
MODEL_TO_NAME.put(GroupModel.class, "groups");
|
||||
MODEL_TO_NAME.put(RealmModel.class, "realms");
|
||||
MODEL_TO_NAME.put(RoleModel.class, "roles");
|
||||
MODEL_TO_NAME.put(RootAuthenticationSessionModel.class, "auth-sessions");
|
||||
MODEL_TO_NAME.put(UserLoginFailureModel.class, "user-login-failures");
|
||||
MODEL_TO_NAME.put(UserModel.class, "users");
|
||||
MODEL_TO_NAME.put(UserSessionModel.class, "user-sessions");
|
||||
|
||||
// authz
|
||||
MODEL_TO_NAME.put(PermissionTicket.class, "authz-permission-tickets");
|
||||
MODEL_TO_NAME.put(Policy.class, "authz-policies");
|
||||
MODEL_TO_NAME.put(ResourceServer.class, "authz-resource-servers");
|
||||
MODEL_TO_NAME.put(Resource.class, "authz-resources");
|
||||
MODEL_TO_NAME.put(org.keycloak.authorization.model.Scope.class, "authz-scopes");
|
||||
}
|
||||
|
||||
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
||||
static {
|
||||
KEY_CONVERTORS.put("uuid", StringKeyConvertor.UUIDKey.INSTANCE);
|
||||
KEY_CONVERTORS.put("string", StringKeyConvertor.StringKey.INSTANCE);
|
||||
KEY_CONVERTORS.put("ulong", StringKeyConvertor.ULongKey.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapStorageProvider create(KeycloakSession session) {
|
||||
return new ConcurrentHashMapStorageProvider(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void init(Scope config) {
|
||||
final String dir = config.get("dir");
|
||||
if (dir == null || dir.trim().isEmpty()) {
|
||||
LOG.warn("No directory set, created objects will not survive server restart");
|
||||
this.storageDirectory = null;
|
||||
if (config instanceof ComponentModelScope) {
|
||||
this.suffix = "-" + ((ComponentModelScope) config).getComponentId();
|
||||
} else {
|
||||
File f = new File(dir);
|
||||
try {
|
||||
this.suffix = "";
|
||||
}
|
||||
|
||||
final String keyType = config.get("keyType", "uuid");
|
||||
defaultKeyConvertor = getKeyConvertor(keyType);
|
||||
for (String name : MODEL_TO_NAME.values()) {
|
||||
keyConvertors.put(name, getKeyConvertor(config.get("keyType." + name, keyType)));
|
||||
}
|
||||
|
||||
final String dir = config.get("dir");
|
||||
try {
|
||||
if (dir == null || dir.trim().isEmpty()) {
|
||||
LOG.warn("No directory set, created objects will not survive server restart");
|
||||
this.storageDirectory = null;
|
||||
} else {
|
||||
File f = new File(dir);
|
||||
Files.createDirectories(f.toPath());
|
||||
if (f.exists()) {
|
||||
this.storageDirectory = f;
|
||||
|
@ -72,13 +137,21 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
|
|||
LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
|
||||
this.storageDirectory = null;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
|
||||
this.storageDirectory = null;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
|
||||
this.storageDirectory = null;
|
||||
}
|
||||
}
|
||||
|
||||
private StringKeyConvertor getKeyConvertor(final String keyType) throws IllegalArgumentException {
|
||||
StringKeyConvertor res = KEY_CONVERTORS.get(keyType);
|
||||
if (res == null) {
|
||||
throw new IllegalArgumentException("Unknown key type: " + keyType);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
@ -88,17 +161,17 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
|
|||
storages.forEach(this::storeMap);
|
||||
}
|
||||
|
||||
private void storeMap(String fileName, ConcurrentHashMapStorage<?, ?, ?> store) {
|
||||
if (fileName != null) {
|
||||
File f = getFile(fileName);
|
||||
private void storeMap(String mapName, ConcurrentHashMapStorage<?, ?, ?> store) {
|
||||
if (mapName != null) {
|
||||
File f = getFile(mapName);
|
||||
try {
|
||||
if (storageDirectory != null && storageDirectory.exists()) {
|
||||
if (storageDirectory != null) {
|
||||
LOG.debugf("Storing contents to %s", f.getCanonicalPath());
|
||||
@SuppressWarnings("unchecked")
|
||||
final ModelCriteriaBuilder readAllCriteria = store.createCriteriaBuilder();
|
||||
Serialization.MAPPER.writeValue(f, store.read(readAllCriteria));
|
||||
} else {
|
||||
LOG.debugf("Not storing contents of %s because directory %s does not exist", fileName, this.storageDirectory);
|
||||
LOG.debugf("Not storing contents of %s because directory not set", mapName);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
|
@ -106,19 +179,33 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
|
|||
}
|
||||
}
|
||||
|
||||
private <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> loadMap(String fileName,
|
||||
private <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> loadMap(String mapName,
|
||||
Class<V> valueType, Class<M> modelType, EnumSet<Flag> flags) {
|
||||
final StringKeyConvertor kc = keyConvertors.getOrDefault(mapName, defaultKeyConvertor);
|
||||
|
||||
LOG.debugf("Initializing new map storage: %s", mapName);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ConcurrentHashMapStorage<K, V, M> store;
|
||||
if (modelType == UserSessionModel.class) {
|
||||
ConcurrentHashMapStorage clientSessionStore =
|
||||
getStorage("clientSessions", UUID.class, MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
|
||||
store = new UserSessionConcurrentHashMapStorage<>(clientSessionStore);
|
||||
ConcurrentHashMapStorage clientSessionStore = getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
|
||||
store = new UserSessionConcurrentHashMapStorage(clientSessionStore, kc) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConcurrentHashMapStorage(" + mapName + suffix + ")";
|
||||
}
|
||||
};
|
||||
} else {
|
||||
store = new ConcurrentHashMapStorage<>(modelType);
|
||||
store = new ConcurrentHashMapStorage(modelType, kc) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConcurrentHashMapStorage(" + mapName + suffix + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (! flags.contains(Flag.INITIALIZE_EMPTY)) {
|
||||
final File f = getFile(fileName);
|
||||
final File f = getFile(mapName);
|
||||
if (f != null && f.exists()) {
|
||||
try {
|
||||
LOG.debugf("Restoring contents from %s", f.getCanonicalPath());
|
||||
|
@ -141,17 +228,38 @@ public class ConcurrentHashMapStorageProviderFactory implements MapStorageProvid
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> getStorage(String name,
|
||||
Class<K> keyType, Class<V> valueType, Class<M> modelType, Flag... flags) {
|
||||
public <K, V extends AbstractEntity<K>, M> ConcurrentHashMapStorage<K, V, M> getStorage(
|
||||
Class<V> valueType, Class<M> modelType, Flag... flags) {
|
||||
EnumSet<Flag> f = flags == null || flags.length == 0 ? EnumSet.noneOf(Flag.class) : EnumSet.of(flags[0], flags);
|
||||
String name = MODEL_TO_NAME.getOrDefault(modelType, modelType.getSimpleName());
|
||||
/* From ConcurrentHashMapStorage.computeIfAbsent javadoc:
|
||||
*
|
||||
* "... the computation [...] must not attempt to update any other mappings of this map."
|
||||
*
|
||||
* For UserSessionModel, there is a separate clientSessionStore in this CHM implementation. Thus
|
||||
* we cannot guarantee that this won't be the case e.g. for user and client sessions. Hence we need
|
||||
* to prepare clientSessionStore outside computeIfAbsent, otherwise deadlock occurs.
|
||||
*/
|
||||
if (modelType == UserSessionModel.class) {
|
||||
getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
|
||||
}
|
||||
return (ConcurrentHashMapStorage<K, V, M>) storages.computeIfAbsent(name, n -> loadMap(name, valueType, modelType, f));
|
||||
}
|
||||
|
||||
private File getFile(String fileName) {
|
||||
return storageDirectory == null
|
||||
? null
|
||||
: new File(storageDirectory, "map-" + fileName + ".json");
|
||||
: new File(storageDirectory, "map-" + fileName + suffix + ".json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "In-memory ConcurrentHashMap storage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,8 +23,9 @@ import org.keycloak.models.map.common.AbstractEntity;
|
|||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import org.keycloak.models.map.userSession.AbstractAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.AbstractUserSessionEntity;
|
||||
import org.keycloak.models.map.storage.StringKeyConvertor;
|
||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -34,15 +35,15 @@ import java.util.stream.Collectors;
|
|||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapStorage<K, AbstractUserSessionEntity<K>, UserSessionModel> {
|
||||
public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapStorage<K, MapUserSessionEntity<K>, UserSessionModel> {
|
||||
|
||||
private final ConcurrentHashMapStorage<K, AbstractAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionStore;
|
||||
private final ConcurrentHashMapStorage<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionStore;
|
||||
|
||||
private class Transaction extends MapKeycloakTransaction<K, AbstractUserSessionEntity<K>, UserSessionModel> {
|
||||
private class Transaction extends MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> {
|
||||
|
||||
private final MapKeycloakTransaction<K, AbstractAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionTr;
|
||||
private final MapKeycloakTransaction<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionTr;
|
||||
|
||||
public Transaction(MapKeycloakTransaction<K, AbstractAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionTr) {
|
||||
public Transaction(MapKeycloakTransaction<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionTr) {
|
||||
super(UserSessionConcurrentHashMapStorage.this);
|
||||
this.clientSessionTr = clientSessionTr;
|
||||
}
|
||||
|
@ -65,15 +66,16 @@ public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapSto
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UserSessionConcurrentHashMapStorage(ConcurrentHashMapStorage<K, AbstractAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionStore) {
|
||||
super(UserSessionModel.class);
|
||||
public UserSessionConcurrentHashMapStorage(ConcurrentHashMapStorage<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionStore,
|
||||
StringKeyConvertor<K> keyConvertor) {
|
||||
super(UserSessionModel.class, keyConvertor);
|
||||
this.clientSessionStore = clientSessionStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public MapKeycloakTransaction<K, AbstractUserSessionEntity<K>, UserSessionModel> createTransaction(KeycloakSession session) {
|
||||
public MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> createTransaction(KeycloakSession session) {
|
||||
MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
|
||||
return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction<K, AbstractUserSessionEntity<K>, UserSessionModel>) sessionTransaction;
|
||||
return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel>) sessionTransaction;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,371 +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.map.user;
|
||||
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public abstract class AbstractUserEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String username;
|
||||
private String firstName;
|
||||
private Long createdTimestamp;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private boolean enabled;
|
||||
private boolean emailVerified;
|
||||
// This is necessary to be able to dynamically switch unique email constraints on and off in the realm settings
|
||||
private String emailConstraint = KeycloakModelUtils.generateId();
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
private Set<String> requiredActions = new HashSet<>();
|
||||
private final Map<String, UserCredentialEntity> credentials = new HashMap<>();
|
||||
private final List<String> credentialsOrder = new LinkedList<>();
|
||||
private final Map<String, UserFederatedIdentityEntity> federatedIdentities = new HashMap<>();
|
||||
private final Map<String, UserConsentEntity> userConsents = new HashMap<>();
|
||||
private Set<String> groupsMembership = new HashSet<>();
|
||||
private Set<String> rolesMembership = new HashSet<>();
|
||||
private String federationLink;
|
||||
private String serviceAccountClientLink;
|
||||
private int notBefore;
|
||||
|
||||
static Comparator<AbstractUserEntity<?>> COMPARE_BY_USERNAME = Comparator.comparing(AbstractUserEntity::getUsername);
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
protected AbstractUserEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractUserEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated
|
||||
|| userConsents.values().stream().anyMatch(UserConsentEntity::isUpdated)
|
||||
|| credentials.values().stream().anyMatch(UserCredentialEntity::isUpdated)
|
||||
|| federatedIdentities.values().stream().anyMatch(UserFederatedIdentityEntity::isUpdated);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.updated |= !Objects.equals(this.username, username);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.updated |= !Objects.equals(this.firstName, firstName);
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.updated |= !Objects.equals(this.lastName, lastName);
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email, boolean duplicateEmailsAllowed) {
|
||||
this.updated |= !Objects.equals(this.email, email);
|
||||
this.email = email;
|
||||
this.emailConstraint = email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.updated |= !Objects.equals(this.enabled, enabled);
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isEmailVerified() {
|
||||
return emailVerified;
|
||||
}
|
||||
|
||||
public void setEmailVerified(boolean emailVerified) {
|
||||
this.updated |= !Objects.equals(this.emailVerified, emailVerified);
|
||||
this.emailVerified = emailVerified;
|
||||
}
|
||||
|
||||
public String getEmailConstraint() {
|
||||
return emailConstraint;
|
||||
}
|
||||
|
||||
public void setEmailConstraint(String emailConstraint) {
|
||||
this.updated |= !Objects.equals(this.emailConstraint, emailConstraint);
|
||||
this.emailConstraint = emailConstraint;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.getOrDefault(name, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= !Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public Set<String> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<String> requiredActions) {
|
||||
this.updated |= !Objects.equals(this.requiredActions, requiredActions);
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
|
||||
public void addRequiredAction(String requiredAction) {
|
||||
this.updated |= this.requiredActions.add(requiredAction);
|
||||
}
|
||||
|
||||
public void removeRequiredAction(String requiredAction) {
|
||||
this.updated |= this.requiredActions.remove(requiredAction);
|
||||
}
|
||||
|
||||
public void updateCredential(UserCredentialEntity credentialEntity) {
|
||||
this.updated |= credentials.replace(credentialEntity.getId(), credentialEntity) != null;
|
||||
}
|
||||
|
||||
public void addCredential(UserCredentialEntity credentialEntity) {
|
||||
if (credentials.containsKey(credentialEntity.getId())) {
|
||||
throw new ModelDuplicateException("A CredentialModel with given id already exists");
|
||||
}
|
||||
|
||||
this.updated = true;
|
||||
credentials.put(credentialEntity.getId(), credentialEntity);
|
||||
credentialsOrder.add(credentialEntity.getId());
|
||||
}
|
||||
|
||||
public boolean removeCredential(String credentialId) {
|
||||
if (!credentials.containsKey(credentialId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.updated = true;
|
||||
this.credentials.remove(credentialId);
|
||||
this.credentialsOrder.remove(credentialId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserCredentialEntity getCredential(String id) {
|
||||
return credentials.get(id);
|
||||
}
|
||||
|
||||
public Stream<UserCredentialEntity> getCredentials() {
|
||||
return credentialsOrder.stream()
|
||||
.map(credentials::get);
|
||||
}
|
||||
|
||||
public int getCredentialIndex(String credentialId) {
|
||||
return credentialsOrder.indexOf(credentialId);
|
||||
}
|
||||
|
||||
public void moveCredential(int currentPosition, int newPosition) {
|
||||
this.updated |= currentPosition != newPosition;
|
||||
credentialsOrder.add(newPosition, credentialsOrder.remove(currentPosition));
|
||||
}
|
||||
|
||||
public Stream<UserFederatedIdentityEntity> getFederatedIdentities() {
|
||||
return federatedIdentities.values().stream();
|
||||
}
|
||||
|
||||
public void setFederatedIdentities(Collection<UserFederatedIdentityEntity> federatedIdentities) {
|
||||
this.updated = true;
|
||||
this.federatedIdentities.clear();
|
||||
this.federatedIdentities.putAll(federatedIdentities.stream()
|
||||
.collect(Collectors.toMap(UserFederatedIdentityEntity::getIdentityProvider, Function.identity())));
|
||||
}
|
||||
|
||||
public void addFederatedIdentity(UserFederatedIdentityEntity federatedIdentity) {
|
||||
String idpId = federatedIdentity.getIdentityProvider();
|
||||
this.updated |= !Objects.equals(this.federatedIdentities.put(idpId, federatedIdentity), federatedIdentity);
|
||||
}
|
||||
|
||||
public UserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity) {
|
||||
return this.federatedIdentities.get(federatedIdentity);
|
||||
}
|
||||
|
||||
public boolean removeFederatedIdentity(String providerId) {
|
||||
boolean removed = federatedIdentities.remove(providerId) != null;
|
||||
this.updated |= removed;
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void updateFederatedIdentity(UserFederatedIdentityEntity federatedIdentityModel) {
|
||||
this.updated |= federatedIdentities.replace(federatedIdentityModel.getIdentityProvider(), federatedIdentityModel) != null;
|
||||
}
|
||||
|
||||
public Stream<UserConsentEntity> getUserConsents() {
|
||||
return userConsents.values().stream();
|
||||
}
|
||||
|
||||
public UserConsentEntity getUserConsent(String clientId) {
|
||||
return this.userConsents.get(clientId);
|
||||
}
|
||||
|
||||
|
||||
public void addUserConsent(UserConsentEntity userConsentEntity) {
|
||||
String clientId = userConsentEntity.getClientId();
|
||||
this.updated |= !Objects.equals(this.userConsents.put(clientId, userConsentEntity), userConsentEntity);
|
||||
}
|
||||
|
||||
public boolean removeUserConsent(String clientId) {
|
||||
boolean removed = userConsents.remove(clientId) != null;
|
||||
this.updated |= removed;
|
||||
return removed;
|
||||
}
|
||||
|
||||
public Set<String> getGroupsMembership() {
|
||||
return groupsMembership;
|
||||
}
|
||||
|
||||
public void setGroupsMembership(Set<String> groupsMembership) {
|
||||
this.updated |= Objects.equals(groupsMembership, this.groupsMembership);
|
||||
this.groupsMembership = groupsMembership;
|
||||
}
|
||||
|
||||
public void addGroupsMembership(String groupId) {
|
||||
this.updated |= this.groupsMembership.add(groupId);
|
||||
}
|
||||
|
||||
public void removeGroupsMembership(String groupId) {
|
||||
this.updated |= this.groupsMembership.remove(groupId);
|
||||
}
|
||||
|
||||
public Set<String> getRolesMembership() {
|
||||
return rolesMembership;
|
||||
}
|
||||
|
||||
public void setRolesMembership(Set<String> rolesMembership) {
|
||||
this.updated |= Objects.equals(rolesMembership, this.rolesMembership);
|
||||
this.rolesMembership = rolesMembership;
|
||||
}
|
||||
|
||||
public void addRolesMembership(String roleId) {
|
||||
this.updated |= this.rolesMembership.add(roleId);
|
||||
}
|
||||
|
||||
public void removeRolesMembership(String roleId) {
|
||||
this.updated |= this.rolesMembership.remove(roleId);
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.updated |= !Objects.equals(this.federationLink, federationLink);
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
|
||||
public String getServiceAccountClientLink() {
|
||||
return serviceAccountClientLink;
|
||||
}
|
||||
|
||||
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
||||
this.updated |= !Objects.equals(this.serviceAccountClientLink, serviceAccountClientLink);
|
||||
this.serviceAccountClientLink = serviceAccountClientLink;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.updated |= !Objects.equals(this.notBefore, notBefore);
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
}
|
|
@ -32,20 +32,14 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||
public MapUserAdapter(KeycloakSession session, RealmModel realm, MapUserEntity entity) {
|
||||
public abstract class MapUserAdapter<K> extends AbstractUserModel<MapUserEntity<K>> {
|
||||
public MapUserAdapter(KeycloakSession session, RealmModel realm, MapUserEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return entity.getUsername();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
|
@ -17,18 +17,355 @@
|
|||
|
||||
package org.keycloak.models.map.user;
|
||||
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MapUserEntity extends AbstractUserEntity<UUID> {
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapUserEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private final String realmId;
|
||||
|
||||
private String username;
|
||||
private String firstName;
|
||||
private Long createdTimestamp;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private boolean enabled;
|
||||
private boolean emailVerified;
|
||||
// This is necessary to be able to dynamically switch unique email constraints on and off in the realm settings
|
||||
private String emailConstraint = KeycloakModelUtils.generateId();
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
private Set<String> requiredActions = new HashSet<>();
|
||||
private final Map<String, UserCredentialEntity> credentials = new HashMap<>();
|
||||
private final List<String> credentialsOrder = new LinkedList<>();
|
||||
private final Map<String, UserFederatedIdentityEntity> federatedIdentities = new HashMap<>();
|
||||
private final Map<String, UserConsentEntity> userConsents = new HashMap<>();
|
||||
private Set<String> groupsMembership = new HashSet<>();
|
||||
private Set<String> rolesMembership = new HashSet<>();
|
||||
private String federationLink;
|
||||
private String serviceAccountClientLink;
|
||||
private int notBefore;
|
||||
|
||||
static Comparator<MapUserEntity<?>> COMPARE_BY_USERNAME = Comparator.comparing(MapUserEntity::getUsername);
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
public static final Comparator<MapUserEntity> COMPARE_BY_USERNAME = Comparator.comparing(MapUserEntity::getUsername, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
protected MapUserEntity() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapUserEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapUserEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated
|
||||
|| userConsents.values().stream().anyMatch(UserConsentEntity::isUpdated)
|
||||
|| credentials.values().stream().anyMatch(UserCredentialEntity::isUpdated)
|
||||
|| federatedIdentities.values().stream().anyMatch(UserFederatedIdentityEntity::isUpdated);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.updated |= !Objects.equals(this.username, username);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.updated |= !Objects.equals(this.firstName, firstName);
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.updated |= !Objects.equals(this.lastName, lastName);
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email, boolean duplicateEmailsAllowed) {
|
||||
this.updated |= !Objects.equals(this.email, email);
|
||||
this.email = email;
|
||||
this.emailConstraint = email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.updated |= !Objects.equals(this.enabled, enabled);
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isEmailVerified() {
|
||||
return emailVerified;
|
||||
}
|
||||
|
||||
public void setEmailVerified(boolean emailVerified) {
|
||||
this.updated |= !Objects.equals(this.emailVerified, emailVerified);
|
||||
this.emailVerified = emailVerified;
|
||||
}
|
||||
|
||||
public String getEmailConstraint() {
|
||||
return emailConstraint;
|
||||
}
|
||||
|
||||
public void setEmailConstraint(String emailConstraint) {
|
||||
this.updated |= !Objects.equals(this.emailConstraint, emailConstraint);
|
||||
this.emailConstraint = emailConstraint;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.getOrDefault(name, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.updated |= !Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public Set<String> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<String> requiredActions) {
|
||||
this.updated |= !Objects.equals(this.requiredActions, requiredActions);
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
|
||||
public void addRequiredAction(String requiredAction) {
|
||||
this.updated |= this.requiredActions.add(requiredAction);
|
||||
}
|
||||
|
||||
public void removeRequiredAction(String requiredAction) {
|
||||
this.updated |= this.requiredActions.remove(requiredAction);
|
||||
}
|
||||
|
||||
public void updateCredential(UserCredentialEntity credentialEntity) {
|
||||
this.updated |= credentials.replace(credentialEntity.getId(), credentialEntity) != null;
|
||||
}
|
||||
|
||||
public void addCredential(UserCredentialEntity credentialEntity) {
|
||||
if (credentials.containsKey(credentialEntity.getId())) {
|
||||
throw new ModelDuplicateException("A CredentialModel with given id already exists");
|
||||
}
|
||||
|
||||
this.updated = true;
|
||||
credentials.put(credentialEntity.getId(), credentialEntity);
|
||||
credentialsOrder.add(credentialEntity.getId());
|
||||
}
|
||||
|
||||
public boolean removeCredential(String credentialId) {
|
||||
if (!credentials.containsKey(credentialId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.updated = true;
|
||||
this.credentials.remove(credentialId);
|
||||
this.credentialsOrder.remove(credentialId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserCredentialEntity getCredential(String id) {
|
||||
return credentials.get(id);
|
||||
}
|
||||
|
||||
public Stream<UserCredentialEntity> getCredentials() {
|
||||
return credentialsOrder.stream()
|
||||
.map(credentials::get);
|
||||
}
|
||||
|
||||
public int getCredentialIndex(String credentialId) {
|
||||
return credentialsOrder.indexOf(credentialId);
|
||||
}
|
||||
|
||||
public void moveCredential(int currentPosition, int newPosition) {
|
||||
this.updated |= currentPosition != newPosition;
|
||||
credentialsOrder.add(newPosition, credentialsOrder.remove(currentPosition));
|
||||
}
|
||||
|
||||
public Stream<UserFederatedIdentityEntity> getFederatedIdentities() {
|
||||
return federatedIdentities.values().stream();
|
||||
}
|
||||
|
||||
public void setFederatedIdentities(Collection<UserFederatedIdentityEntity> federatedIdentities) {
|
||||
this.updated = true;
|
||||
this.federatedIdentities.clear();
|
||||
this.federatedIdentities.putAll(federatedIdentities.stream()
|
||||
.collect(Collectors.toMap(UserFederatedIdentityEntity::getIdentityProvider, Function.identity())));
|
||||
}
|
||||
|
||||
public void addFederatedIdentity(UserFederatedIdentityEntity federatedIdentity) {
|
||||
String idpId = federatedIdentity.getIdentityProvider();
|
||||
this.updated |= !Objects.equals(this.federatedIdentities.put(idpId, federatedIdentity), federatedIdentity);
|
||||
}
|
||||
|
||||
public UserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity) {
|
||||
return this.federatedIdentities.get(federatedIdentity);
|
||||
}
|
||||
|
||||
public boolean removeFederatedIdentity(String providerId) {
|
||||
boolean removed = federatedIdentities.remove(providerId) != null;
|
||||
this.updated |= removed;
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void updateFederatedIdentity(UserFederatedIdentityEntity federatedIdentityModel) {
|
||||
this.updated |= federatedIdentities.replace(federatedIdentityModel.getIdentityProvider(), federatedIdentityModel) != null;
|
||||
}
|
||||
|
||||
public Stream<UserConsentEntity> getUserConsents() {
|
||||
return userConsents.values().stream();
|
||||
}
|
||||
|
||||
public UserConsentEntity getUserConsent(String clientId) {
|
||||
return this.userConsents.get(clientId);
|
||||
}
|
||||
|
||||
|
||||
public void addUserConsent(UserConsentEntity userConsentEntity) {
|
||||
String clientId = userConsentEntity.getClientId();
|
||||
this.updated |= !Objects.equals(this.userConsents.put(clientId, userConsentEntity), userConsentEntity);
|
||||
}
|
||||
|
||||
public boolean removeUserConsent(String clientId) {
|
||||
boolean removed = userConsents.remove(clientId) != null;
|
||||
this.updated |= removed;
|
||||
return removed;
|
||||
}
|
||||
|
||||
public Set<String> getGroupsMembership() {
|
||||
return groupsMembership;
|
||||
}
|
||||
|
||||
public void setGroupsMembership(Set<String> groupsMembership) {
|
||||
this.updated |= Objects.equals(groupsMembership, this.groupsMembership);
|
||||
this.groupsMembership = groupsMembership;
|
||||
}
|
||||
|
||||
public void addGroupsMembership(String groupId) {
|
||||
this.updated |= this.groupsMembership.add(groupId);
|
||||
}
|
||||
|
||||
public void removeGroupsMembership(String groupId) {
|
||||
this.updated |= this.groupsMembership.remove(groupId);
|
||||
}
|
||||
|
||||
public Set<String> getRolesMembership() {
|
||||
return rolesMembership;
|
||||
}
|
||||
|
||||
public void setRolesMembership(Set<String> rolesMembership) {
|
||||
this.updated |= Objects.equals(rolesMembership, this.rolesMembership);
|
||||
this.rolesMembership = rolesMembership;
|
||||
}
|
||||
|
||||
public void addRolesMembership(String roleId) {
|
||||
this.updated |= this.rolesMembership.add(roleId);
|
||||
}
|
||||
|
||||
public void removeRolesMembership(String roleId) {
|
||||
this.updated |= this.rolesMembership.remove(roleId);
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.updated |= !Objects.equals(this.federationLink, federationLink);
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
|
||||
public String getServiceAccountClientLink() {
|
||||
return serviceAccountClientLink;
|
||||
}
|
||||
|
||||
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
||||
this.updated |= !Objects.equals(this.serviceAccountClientLink, serviceAccountClientLink);
|
||||
this.serviceAccountClientLink = serviceAccountClientLink;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.updated |= !Objects.equals(this.notBefore, notBefore);
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -73,30 +72,33 @@ import static org.keycloak.models.UserModel.LAST_NAME;
|
|||
import static org.keycloak.models.UserModel.USERNAME;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
|
||||
public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialStore.Streams {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapUserProvider.class);
|
||||
private static final Predicate<MapUserEntity> ALWAYS_FALSE = c -> { return false; };
|
||||
private final KeycloakSession session;
|
||||
final MapKeycloakTransaction<UUID, MapUserEntity, UserModel> tx;
|
||||
private final MapStorage<UUID, MapUserEntity, UserModel> userStore;
|
||||
final MapKeycloakTransaction<K, MapUserEntity<K>, UserModel> tx;
|
||||
private final MapStorage<K, MapUserEntity<K>, UserModel> userStore;
|
||||
|
||||
public MapUserProvider(KeycloakSession session, MapStorage<UUID, MapUserEntity, UserModel> store) {
|
||||
public MapUserProvider(KeycloakSession session, MapStorage<K, MapUserEntity<K>, UserModel> store) {
|
||||
this.session = session;
|
||||
this.userStore = store;
|
||||
this.tx = userStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapUserEntity registerEntityForChanges(MapUserEntity origEntity) {
|
||||
MapUserEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapUserEntity::isUpdated);
|
||||
private MapUserEntity<K> registerEntityForChanges(MapUserEntity<K> origEntity) {
|
||||
MapUserEntity<K> res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapUserEntity<K>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Function<MapUserEntity, UserModel> entityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapUserEntity<K>, UserModel> entityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return origEntity -> new MapUserAdapter(session, realm, registerEntityForChanges(origEntity)) {
|
||||
return origEntity -> new MapUserAdapter<K>(session, realm, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return userStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEmailUniqueness(RealmModel realm, String email) {
|
||||
|
@ -110,9 +112,9 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
};
|
||||
}
|
||||
|
||||
private Predicate<MapUserEntity> entityRealmFilter(RealmModel realm) {
|
||||
private Predicate<MapUserEntity<K>> entityRealmFilter(RealmModel realm) {
|
||||
if (realm == null || realm.getId() == null) {
|
||||
return MapUserProvider.ALWAYS_FALSE;
|
||||
return c -> false;
|
||||
}
|
||||
String realmId = realm.getId();
|
||||
return entity -> Objects.equals(realmId, entity.getRealmId());
|
||||
|
@ -122,22 +124,22 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
return new ModelException("Specified user doesn't exist.");
|
||||
}
|
||||
|
||||
private Optional<MapUserEntity> getEntityById(RealmModel realm, String id) {
|
||||
private Optional<MapUserEntity<K>> getEntityById(RealmModel realm, String id) {
|
||||
try {
|
||||
return getEntityById(realm, UUID.fromString(id));
|
||||
return getEntityById(realm, userStore.getKeyConvertor().fromString(id));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private MapUserEntity getRegisteredEntityByIdOrThrow(RealmModel realm, String id) {
|
||||
private MapUserEntity<K> getRegisteredEntityByIdOrThrow(RealmModel realm, String id) {
|
||||
return getEntityById(realm, id)
|
||||
.map(this::registerEntityForChanges)
|
||||
.orElseThrow(this::userDoesntExistException);
|
||||
}
|
||||
|
||||
private Optional<MapUserEntity> getEntityById(RealmModel realm, UUID id) {
|
||||
MapUserEntity mapUserEntity = tx.read(id);
|
||||
private Optional<MapUserEntity<K>> getEntityById(RealmModel realm, K id) {
|
||||
MapUserEntity<K> mapUserEntity = tx.read(id);
|
||||
if (mapUserEntity != null && entityRealmFilter(realm).test(mapUserEntity)) {
|
||||
return Optional.of(mapUserEntity);
|
||||
}
|
||||
|
@ -145,7 +147,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<MapUserEntity> getRegisteredEntityById(RealmModel realm, String id) {
|
||||
private Optional<MapUserEntity<K>> getRegisteredEntityById(RealmModel realm, String id) {
|
||||
return getEntityById(realm, id).map(this::registerEntityForChanges);
|
||||
}
|
||||
|
||||
|
@ -193,7 +195,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
public Stream<FederatedIdentityModel> getFederatedIdentitiesStream(RealmModel realm, UserModel user) {
|
||||
LOG.tracef("getFederatedIdentitiesStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
||||
return getEntityById(realm, user.getId())
|
||||
.map(AbstractUserEntity::getFederatedIdentities).orElseGet(Stream::empty)
|
||||
.map(MapUserEntity::getFederatedIdentities).orElseGet(Stream::empty)
|
||||
.map(UserFederatedIdentityEntity::toModel);
|
||||
}
|
||||
|
||||
|
@ -249,7 +251,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
public Stream<UserConsentModel> getConsentsStream(RealmModel realm, String userId) {
|
||||
LOG.tracef("getConsentByClientStream(%s, %s)%s", realm, userId, getShortStackTrace());
|
||||
return getEntityById(realm, userId)
|
||||
.map(AbstractUserEntity::getUserConsents)
|
||||
.map(MapUserEntity::getUserConsents)
|
||||
.orElse(Stream.empty())
|
||||
.map(consent -> UserConsentEntity.toModel(realm, consent));
|
||||
}
|
||||
|
@ -258,7 +260,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
public void updateConsent(RealmModel realm, String userId, UserConsentModel consent) {
|
||||
LOG.tracef("updateConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
|
||||
|
||||
MapUserEntity user = getRegisteredEntityByIdOrThrow(realm, userId);
|
||||
MapUserEntity<K> user = getRegisteredEntityByIdOrThrow(realm, userId);
|
||||
UserConsentEntity userConsentEntity = user.getUserConsent(consent.getClient().getId());
|
||||
if (userConsentEntity == null) {
|
||||
throw new ModelException("Consent not found for client [" + consent.getClient().getId() + "] and user [" + userId + "]");
|
||||
|
@ -329,13 +331,13 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
throw new ModelDuplicateException("User with username '" + username + "' in realm " + realm.getName() + " already exists" );
|
||||
}
|
||||
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final K entityId = id == null ? userStore.getKeyConvertor().yieldNewUniqueKey() : userStore.getKeyConvertor().fromString(id);
|
||||
|
||||
if (tx.read(entityId) != null) {
|
||||
throw new ModelDuplicateException("User exists: " + entityId);
|
||||
}
|
||||
|
||||
MapUserEntity entity = new MapUserEntity(entityId, realm.getId());
|
||||
MapUserEntity<K> entity = new MapUserEntity<>(entityId, realm.getId());
|
||||
entity.setUsername(username.toLowerCase());
|
||||
entity.setCreatedTimestamp(Time.currentTimeMillis());
|
||||
|
||||
|
@ -366,7 +368,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
ModelCriteriaBuilder<UserModel> mcb = userStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
|
||||
|
||||
tx.delete(UUID.randomUUID(), mcb);
|
||||
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -376,7 +378,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
|
||||
|
||||
tx.delete(UUID.randomUUID(), mcb);
|
||||
tx.delete(userStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -386,7 +388,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.map(this::registerEntityForChanges)
|
||||
.forEach(userEntity -> userEntity.setFederationLink(null));
|
||||
}
|
||||
|
@ -400,7 +402,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.map(this::registerEntityForChanges)
|
||||
.forEach(userEntity -> userEntity.removeRolesMembership(roleId));
|
||||
}
|
||||
|
@ -414,7 +416,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.map(this::registerEntityForChanges)
|
||||
.forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
|
||||
}
|
||||
|
@ -428,7 +430,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.map(this::registerEntityForChanges)
|
||||
.forEach(userEntity -> userEntity.removeUserConsent(clientId));
|
||||
}
|
||||
|
@ -448,8 +450,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, clientScope.getRealm().getId())
|
||||
.compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.flatMap(AbstractUserEntity::getUserConsents)
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.flatMap(MapUserEntity::getUserConsents)
|
||||
.forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId));
|
||||
}
|
||||
}
|
||||
|
@ -466,14 +468,14 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, Operator.EQ, componentId);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
String providerIdS = new StorageId(componentId, "").getId();
|
||||
s.forEach(removeConsentsForExternalClient(providerIdS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Consumer<MapUserEntity> removeConsentsForExternalClient(String idPrefix) {
|
||||
private Consumer<MapUserEntity<K>> removeConsentsForExternalClient(String idPrefix) {
|
||||
return userEntity -> {
|
||||
List<String> consentClientIds = userEntity.getUserConsents()
|
||||
.map(UserConsentEntity::getClientId)
|
||||
|
@ -494,7 +496,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
ModelCriteriaBuilder<UserModel> mcb = userStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
s.map(this::registerEntityForChanges)
|
||||
.forEach(entity -> entity.addRolesMembership(roleId));
|
||||
}
|
||||
|
@ -514,7 +516,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.USERNAME, Operator.ILIKE, username);
|
||||
|
||||
try (Stream<MapUserEntity> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
|
||||
return s.findFirst()
|
||||
.map(entityToAdapterFunc(realm)).orElse(null);
|
||||
}
|
||||
|
@ -527,7 +529,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
|
||||
.compare(SearchableFields.EMAIL, Operator.EQ, email);
|
||||
|
||||
List<MapUserEntity> usersWithEmail = tx.getUpdatedNotRemoved(mcb)
|
||||
List<MapUserEntity<K>> usersWithEmail = tx.getUpdatedNotRemoved(mcb)
|
||||
.filter(userEntity -> Objects.equals(userEntity.getEmail(), email))
|
||||
.collect(Collectors.toList());
|
||||
if (usersWithEmail.isEmpty()) return null;
|
||||
|
@ -538,7 +540,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
throw new ModelDuplicateException("Multiple users with email '" + email + "' exist in Keycloak.");
|
||||
}
|
||||
|
||||
MapUserEntity userEntity = registerEntityForChanges(usersWithEmail.get(0));
|
||||
MapUserEntity<K> userEntity = registerEntityForChanges(usersWithEmail.get(0));
|
||||
|
||||
if (!realm.isDuplicateEmailsAllowed()) {
|
||||
if (userEntity.getEmail() != null && !userEntity.getEmail().equals(userEntity.getEmailConstraint())) {
|
||||
|
@ -548,7 +550,12 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
}
|
||||
}
|
||||
|
||||
return new MapUserAdapter(session, realm, userEntity) {
|
||||
return new MapUserAdapter<K>(session, realm, userEntity) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return userStore.getKeyConvertor().keyToString(userEntity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEmailUniqueness(RealmModel realm, String email) {
|
||||
return getUserByEmail(realm, email) != null;
|
||||
|
@ -700,8 +707,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
mcb = mcb.compare(SearchableFields.ASSIGNED_GROUP, Operator.IN, authorizedGroups);
|
||||
}
|
||||
|
||||
Stream<MapUserEntity> usersStream = tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(AbstractUserEntity.COMPARE_BY_USERNAME); // Sort before paginating
|
||||
Stream<MapUserEntity<K>> usersStream = tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(MapUserEntity.COMPARE_BY_USERNAME); // Sort before paginating
|
||||
|
||||
return paginatedStream(usersStream, firstResult, maxResults) // paginate if necessary
|
||||
.map(entityToAdapterFunc(realm))
|
||||
|
@ -739,9 +746,9 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
String userId = user.getId();
|
||||
Optional<MapUserEntity> userById = getEntityById(realm, userId);
|
||||
Optional<MapUserEntity<K>> userById = getEntityById(realm, userId);
|
||||
if (userById.isPresent()) {
|
||||
tx.delete(UUID.fromString(userId));
|
||||
tx.delete(userStore.getKeyConvertor().fromString(userId));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -766,7 +773,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
.ifPresent(updateCredential(cred));
|
||||
}
|
||||
|
||||
private Consumer<MapUserEntity> updateCredential(CredentialModel credentialModel) {
|
||||
private Consumer<MapUserEntity<K>> updateCredential(CredentialModel credentialModel) {
|
||||
return user -> {
|
||||
UserCredentialEntity credentialEntity = user.getCredential(credentialModel.getId());
|
||||
if (credentialEntity == null) return;
|
||||
|
@ -811,7 +818,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||
LOG.tracef("getStoredCredentialsStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
||||
return getEntityById(realm, user.getId())
|
||||
.map(AbstractUserEntity::getCredentials)
|
||||
.map(MapUserEntity::getCredentials)
|
||||
.orElseGet(Stream::empty)
|
||||
.map(UserCredentialEntity::toModel);
|
||||
}
|
||||
|
@ -835,7 +842,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) {
|
||||
LOG.tracef("moveCredentialTo(%s, %s, %s, %s)%s", realm, user.getId(), id, newPreviousCredentialId, getShortStackTrace());
|
||||
String userId = user.getId();
|
||||
MapUserEntity userEntity = getRegisteredEntityById(realm, userId).orElse(null);
|
||||
MapUserEntity<K> userEntity = getRegisteredEntityById(realm, userId).orElse(null);
|
||||
if (userEntity == null) {
|
||||
LOG.warnf("User with id: [%s] not found", userId);
|
||||
return false;
|
||||
|
|
|
@ -18,34 +18,29 @@
|
|||
package org.keycloak.models.map.user;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.UserProviderFactory;
|
||||
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapUserProviderFactory extends AbstractMapProviderFactory<UserProvider> implements UserProviderFactory {
|
||||
public class MapUserProviderFactory<K> extends AbstractMapProviderFactory<UserProvider, K, MapUserEntity<K>, UserModel> implements UserProviderFactory {
|
||||
|
||||
private MapStorage<UUID, MapUserEntity, UserModel> store;
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
this.store = sp.getStorage("users", UUID.class, MapUserEntity.class, UserModel.class);
|
||||
public MapUserProviderFactory() {
|
||||
super(MapUserEntity.class, UserModel.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserProvider create(KeycloakSession session) {
|
||||
return new MapUserProvider(session, store);
|
||||
return new MapUserProvider<>(session, getStorage(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "User provider";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.userSession;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractAuthenticatedClientSessionEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private K id;
|
||||
private String userSessionId;
|
||||
private String realmId;
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private String authMethod;
|
||||
private String redirectUri;
|
||||
private volatile int timestamp;
|
||||
private long expiration;
|
||||
private String action;
|
||||
|
||||
private Map<String, String> notes = new ConcurrentHashMap<>();
|
||||
|
||||
private String currentRefreshToken;
|
||||
private int currentRefreshTokenUseCount;
|
||||
|
||||
private boolean offline;
|
||||
|
||||
public AbstractAuthenticatedClientSessionEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractAuthenticatedClientSessionEntity(K id, String userSessionId, String realmId, String clientId, boolean offline) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(userSessionId, "userSessionId");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
Objects.requireNonNull(clientId, "clientId");
|
||||
|
||||
this.id = id;
|
||||
this.userSessionId = userSessionId;
|
||||
this.realmId = realmId;
|
||||
this.clientId = clientId;
|
||||
this.offline = offline;
|
||||
this.timestamp = Time.currentTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= !Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
}
|
||||
|
||||
public void setUserSessionId(String userSessionId) {
|
||||
this.updated |= !Objects.equals(this.userSessionId, userSessionId);
|
||||
this.userSessionId = userSessionId;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.updated |= !Objects.equals(this.authMethod, authMethod);
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.updated |= !Objects.equals(this.redirectUri, redirectUri);
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.updated |= this.timestamp != timestamp;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(long expiration) {
|
||||
this.updated |= this.expiration != expiration;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.updated |= !Objects.equals(this.action, action);
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.updated |= !Objects.equals(this.notes, notes);
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public String removeNote(String name) {
|
||||
String note = this.notes.remove(name);
|
||||
this.updated |= note != null;
|
||||
return note;
|
||||
}
|
||||
|
||||
public void addNote(String name, String value) {
|
||||
this.updated |= !Objects.equals(this.notes.put(name, value), value);
|
||||
}
|
||||
|
||||
public String getCurrentRefreshToken() {
|
||||
return currentRefreshToken;
|
||||
}
|
||||
|
||||
public void setCurrentRefreshToken(String currentRefreshToken) {
|
||||
this.updated |= !Objects.equals(this.currentRefreshToken, currentRefreshToken);
|
||||
this.currentRefreshToken = currentRefreshToken;
|
||||
}
|
||||
|
||||
public int getCurrentRefreshTokenUseCount() {
|
||||
return currentRefreshTokenUseCount;
|
||||
}
|
||||
|
||||
public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) {
|
||||
this.updated |= this.currentRefreshTokenUseCount != currentRefreshTokenUseCount;
|
||||
this.currentRefreshTokenUseCount = currentRefreshTokenUseCount;
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return offline;
|
||||
}
|
||||
|
||||
public void setOffline(boolean offline) {
|
||||
this.updated |= this.offline != offline;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
|
@ -28,15 +28,15 @@ import java.util.Objects;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractAuthenticatedClientSessionModel<E extends AbstractEntity> implements AuthenticatedClientSessionModel {
|
||||
public abstract class AbstractAuthenticatedClientSessionModel<K> implements AuthenticatedClientSessionModel {
|
||||
protected final KeycloakSession session;
|
||||
protected final RealmModel realm;
|
||||
protected ClientModel client;
|
||||
protected UserSessionModel userSession;
|
||||
protected final E entity;
|
||||
protected final MapAuthenticatedClientSessionEntity<K> entity;
|
||||
|
||||
public AbstractAuthenticatedClientSessionModel(KeycloakSession session, RealmModel realm, ClientModel client,
|
||||
UserSessionModel userSession, E entity) {
|
||||
UserSessionModel userSession, MapAuthenticatedClientSessionEntity<K> entity) {
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
Objects.requireNonNull(realm, "realm");
|
||||
Objects.requireNonNull(client, "client");
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 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.userSession;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractUserSessionEntity<K> implements AbstractEntity<K> {
|
||||
private K id;
|
||||
|
||||
private String realmId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String brokerSessionId;
|
||||
private String brokerUserId;
|
||||
|
||||
private String loginUsername;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private String authMethod;
|
||||
|
||||
private boolean rememberMe;
|
||||
|
||||
private int started;
|
||||
|
||||
private int lastSessionRefresh;
|
||||
|
||||
private long expiration;
|
||||
|
||||
private Map<String, String> notes = new ConcurrentHashMap<>();
|
||||
|
||||
private UserSessionModel.State state;
|
||||
|
||||
private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT;
|
||||
|
||||
private Map<String, K> authenticatedClientSessions = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean offline;
|
||||
|
||||
public AbstractUserSessionEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public AbstractUserSessionEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public AbstractUserSessionEntity(K id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
|
||||
String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
|
||||
boolean offline) {
|
||||
this.id = id;
|
||||
this.realmId = realm.getId();
|
||||
this.userId = user.getId();
|
||||
this.loginUsername = loginUsername;
|
||||
this.ipAddress = ipAddress;
|
||||
this.authMethod = authMethod;
|
||||
this.rememberMe = rememberMe;
|
||||
this.brokerSessionId = brokerSessionId;
|
||||
this.brokerUserId = brokerUserId;
|
||||
this.started = Time.currentTime();
|
||||
this.lastSessionRefresh = started;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.updated |= !Objects.equals(this.userId, userId);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getBrokerSessionId() {
|
||||
return brokerSessionId;
|
||||
}
|
||||
|
||||
public void setBrokerSessionId(String brokerSessionId) {
|
||||
this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId);
|
||||
this.brokerSessionId = brokerSessionId;
|
||||
}
|
||||
|
||||
public String getBrokerUserId() {
|
||||
return brokerUserId;
|
||||
}
|
||||
|
||||
public void setBrokerUserId(String brokerUserId) {
|
||||
this.updated |= !Objects.equals(this.brokerUserId, brokerUserId);
|
||||
this.brokerUserId = brokerUserId;
|
||||
}
|
||||
|
||||
public String getLoginUsername() {
|
||||
return loginUsername;
|
||||
}
|
||||
|
||||
public void setLoginUsername(String loginUsername) {
|
||||
this.updated |= !Objects.equals(this.loginUsername, loginUsername);
|
||||
this.loginUsername = loginUsername;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.updated |= !Objects.equals(this.ipAddress, ipAddress);
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.updated |= !Objects.equals(this.authMethod, authMethod);
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public boolean isRememberMe() {
|
||||
return rememberMe;
|
||||
}
|
||||
|
||||
public void setRememberMe(boolean rememberMe) {
|
||||
this.updated |= this.rememberMe != rememberMe;
|
||||
this.rememberMe = rememberMe;
|
||||
}
|
||||
|
||||
public int getStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
public void setStarted(int started) {
|
||||
this.updated |= this.started != started;
|
||||
this.started = started;
|
||||
}
|
||||
|
||||
public int getLastSessionRefresh() {
|
||||
return lastSessionRefresh;
|
||||
}
|
||||
|
||||
public void setLastSessionRefresh(int lastSessionRefresh) {
|
||||
this.updated |= this.lastSessionRefresh != lastSessionRefresh;
|
||||
this.lastSessionRefresh = lastSessionRefresh;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(long expiration) {
|
||||
this.updated |= this.expiration != expiration;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public String getNote(String name) {
|
||||
return notes.get(name);
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.updated |= !Objects.equals(this.notes, notes);
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public String removeNote(String name) {
|
||||
String note = this.notes.remove(name);
|
||||
this.updated |= note != null;
|
||||
return note;
|
||||
}
|
||||
|
||||
public void addNote(String name, String value) {
|
||||
this.updated |= !Objects.equals(this.notes.put(name, value), value);
|
||||
}
|
||||
|
||||
public UserSessionModel.State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(UserSessionModel.State state) {
|
||||
this.updated |= !Objects.equals(this.state, state);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Map<String, K> getAuthenticatedClientSessions() {
|
||||
return authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public void setAuthenticatedClientSessions(Map<String, K> authenticatedClientSessions) {
|
||||
this.updated |= !Objects.equals(this.authenticatedClientSessions, authenticatedClientSessions);
|
||||
this.authenticatedClientSessions = authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public void addAuthenticatedClientSession(String clientId, K clientSessionId) {
|
||||
this.updated |= !Objects.equals(this.authenticatedClientSessions.put(clientId, clientSessionId), clientSessionId);
|
||||
}
|
||||
|
||||
public K removeAuthenticatedClientSession(String clientId) {
|
||||
K entity = this.authenticatedClientSessions.remove(clientId);
|
||||
this.updated |= entity != null;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void clearAuthenticatedClientSessions() {
|
||||
this.updated |= !authenticatedClientSessions.isEmpty();
|
||||
this.authenticatedClientSessions.clear();
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return offline;
|
||||
}
|
||||
|
||||
public void setOffline(boolean offline) {
|
||||
this.updated |= this.offline != offline;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
public UserSessionModel.SessionPersistenceState getPersistenceState() {
|
||||
return persistenceState;
|
||||
}
|
||||
|
||||
public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) {
|
||||
this.updated |= !Objects.equals(this.persistenceState, persistenceState);
|
||||
this.persistenceState = persistenceState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
|
@ -26,12 +26,12 @@ import java.util.Objects;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class AbstractUserSessionModel<E extends AbstractEntity> implements UserSessionModel {
|
||||
public abstract class AbstractUserSessionModel<K> implements UserSessionModel {
|
||||
protected final KeycloakSession session;
|
||||
protected final RealmModel realm;
|
||||
protected final E entity;
|
||||
protected final MapUserSessionEntity<K> entity;
|
||||
|
||||
public AbstractUserSessionModel(KeycloakSession session, RealmModel realm, E entity) {
|
||||
public AbstractUserSessionModel(KeycloakSession session, RealmModel realm, MapUserSessionEntity<K> entity) {
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
Objects.requireNonNull(realm, "realm");
|
||||
|
||||
|
@ -39,18 +39,4 @@ public abstract class AbstractUserSessionModel<E extends AbstractEntity> impleme
|
|||
this.realm = realm;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof UserSessionModel)) return false;
|
||||
|
||||
UserSessionModel that = (UserSessionModel) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,18 +26,13 @@ import java.util.Map;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthenticatedClientSessionModel<MapAuthenticatedClientSessionEntity> {
|
||||
public abstract class MapAuthenticatedClientSessionAdapter<K> extends AbstractAuthenticatedClientSessionModel<K> {
|
||||
|
||||
public MapAuthenticatedClientSessionAdapter(KeycloakSession session, RealmModel realm, ClientModel client,
|
||||
UserSessionModel userSession, MapAuthenticatedClientSessionEntity entity) {
|
||||
UserSessionModel userSession, MapAuthenticatedClientSessionEntity<K> entity) {
|
||||
super(session, realm, client, userSession, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp() {
|
||||
return entity.getTimestamp();
|
||||
|
|
|
@ -16,18 +16,190 @@
|
|||
*/
|
||||
package org.keycloak.models.map.userSession;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapAuthenticatedClientSessionEntity extends AbstractAuthenticatedClientSessionEntity<UUID> {
|
||||
public class MapAuthenticatedClientSessionEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
protected MapAuthenticatedClientSessionEntity() {
|
||||
super();
|
||||
private K id;
|
||||
private String userSessionId;
|
||||
private String realmId;
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private String authMethod;
|
||||
private String redirectUri;
|
||||
private volatile int timestamp;
|
||||
private long expiration;
|
||||
private String action;
|
||||
|
||||
private Map<String, String> notes = new ConcurrentHashMap<>();
|
||||
|
||||
private String currentRefreshToken;
|
||||
private int currentRefreshTokenUseCount;
|
||||
|
||||
private boolean offline;
|
||||
|
||||
public MapAuthenticatedClientSessionEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapAuthenticatedClientSessionEntity(UUID id, String userSessionId, String realmId, String clientId, boolean offline) {
|
||||
super(id, userSessionId, realmId, clientId, offline);
|
||||
public MapAuthenticatedClientSessionEntity(K id, String userSessionId, String realmId, String clientId, boolean offline) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(userSessionId, "userSessionId");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
Objects.requireNonNull(clientId, "clientId");
|
||||
|
||||
this.id = id;
|
||||
this.userSessionId = userSessionId;
|
||||
this.realmId = realmId;
|
||||
this.clientId = clientId;
|
||||
this.offline = offline;
|
||||
this.timestamp = Time.currentTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.updated |= !Objects.equals(this.clientId, clientId);
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
}
|
||||
|
||||
public void setUserSessionId(String userSessionId) {
|
||||
this.updated |= !Objects.equals(this.userSessionId, userSessionId);
|
||||
this.userSessionId = userSessionId;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.updated |= !Objects.equals(this.authMethod, authMethod);
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.updated |= !Objects.equals(this.redirectUri, redirectUri);
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.updated |= this.timestamp != timestamp;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(long expiration) {
|
||||
this.updated |= this.expiration != expiration;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.updated |= !Objects.equals(this.action, action);
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.updated |= !Objects.equals(this.notes, notes);
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public String removeNote(String name) {
|
||||
String note = this.notes.remove(name);
|
||||
this.updated |= note != null;
|
||||
return note;
|
||||
}
|
||||
|
||||
public void addNote(String name, String value) {
|
||||
this.updated |= !Objects.equals(this.notes.put(name, value), value);
|
||||
}
|
||||
|
||||
public String getCurrentRefreshToken() {
|
||||
return currentRefreshToken;
|
||||
}
|
||||
|
||||
public void setCurrentRefreshToken(String currentRefreshToken) {
|
||||
this.updated |= !Objects.equals(this.currentRefreshToken, currentRefreshToken);
|
||||
this.currentRefreshToken = currentRefreshToken;
|
||||
}
|
||||
|
||||
public int getCurrentRefreshTokenUseCount() {
|
||||
return currentRefreshTokenUseCount;
|
||||
}
|
||||
|
||||
public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) {
|
||||
this.updated |= this.currentRefreshTokenUseCount != currentRefreshTokenUseCount;
|
||||
this.currentRefreshTokenUseCount = currentRefreshTokenUseCount;
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return offline;
|
||||
}
|
||||
|
||||
public void setOffline(boolean offline) {
|
||||
this.updated |= this.offline != offline;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,28 +23,25 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public abstract class MapUserSessionAdapter extends AbstractUserSessionModel<MapUserSessionEntity> {
|
||||
public abstract class MapUserSessionAdapter<K> extends AbstractUserSessionModel<K> {
|
||||
|
||||
public MapUserSessionAdapter(KeycloakSession session, RealmModel realm, MapUserSessionEntity entity) {
|
||||
public MapUserSessionAdapter(KeycloakSession session, RealmModel realm, MapUserSessionEntity<K> entity) {
|
||||
super(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
|
@ -134,7 +131,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel<Map
|
|||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) {
|
||||
UUID clientSessionId = entity.getAuthenticatedClientSessions().get(clientUUID);
|
||||
String clientSessionId = entity.getAuthenticatedClientSessions().get(clientUUID);
|
||||
|
||||
if (clientSessionId == null) {
|
||||
return null;
|
||||
|
@ -220,4 +217,18 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel<Map
|
|||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof UserSessionModel)) return false;
|
||||
|
||||
UserSessionModel that = (UserSessionModel) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,26 +16,273 @@
|
|||
*/
|
||||
package org.keycloak.models.map.userSession;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserSessionEntity extends AbstractUserSessionEntity<UUID> {
|
||||
protected MapUserSessionEntity() {
|
||||
super();
|
||||
public class MapUserSessionEntity<K> implements AbstractEntity<K> {
|
||||
private K id;
|
||||
|
||||
private String realmId;
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
*/
|
||||
protected boolean updated;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String brokerSessionId;
|
||||
private String brokerUserId;
|
||||
|
||||
private String loginUsername;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private String authMethod;
|
||||
|
||||
private boolean rememberMe;
|
||||
|
||||
private int started;
|
||||
|
||||
private int lastSessionRefresh;
|
||||
|
||||
private long expiration;
|
||||
|
||||
private Map<String, String> notes = new ConcurrentHashMap<>();
|
||||
|
||||
private UserSessionModel.State state;
|
||||
|
||||
private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT;
|
||||
|
||||
private Map<String, String> authenticatedClientSessions = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean offline;
|
||||
|
||||
public MapUserSessionEntity() {
|
||||
this.id = null;
|
||||
this.realmId = null;
|
||||
}
|
||||
|
||||
public MapUserSessionEntity(UUID id, String realmId) {
|
||||
super(id, realmId);
|
||||
public MapUserSessionEntity(K id, String realmId) {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(realmId, "realmId");
|
||||
|
||||
this.id = id;
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public MapUserSessionEntity(UUID id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
|
||||
String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
|
||||
boolean offline) {
|
||||
super(id, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, offline);
|
||||
public MapUserSessionEntity(K id, RealmModel realm, UserModel user, String loginUsername, String ipAddress,
|
||||
String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId,
|
||||
boolean offline) {
|
||||
this.id = id;
|
||||
this.realmId = realm.getId();
|
||||
this.userId = user.getId();
|
||||
this.loginUsername = loginUsername;
|
||||
this.ipAddress = ipAddress;
|
||||
this.authMethod = authMethod;
|
||||
this.rememberMe = rememberMe;
|
||||
this.brokerSessionId = brokerSessionId;
|
||||
this.brokerUserId = brokerUserId;
|
||||
this.started = Time.currentTime();
|
||||
this.lastSessionRefresh = started;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return this.updated;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.updated |= !Objects.equals(this.userId, userId);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getBrokerSessionId() {
|
||||
return brokerSessionId;
|
||||
}
|
||||
|
||||
public void setBrokerSessionId(String brokerSessionId) {
|
||||
this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId);
|
||||
this.brokerSessionId = brokerSessionId;
|
||||
}
|
||||
|
||||
public String getBrokerUserId() {
|
||||
return brokerUserId;
|
||||
}
|
||||
|
||||
public void setBrokerUserId(String brokerUserId) {
|
||||
this.updated |= !Objects.equals(this.brokerUserId, brokerUserId);
|
||||
this.brokerUserId = brokerUserId;
|
||||
}
|
||||
|
||||
public String getLoginUsername() {
|
||||
return loginUsername;
|
||||
}
|
||||
|
||||
public void setLoginUsername(String loginUsername) {
|
||||
this.updated |= !Objects.equals(this.loginUsername, loginUsername);
|
||||
this.loginUsername = loginUsername;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.updated |= !Objects.equals(this.ipAddress, ipAddress);
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.updated |= !Objects.equals(this.authMethod, authMethod);
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public boolean isRememberMe() {
|
||||
return rememberMe;
|
||||
}
|
||||
|
||||
public void setRememberMe(boolean rememberMe) {
|
||||
this.updated |= this.rememberMe != rememberMe;
|
||||
this.rememberMe = rememberMe;
|
||||
}
|
||||
|
||||
public int getStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
public void setStarted(int started) {
|
||||
this.updated |= this.started != started;
|
||||
this.started = started;
|
||||
}
|
||||
|
||||
public int getLastSessionRefresh() {
|
||||
return lastSessionRefresh;
|
||||
}
|
||||
|
||||
public void setLastSessionRefresh(int lastSessionRefresh) {
|
||||
this.updated |= this.lastSessionRefresh != lastSessionRefresh;
|
||||
this.lastSessionRefresh = lastSessionRefresh;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(long expiration) {
|
||||
this.updated |= this.expiration != expiration;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public String getNote(String name) {
|
||||
return notes.get(name);
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.updated |= !Objects.equals(this.notes, notes);
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public String removeNote(String name) {
|
||||
String note = this.notes.remove(name);
|
||||
this.updated |= note != null;
|
||||
return note;
|
||||
}
|
||||
|
||||
public void addNote(String name, String value) {
|
||||
this.updated |= !Objects.equals(this.notes.put(name, value), value);
|
||||
}
|
||||
|
||||
public UserSessionModel.State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(UserSessionModel.State state) {
|
||||
this.updated |= !Objects.equals(this.state, state);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthenticatedClientSessions() {
|
||||
return authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public void setAuthenticatedClientSessions(Map<String, String> authenticatedClientSessions) {
|
||||
this.updated |= !Objects.equals(this.authenticatedClientSessions, authenticatedClientSessions);
|
||||
this.authenticatedClientSessions = authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public void addAuthenticatedClientSession(String clientId, String clientSessionId) {
|
||||
this.updated |= !Objects.equals(this.authenticatedClientSessions.put(clientId, clientSessionId), clientSessionId);
|
||||
}
|
||||
|
||||
public String removeAuthenticatedClientSession(String clientId) {
|
||||
String entity = this.authenticatedClientSessions.remove(clientId);
|
||||
this.updated |= entity != null;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void clearAuthenticatedClientSessions() {
|
||||
this.updated |= !authenticatedClientSessions.isEmpty();
|
||||
this.authenticatedClientSessions.clear();
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return offline;
|
||||
}
|
||||
|
||||
public void setOffline(boolean offline) {
|
||||
this.updated |= this.offline != offline;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
public UserSessionModel.SessionPersistenceState getPersistenceState() {
|
||||
return persistenceState;
|
||||
}
|
||||
|
||||
public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) {
|
||||
this.updated |= !Objects.equals(this.persistenceState, persistenceState);
|
||||
this.persistenceState = persistenceState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), hashCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -56,22 +55,22 @@ import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
|||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserSessionProvider implements UserSessionProvider {
|
||||
public class MapUserSessionProvider<UK, CK> implements UserSessionProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapUserSessionProvider.class);
|
||||
private final KeycloakSession session;
|
||||
protected final MapKeycloakTransaction<UUID, MapUserSessionEntity, UserSessionModel> userSessionTx;
|
||||
protected final MapKeycloakTransaction<UUID, MapAuthenticatedClientSessionEntity, AuthenticatedClientSessionModel> clientSessionTx;
|
||||
private final MapStorage<UUID, MapUserSessionEntity, UserSessionModel> userSessionStore;
|
||||
private final MapStorage<UUID, MapAuthenticatedClientSessionEntity, AuthenticatedClientSessionModel> clientSessionStore;
|
||||
protected final MapKeycloakTransaction<UK, MapUserSessionEntity<UK>, UserSessionModel> userSessionTx;
|
||||
protected final MapKeycloakTransaction<CK, MapAuthenticatedClientSessionEntity<CK>, AuthenticatedClientSessionModel> clientSessionTx;
|
||||
private final MapStorage<UK, MapUserSessionEntity<UK>, UserSessionModel> userSessionStore;
|
||||
private final MapStorage<CK, MapAuthenticatedClientSessionEntity<CK>, AuthenticatedClientSessionModel> clientSessionStore;
|
||||
|
||||
/**
|
||||
* Storage for transient user sessions which lifespan is limited to one request.
|
||||
*/
|
||||
private final Map<UUID, MapUserSessionEntity> transientUserSessions = new HashMap<>();
|
||||
private final Map<UK, MapUserSessionEntity<UK>> transientUserSessions = new HashMap<>();
|
||||
|
||||
public MapUserSessionProvider(KeycloakSession session, MapStorage<UUID, MapUserSessionEntity, UserSessionModel> userSessionStore,
|
||||
MapStorage<UUID, MapAuthenticatedClientSessionEntity, AuthenticatedClientSessionModel> clientSessionStore) {
|
||||
public MapUserSessionProvider(KeycloakSession session, MapStorage<UK, MapUserSessionEntity<UK>, UserSessionModel> userSessionStore,
|
||||
MapStorage<CK, MapAuthenticatedClientSessionEntity<CK>, AuthenticatedClientSessionModel> clientSessionStore) {
|
||||
this.session = session;
|
||||
this.userSessionStore = userSessionStore;
|
||||
this.clientSessionStore = clientSessionStore;
|
||||
|
@ -82,7 +81,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
session.getTransactionManager().enlistAfterCompletion(clientSessionTx);
|
||||
}
|
||||
|
||||
private Function<MapUserSessionEntity, UserSessionModel> userEntityToAdapterFunc(RealmModel realm) {
|
||||
private Function<MapUserSessionEntity<UK>, UserSessionModel> userEntityToAdapterFunc(RealmModel realm) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return (origEntity) -> {
|
||||
if (origEntity.getExpiration() <= Time.currentTime()) {
|
||||
|
@ -92,12 +91,16 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
userSessionTx.delete(origEntity.getId());
|
||||
return null;
|
||||
} else {
|
||||
return new MapUserSessionAdapter(session, realm,
|
||||
return new MapUserSessionAdapter<UK>(session, realm,
|
||||
Objects.equals(origEntity.getPersistenceState(), TRANSIENT) ? origEntity : registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return userSessionStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthenticatedClientSessions(Collection<String> removedClientUUIDS) {
|
||||
removedClientUUIDS.forEach(entity::removeAuthenticatedClientSession);
|
||||
public void removeAuthenticatedClientSessions(Collection<String> removedClientUKS) {
|
||||
removedClientUKS.forEach(entity::removeAuthenticatedClientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,7 +114,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
};
|
||||
}
|
||||
|
||||
private Function<MapAuthenticatedClientSessionEntity, AuthenticatedClientSessionModel> clientEntityToAdapterFunc(RealmModel realm,
|
||||
private Function<MapAuthenticatedClientSessionEntity<CK>, AuthenticatedClientSessionModel> clientEntityToAdapterFunc(RealmModel realm,
|
||||
ClientModel client,
|
||||
UserSessionModel userSession) {
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
|
@ -121,7 +124,12 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
clientSessionTx.delete(origEntity.getId());
|
||||
return null;
|
||||
} else {
|
||||
return new MapAuthenticatedClientSessionAdapter(session, realm, client, userSession, registerEntityForChanges(origEntity)) {
|
||||
return new MapAuthenticatedClientSessionAdapter<CK>(session, realm, client, userSession, registerEntityForChanges(origEntity)) {
|
||||
@Override
|
||||
public String getId() {
|
||||
return clientSessionStore.getKeyConvertor().keyToString(entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detachFromUserSession() {
|
||||
this.userSession = null;
|
||||
|
@ -140,15 +148,15 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
};
|
||||
}
|
||||
|
||||
private MapUserSessionEntity registerEntityForChanges(MapUserSessionEntity origEntity) {
|
||||
MapUserSessionEntity res = userSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
userSessionTx.updateIfChanged(origEntity.getId(), res, MapUserSessionEntity::isUpdated);
|
||||
private MapUserSessionEntity<UK> registerEntityForChanges(MapUserSessionEntity<UK> origEntity) {
|
||||
MapUserSessionEntity<UK> res = userSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
userSessionTx.updateIfChanged(origEntity.getId(), res, MapUserSessionEntity<UK>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private MapAuthenticatedClientSessionEntity registerEntityForChanges(MapAuthenticatedClientSessionEntity origEntity) {
|
||||
MapAuthenticatedClientSessionEntity res = clientSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
clientSessionTx.updateIfChanged(origEntity.getId(), res, MapAuthenticatedClientSessionEntity::isUpdated);
|
||||
private MapAuthenticatedClientSessionEntity<CK> registerEntityForChanges(MapAuthenticatedClientSessionEntity<CK> origEntity) {
|
||||
MapAuthenticatedClientSessionEntity<CK> res = clientSessionTx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
clientSessionTx.updateIfChanged(origEntity.getId(), res, MapAuthenticatedClientSessionEntity<CK>::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -159,28 +167,28 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
|
||||
MapAuthenticatedClientSessionEntity entity =
|
||||
new MapAuthenticatedClientSessionEntity(UUID.randomUUID(), userSession.getId(), realm.getId(), client.getId(), false);
|
||||
MapAuthenticatedClientSessionEntity<CK> entity =
|
||||
new MapAuthenticatedClientSessionEntity<>(clientSessionStore.getKeyConvertor().yieldNewUniqueKey(), userSession.getId(), realm.getId(), client.getId(), false);
|
||||
setClientSessionExpiration(entity, realm, client);
|
||||
|
||||
LOG.tracef("createClientSession(%s, %s, %s)%s", realm, client, userSession, getShortStackTrace());
|
||||
|
||||
clientSessionTx.create(entity.getId(), entity);
|
||||
|
||||
MapUserSessionEntity userSessionEntity = getUserSessionById(UUID.fromString(userSession.getId()));
|
||||
MapUserSessionEntity<UK> userSessionEntity = getUserSessionById(userSessionStore.getKeyConvertor().fromString(userSession.getId()));
|
||||
|
||||
if (userSessionEntity == null) {
|
||||
throw new IllegalStateException("User session entity does not exist: " + userSession.getId());
|
||||
}
|
||||
|
||||
userSessionEntity.addAuthenticatedClientSession(client.getId(), entity.getId());
|
||||
userSessionEntity.addAuthenticatedClientSession(client.getId(), clientSessionStore.getKeyConvertor().keyToString(entity.getId()));
|
||||
|
||||
return clientEntityToAdapterFunc(realm, client, userSession).apply(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client,
|
||||
UUID clientSessionId, boolean offline) {
|
||||
String clientSessionId, boolean offline) {
|
||||
LOG.tracef("getClientSession(%s, %s, %s, %s)%s", userSession, client,
|
||||
clientSessionId, offline, getShortStackTrace());
|
||||
|
||||
|
@ -190,8 +198,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
CK ck = clientSessionStore.getKeyConvertor().fromStringSafe(clientSessionId);
|
||||
ModelCriteriaBuilder<AuthenticatedClientSessionModel> mcb = clientSessionStore.createCriteriaBuilder()
|
||||
.compare(AuthenticatedClientSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, clientSessionId)
|
||||
.compare(AuthenticatedClientSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, ck)
|
||||
.compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, ModelCriteriaBuilder.Operator.EQ, userSession.getId())
|
||||
.compare(AuthenticatedClientSessionModel.SearchableFields.REALM_ID, ModelCriteriaBuilder.Operator.EQ, userSession.getRealm().getId())
|
||||
.compare(AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, ModelCriteriaBuilder.Operator.EQ, client.getId())
|
||||
|
@ -214,11 +223,11 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
public UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername,
|
||||
String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId,
|
||||
String brokerUserId, UserSessionModel.SessionPersistenceState persistenceState) {
|
||||
final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
final UK entityId = id == null ? userSessionStore.getKeyConvertor().yieldNewUniqueKey(): userSessionStore.getKeyConvertor().fromString(id);
|
||||
|
||||
LOG.tracef("createUserSession(%s, %s, %s, %s)%s", id, realm, loginUsername, persistenceState, getShortStackTrace());
|
||||
|
||||
MapUserSessionEntity entity = new MapUserSessionEntity(entityId, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false);
|
||||
MapUserSessionEntity<UK> entity = new MapUserSessionEntity<>(entityId, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false);
|
||||
entity.setPersistenceState(persistenceState);
|
||||
setUserSessionExpiration(entity, realm);
|
||||
|
||||
|
@ -247,12 +256,12 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
LOG.tracef("getUserSession(%s, %s)%s", realm, id, getShortStackTrace());
|
||||
|
||||
UUID uuid = toUUID(id);
|
||||
UK uuid = userSessionStore.getKeyConvertor().fromStringSafe(id);
|
||||
if (uuid == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapUserSessionEntity userSessionEntity = transientUserSessions.get(uuid);
|
||||
MapUserSessionEntity<UK> userSessionEntity = transientUserSessions.get(uuid);
|
||||
if (userSessionEntity != null) {
|
||||
return userEntityToAdapterFunc(realm).apply(userSessionEntity);
|
||||
}
|
||||
|
@ -371,12 +380,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
public void removeUserSession(RealmModel realm, UserSessionModel session) {
|
||||
Objects.requireNonNull(session, "The provided user session can't be null!");
|
||||
|
||||
UK uk = userSessionStore.getKeyConvertor().fromString(session.getId());
|
||||
ModelCriteriaBuilder<UserSessionModel> mcb = realmAndOfflineCriteriaBuilder(realm, false)
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(session.getId()));
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
|
||||
|
||||
LOG.tracef("removeUserSession(%s, %s)%s", realm, session, getShortStackTrace());
|
||||
|
||||
userSessionTx.delete(UUID.randomUUID(), mcb);
|
||||
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -387,7 +397,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
LOG.tracef("removeUserSessions(%s, %s)%s", realm, user, getShortStackTrace());
|
||||
|
||||
userSessionTx.delete(UUID.randomUUID(), mcb);
|
||||
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -406,7 +416,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
LOG.tracef("removeUserSessions(%s)%s", realm, getShortStackTrace());
|
||||
|
||||
userSessionTx.delete(UUID.randomUUID(), mcb);
|
||||
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -425,7 +435,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
public UserSessionModel createOfflineUserSession(UserSessionModel userSession) {
|
||||
LOG.tracef("createOfflineUserSession(%s)%s", userSession, getShortStackTrace());
|
||||
|
||||
MapUserSessionEntity offlineUserSession = createUserSessionEntityInstance(userSession, true);
|
||||
MapUserSessionEntity<UK> offlineUserSession = createUserSessionEntityInstance(userSession, true);
|
||||
|
||||
// set a reference for the offline user session to the original online user session
|
||||
userSession.setNote(CORRESPONDING_SESSION_ID, offlineUserSession.getId().toString());
|
||||
|
@ -458,12 +468,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
ModelCriteriaBuilder<UserSessionModel> mcb;
|
||||
if (userSession.isOffline()) {
|
||||
userSessionTx.delete(UUID.fromString(userSession.getId()));
|
||||
userSessionTx.delete(userSessionStore.getKeyConvertor().fromString(userSession.getId()));
|
||||
} else if (userSession.getNote(CORRESPONDING_SESSION_ID) != null) {
|
||||
mcb = realmAndOfflineCriteriaBuilder(realm, true)
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(userSession.getNote(CORRESPONDING_SESSION_ID)));
|
||||
userSessionTx.delete(UUID.randomUUID(), mcb);
|
||||
userSession.removeNote(CORRESPONDING_SESSION_ID);
|
||||
UK uk = userSessionStore.getKeyConvertor().fromString(userSession.getNote(CORRESPONDING_SESSION_ID));
|
||||
mcb = realmAndOfflineCriteriaBuilder(realm, true)
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
|
||||
userSessionTx.delete(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), mcb);
|
||||
userSession.removeNote(CORRESPONDING_SESSION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,13 +483,13 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
UserSessionModel offlineUserSession) {
|
||||
LOG.tracef("createOfflineClientSession(%s, %s)%s", clientSession, offlineUserSession, getShortStackTrace());
|
||||
|
||||
MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
|
||||
MapAuthenticatedClientSessionEntity<CK> clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
|
||||
clientSessionEntity.setTimestamp(Time.currentTime());
|
||||
setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient());
|
||||
|
||||
Optional<MapUserSessionEntity> userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst();
|
||||
Optional<MapUserSessionEntity<UK>> userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst();
|
||||
if (userSessionEntity.isPresent()) {
|
||||
userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId());
|
||||
userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionStore.getKeyConvertor().keyToString(clientSessionEntity.getId()));
|
||||
}
|
||||
|
||||
clientSessionTx.create(clientSessionEntity.getId(), clientSessionEntity);
|
||||
|
@ -556,17 +567,17 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
persistentUserSessions.stream()
|
||||
.map(pus -> {
|
||||
MapUserSessionEntity userSessionEntity = new MapUserSessionEntity(UUID.randomUUID(), pus.getRealm(), pus.getUser(),
|
||||
MapUserSessionEntity<UK> userSessionEntity = new MapUserSessionEntity<UK>(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), pus.getRealm(), pus.getUser(),
|
||||
pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(),
|
||||
pus.isRememberMe(), pus.getBrokerSessionId(), pus.getBrokerUserId(), offline);
|
||||
|
||||
for (Map.Entry<String, AuthenticatedClientSessionModel> entry : pus.getAuthenticatedClientSessions().entrySet()) {
|
||||
MapAuthenticatedClientSessionEntity clientSession = createAuthenticatedClientSessionInstance(entry.getValue(), entry.getValue().getUserSession(), offline);
|
||||
MapAuthenticatedClientSessionEntity<CK> clientSession = createAuthenticatedClientSessionInstance(entry.getValue(), entry.getValue().getUserSession(), offline);
|
||||
|
||||
// Update timestamp to same value as userSession. LastSessionRefresh of userSession from DB will have correct value
|
||||
clientSession.setTimestamp(userSessionEntity.getLastSessionRefresh());
|
||||
|
||||
userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSession.getId());
|
||||
userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSessionStore.getKeyConvertor().keyToString(clientSession.getId()));
|
||||
|
||||
clientSessionTx.create(clientSession.getId(), clientSession);
|
||||
}
|
||||
|
@ -581,8 +592,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
}
|
||||
|
||||
private Stream<MapUserSessionEntity> getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
|
||||
UUID uuid = toUUID(userSessionId);
|
||||
private Stream<MapUserSessionEntity<UK>> getOfflineUserSessionEntityStream(RealmModel realm, String userSessionId) {
|
||||
UK uuid = userSessionStore.getKeyConvertor().fromStringSafe(userSessionId);
|
||||
if (uuid == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
@ -593,7 +604,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid);
|
||||
|
||||
// check if it's an offline user session
|
||||
MapUserSessionEntity userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
|
||||
MapUserSessionEntity<UK> userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
|
||||
if (userSessionEntity != null) {
|
||||
if (userSessionEntity.isOffline()) {
|
||||
return Stream.of(userSessionEntity);
|
||||
|
@ -608,8 +619,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
// it's online user session so lookup offline user session by corresponding session id reference
|
||||
String offlineUserSessionId = userSessionEntity.getNote(CORRESPONDING_SESSION_ID);
|
||||
if (offlineUserSessionId != null) {
|
||||
UK uk = userSessionStore.getKeyConvertor().fromStringSafe(offlineUserSessionId);
|
||||
mcb = realmAndOfflineCriteriaBuilder(realm, true)
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, UUID.fromString(offlineUserSessionId));
|
||||
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk);
|
||||
return userSessionTx.getUpdatedNotRemoved(mcb);
|
||||
}
|
||||
|
||||
|
@ -622,18 +634,18 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
.compare(UserSessionModel.SearchableFields.IS_OFFLINE, ModelCriteriaBuilder.Operator.EQ, offline);
|
||||
}
|
||||
|
||||
private MapUserSessionEntity getUserSessionById(UUID id) {
|
||||
MapUserSessionEntity userSessionEntity = transientUserSessions.get(id);
|
||||
private MapUserSessionEntity<UK> getUserSessionById(UK id) {
|
||||
MapUserSessionEntity<UK> userSessionEntity = transientUserSessions.get(id);
|
||||
|
||||
if (userSessionEntity == null) {
|
||||
MapUserSessionEntity userSession = userSessionTx.read(id);
|
||||
MapUserSessionEntity<UK> userSession = userSessionTx.read(id);
|
||||
return userSession != null ? registerEntityForChanges(userSession) : null;
|
||||
}
|
||||
return userSessionEntity;
|
||||
}
|
||||
|
||||
private MapUserSessionEntity createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) {
|
||||
MapUserSessionEntity entity = new MapUserSessionEntity(UUID.randomUUID(), userSession.getRealm().getId());
|
||||
private MapUserSessionEntity<UK> createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) {
|
||||
MapUserSessionEntity<UK> entity = new MapUserSessionEntity<UK>(userSessionStore.getKeyConvertor().yieldNewUniqueKey(), userSession.getRealm().getId());
|
||||
|
||||
entity.setAuthMethod(userSession.getAuthMethod());
|
||||
entity.setBrokerSessionId(userSession.getBrokerSessionId());
|
||||
|
@ -655,9 +667,9 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
return entity;
|
||||
}
|
||||
|
||||
private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession,
|
||||
private MapAuthenticatedClientSessionEntity<CK> createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession,
|
||||
UserSessionModel userSession, boolean offline) {
|
||||
MapAuthenticatedClientSessionEntity entity = new MapAuthenticatedClientSessionEntity(UUID.randomUUID(),
|
||||
MapAuthenticatedClientSessionEntity<CK> entity = new MapAuthenticatedClientSessionEntity<CK>(clientSessionStore.getKeyConvertor().yieldNewUniqueKey(),
|
||||
userSession.getId(), clientSession.getRealm().getId(), clientSession.getClient().getId(), offline);
|
||||
|
||||
entity.setAction(clientSession.getAction());
|
||||
|
@ -669,12 +681,4 @@ public class MapUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private UUID toUUID(String id) {
|
||||
try {
|
||||
return UUID.fromString(id);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.keycloak.models.map.userSession;
|
||||
|
||||
import org.keycloak.Config.Scope;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
@ -28,40 +30,79 @@ import org.keycloak.models.map.storage.MapStorage;
|
|||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.models.map.storage.MapStorageSpi;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.getComponentFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
*/
|
||||
public class MapUserSessionProviderFactory extends AbstractMapProviderFactory<UserSessionProvider>
|
||||
implements UserSessionProviderFactory {
|
||||
public class MapUserSessionProviderFactory<UK, CK> implements AmphibianProviderFactory<UserSessionProvider>, UserSessionProviderFactory, ProviderEventListener {
|
||||
|
||||
private MapStorage<UUID, MapUserSessionEntity, UserSessionModel> userSessionStore;
|
||||
private MapStorage<UUID, MapAuthenticatedClientSessionEntity, AuthenticatedClientSessionModel> clientSessionStore;
|
||||
public static final String CONFIG_STORAGE_USER_SESSIONS = "storage-user-sessions";
|
||||
public static final String CONFIG_STORAGE_CLIENT_SESSIONS = "storage-client-sessions";
|
||||
public static final String PROVIDER_ID = AbstractMapProviderFactory.PROVIDER_ID;
|
||||
|
||||
private Scope storageConfigScopeUserSessions;
|
||||
private Scope storageConfigScopeClientSessions;
|
||||
|
||||
private Runnable onClose;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Scope config) {
|
||||
storageConfigScopeUserSessions = config.scope(AbstractMapProviderFactory.CONFIG_STORAGE + "-user-sessions");
|
||||
storageConfigScopeClientSessions = config.scope(AbstractMapProviderFactory.CONFIG_STORAGE + "-client-sessions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
MapStorageProviderFactory sp = (MapStorageProviderFactory) factory.getProviderFactory(MapStorageProvider.class);
|
||||
userSessionStore = sp.getStorage("userSessions", UUID.class, MapUserSessionEntity.class, UserSessionModel.class);
|
||||
clientSessionStore = sp.getStorage("clientSessions", UUID.class, MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
|
||||
factory.register(this);
|
||||
onClose = () -> factory.unregister(this);
|
||||
}
|
||||
|
||||
factory.register(event -> {
|
||||
if (event instanceof UserModel.UserRemovedEvent) {
|
||||
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
||||
|
||||
MapUserSessionProvider provider = MapUserSessionProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
|
||||
provider.removeUserSessions(userRemovedEvent.getRealm(), userRemovedEvent.getUser());
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void close() {
|
||||
AmphibianProviderFactory.super.close();
|
||||
onClose.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPersistentSessions(KeycloakSessionFactory sessionFactory, int maxErrors, int sessionsPerSegment) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapUserSessionProvider create(KeycloakSession session) {
|
||||
return new MapUserSessionProvider(session, userSessionStore, clientSessionStore);
|
||||
public MapUserSessionProvider<UK, CK> create(KeycloakSession session) {
|
||||
MapStorageProviderFactory storageProviderFactoryUs = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
|
||||
MapStorageProvider.class, storageConfigScopeUserSessions, MapStorageSpi.NAME);
|
||||
final MapStorageProvider factoryUs = storageProviderFactoryUs.create(session);
|
||||
MapStorage userSessionStore = factoryUs.getStorage(MapUserSessionEntity.class, UserSessionModel.class);
|
||||
|
||||
MapStorageProviderFactory storageProviderFactoryCs = (MapStorageProviderFactory) getComponentFactory(session.getKeycloakSessionFactory(),
|
||||
MapStorageProvider.class, storageConfigScopeClientSessions, MapStorageSpi.NAME);
|
||||
final MapStorageProvider factoryCs = storageProviderFactoryCs.create(session);
|
||||
MapStorage clientSessionStore = factoryCs.getStorage(MapAuthenticatedClientSessionEntity.class, AuthenticatedClientSessionModel.class);
|
||||
|
||||
return new MapUserSessionProvider<>(session, userSessionStore, clientSessionStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "User session provider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
if (event instanceof UserModel.UserRemovedEvent) {
|
||||
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
||||
|
||||
MapUserSessionProvider provider = MapUserSessionProviderFactory.this.create(userRemovedEvent.getKeycloakSession());
|
||||
provider.removeUserSessions(userRemovedEvent.getRealm(), userRemovedEvent.getUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
|||
*/
|
||||
public class SessionExpiration {
|
||||
|
||||
public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) {
|
||||
public static <K> void setClientSessionExpiration(MapAuthenticatedClientSessionEntity<K> entity, RealmModel realm, ClientModel client) {
|
||||
if (entity.isOffline()) {
|
||||
long sessionExpires = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout();
|
||||
if (realm.isOfflineSessionMaxLifespanEnabled()) {
|
||||
|
@ -99,7 +99,7 @@ public class SessionExpiration {
|
|||
}
|
||||
}
|
||||
|
||||
public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) {
|
||||
public static <K> void setUserSessionExpiration(MapUserSessionEntity<K> entity, RealmModel realm) {
|
||||
if (entity.isOffline()) {
|
||||
long sessionExpires = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout();
|
||||
if (realm.isOfflineSessionMaxLifespanEnabled()) {
|
||||
|
|
|
@ -28,11 +28,11 @@ import java.util.stream.Collectors;
|
|||
|
||||
public class AbstractUserEntityCredentialsOrderTest {
|
||||
|
||||
private AbstractUserEntity<Integer> user;
|
||||
private MapUserEntity<Integer> user;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
user = new AbstractUserEntity<Integer>(1, "realmId") {};
|
||||
user = new MapUserEntity<Integer>(1, "realmId") {};
|
||||
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
UserCredentialEntity credentialModel = new UserCredentialEntity();
|
||||
|
|
|
@ -26,6 +26,9 @@ import org.keycloak.provider.Spi;
|
|||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class StoreFactorySpi implements Spi {
|
||||
|
||||
public static final String NAME = "authorizationPersister";
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
|
@ -33,7 +36,7 @@ public class StoreFactorySpi implements Spi {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "authorizationPersister";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,8 @@ import org.keycloak.provider.Spi;
|
|||
|
||||
public class ServerInfoSpi implements Spi {
|
||||
|
||||
public static final String NAME = "serverInfo";
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
|
@ -30,7 +32,7 @@ public class ServerInfoSpi implements Spi {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "serverInfo";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,7 @@ public interface UserLoginFailureModel {
|
|||
public static final SearchableModelField<UserLoginFailureModel> USER_ID = new SearchableModelField<>("userId", String.class);
|
||||
}
|
||||
|
||||
String getId();
|
||||
String getUserId();
|
||||
int getFailedLoginNotBefore();
|
||||
void setFailedLoginNotBefore(int notBefore);
|
||||
|
|
|
@ -40,7 +40,14 @@ public interface UserSessionProvider extends Provider {
|
|||
KeycloakSession getKeycloakSession();
|
||||
|
||||
AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
|
||||
AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getClientSession(UserSessionModel, ClientModel, String, boolean)} instead.
|
||||
*/
|
||||
default AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline) {
|
||||
return getClientSession(userSession, client, clientSessionId == null ? null : clientSessionId.toString(), offline);
|
||||
}
|
||||
AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, String clientSessionId, boolean offline);
|
||||
|
||||
UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
|
||||
|
||||
|
|
|
@ -59,10 +59,12 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
|
|||
private KeycloakSessionFactory factory;
|
||||
private boolean componentCachingAvailable;
|
||||
private boolean componentCachingEnabled;
|
||||
private Boolean componentCachingForced;
|
||||
|
||||
@Override
|
||||
public void init(Scope config) {
|
||||
this.componentCachingEnabled = config.getBoolean("cachingEnabled", true);
|
||||
this.componentCachingForced = config.getBoolean("cachingForced", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,7 +74,12 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
|
|||
if (! componentCachingEnabled) {
|
||||
LOG.warn("Caching of components disabled by the configuration which may have performance impact.");
|
||||
} else if (! componentCachingAvailable) {
|
||||
LOG.warn("No system-wide ClusterProviderFactory found. Cannot send messages across cluster, thus disabling caching of components.");
|
||||
if (Objects.equals(componentCachingForced, Boolean.TRUE)) {
|
||||
LOG.warn("Component caching forced even though no system-wide ClusterProviderFactory found. This would be only reliable in single-node deployment.");
|
||||
this.componentCachingAvailable = true;
|
||||
} else {
|
||||
LOG.warn("No system-wide ClusterProviderFactory found. Cannot send messages across cluster, thus disabling caching of components. Consider setting cachingForced option in single-node deployment.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,9 +127,13 @@ public class DefaultComponentFactoryProviderFactory implements ComponentFactoryP
|
|||
Scope scope = Config.scope(factory.getSpi(clazz).getName(), provider);
|
||||
ComponentModelScope configScope = new ComponentModelScope(scope, cm);
|
||||
|
||||
return this.componentCachingAvailable
|
||||
? componentsMap.get().computeIfAbsent(componentId, cId -> initializeFactory(clazz, realmId, componentId, newFactory, configScope))
|
||||
: initializeFactory(clazz, realmId, componentId, newFactory, configScope);
|
||||
ProviderFactory<T> providerFactory;
|
||||
if (this.componentCachingAvailable) {
|
||||
providerFactory = componentsMap.get().computeIfAbsent(componentId, cId -> initializeFactory(clazz, realmId, componentId, newFactory, configScope));
|
||||
} else {
|
||||
providerFactory = initializeFactory(clazz, realmId, componentId, newFactory, configScope);
|
||||
}
|
||||
return providerFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -82,7 +82,9 @@
|
|||
"mapStorage": {
|
||||
"provider": "${keycloak.mapStorage.provider:concurrenthashmap}",
|
||||
"concurrenthashmap": {
|
||||
"dir": "${project.build.directory:target}"
|
||||
"dir": "${project.build.directory:target}",
|
||||
"keyType.realms": "string",
|
||||
"keyType.authz-resource-servers": "string"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue